[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