summaryrefslogtreecommitdiffstats
path: root/maths/demo
diff options
context:
space:
mode:
authorYohann Roussel <yroussel@google.com>2014-03-19 16:25:37 +0100
committerYohann Roussel <yroussel@google.com>2014-03-20 15:13:33 +0100
commit4eceb95409e844fdc33c9c706e1dc307bfd40303 (patch)
treeee9f4f3fc79f757c79081c336bce4f1782c6ccd8 /maths/demo
parent3d2402901b1a6462e2cf47a6fd09711f327961c3 (diff)
downloadtoolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.zip
toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.gz
toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.bz2
Initial Jack import.
Change-Id: I953cf0a520195a7187d791b2885848ad0d5a9b43
Diffstat (limited to 'maths/demo')
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/BinomialDistribution.java108
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/BinomialParametersPanel.java51
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/DistributionPanel.java80
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/ExponentialDistribution.java97
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/ExponentialParametersPanel.java47
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/GaussianDistribution.java95
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/GaussianParametersPanel.java51
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/GraphPanel.java94
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/ParametersPanel.java33
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/PoissonDistribution.java100
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/PoissonParametersPanel.java47
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/ProbabilityDistribution.java125
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/RNGPanel.java96
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/RandomDemo.java157
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/UniformDistribution.java77
-rw-r--r--maths/demo/src/java/main/org/uncommons/maths/demo/UniformParametersPanel.java51
-rw-r--r--maths/demo/src/java/main/org/uncommons/swing/SpringUtilities.java213
-rw-r--r--maths/demo/src/java/main/org/uncommons/swing/SwingBackgroundTask.java100
-rw-r--r--maths/demo/src/java/test/org/uncommons/swing/SwingBackgroundTaskTest.java58
19 files changed, 1680 insertions, 0 deletions
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/BinomialDistribution.java b/maths/demo/src/java/main/org/uncommons/maths/demo/BinomialDistribution.java
new file mode 100644
index 0000000..5f064e0
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/BinomialDistribution.java
@@ -0,0 +1,108 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import org.uncommons.maths.Maths;
+import org.uncommons.maths.random.BinomialGenerator;
+
+/**
+ * @author Daniel Dyer
+ */
+class BinomialDistribution extends ProbabilityDistribution
+{
+ private final int n;
+ private final double p;
+
+
+ public BinomialDistribution(int n, double p)
+ {
+ this.n = n;
+ this.p = p;
+ }
+
+
+ public Map<Double, Double> getExpectedValues()
+ {
+ Map<Double, Double> values = new HashMap<Double, Double>();
+ for (int i = 0; i <= n; i++)
+ {
+ values.put((double) i, getExpectedProbability(i));
+ }
+ return values;
+ }
+
+
+ /**
+ * This is the probability mass function
+ * (http://en.wikipedia.org/wiki/Probability_mass_function) of
+ * the Binomial distribution represented by this number generator.
+ * @param successes The number of successful trials to determine
+ * the probability for.
+ * @return The probability of obtaining the specified number of
+ * successful trials given the current values of n and p.
+ */
+ private double getExpectedProbability(int successes)
+ {
+ double prob = Math.pow(p, successes) * Math.pow(1 - p, n - successes);
+ BigDecimal coefficient = new BigDecimal(binomialCoefficient(n, successes));
+ return coefficient.multiply(new BigDecimal(prob)).doubleValue();
+ }
+
+
+ private BigInteger binomialCoefficient(int n, int k)
+ {
+ BigInteger nFactorial = Maths.bigFactorial(n);
+ BigInteger kFactorial = Maths.bigFactorial(k);
+ BigInteger nMinusKFactorial = Maths.bigFactorial(n - k);
+ BigInteger divisor = kFactorial.multiply(nMinusKFactorial);
+ return nFactorial.divide(divisor);
+ }
+
+
+ protected BinomialGenerator createValueGenerator(Random rng)
+ {
+ return new BinomialGenerator(n, p, rng);
+ }
+
+
+ public double getExpectedMean()
+ {
+ return n * p;
+ }
+
+
+ public double getExpectedStandardDeviation()
+ {
+ return Math.sqrt(n * p * (1 - p));
+ }
+
+
+ public String getDescription()
+ {
+ return "Binomial Distribution (n = " + n + ", p = " + p + ")";
+ }
+
+
+ public boolean isDiscrete()
+ {
+ return true;
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/BinomialParametersPanel.java b/maths/demo/src/java/main/org/uncommons/maths/demo/BinomialParametersPanel.java
new file mode 100644
index 0000000..92ba99b
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/BinomialParametersPanel.java
@@ -0,0 +1,51 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.awt.BorderLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SpringLayout;
+import org.uncommons.swing.SpringUtilities;
+
+/**
+ * @author Daniel Dyer
+ */
+class BinomialParametersPanel extends ParametersPanel
+{
+ private final SpinnerNumberModel trialsNumberModel = new SpinnerNumberModel(50, 1, 100, 1);
+ private final SpinnerNumberModel probabilityNumberModel = new SpinnerNumberModel(0.5d, 0.0d, 1.0d, 0.01d);
+
+ public BinomialParametersPanel()
+ {
+ JPanel wrapper = new JPanel(new SpringLayout());
+ wrapper.add(new JLabel("No. Trials: "));
+ wrapper.add(new JSpinner(trialsNumberModel));
+ wrapper.add(new JLabel("Probability: "));
+ wrapper.add(new JSpinner(probabilityNumberModel));
+ SpringUtilities.makeCompactGrid(wrapper, 4, 1, 6, 6, 6, 6);
+ add(wrapper, BorderLayout.NORTH);
+ }
+
+
+ public BinomialDistribution createProbabilityDistribution()
+ {
+ return new BinomialDistribution(trialsNumberModel.getNumber().intValue(),
+ probabilityNumberModel.getNumber().doubleValue());
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/DistributionPanel.java b/maths/demo/src/java/main/org/uncommons/maths/demo/DistributionPanel.java
new file mode 100644
index 0000000..e143acc
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/DistributionPanel.java
@@ -0,0 +1,80 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.awt.BorderLayout;
+import java.awt.CardLayout;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import javax.swing.BorderFactory;
+import javax.swing.JComboBox;
+import javax.swing.JPanel;
+
+/**
+ * GUI component for selecting a probability distribution. Displays appropriate
+ * configuration options for each distribution.
+ * @author Daniel Dyer
+ */
+class DistributionPanel extends JPanel
+{
+ private final SortedMap<String, ParametersPanel> parameterPanels = new TreeMap<String, ParametersPanel>();
+ private final JComboBox distributionCombo = new JComboBox();
+
+ {
+ parameterPanels.put("Binomial", new BinomialParametersPanel());
+ parameterPanels.put("Exponential", new ExponentialParametersPanel());
+ parameterPanels.put("Gaussian", new GaussianParametersPanel());
+ parameterPanels.put("Poisson", new PoissonParametersPanel());
+ parameterPanels.put("Uniform", new UniformParametersPanel());
+ }
+
+
+ public DistributionPanel()
+ {
+ super(new BorderLayout());
+ final CardLayout parametersLayout = new CardLayout();
+ final JPanel parametersPanel = new JPanel(parametersLayout);
+ for (Map.Entry<String, ParametersPanel> entry : parameterPanels.entrySet())
+ {
+ distributionCombo.addItem(entry.getKey());
+ parametersPanel.add(entry.getValue(), entry.getKey());
+ }
+ parametersLayout.first(parametersPanel);
+
+ distributionCombo.addItemListener(new ItemListener()
+ {
+ public void itemStateChanged(ItemEvent itemEvent)
+ {
+ parametersLayout.show(parametersPanel,
+ (String) distributionCombo.getSelectedItem());
+ }
+ });
+
+ add(distributionCombo, BorderLayout.NORTH);
+ add(parametersPanel, BorderLayout.CENTER);
+ setBorder(BorderFactory.createTitledBorder("Probability Distribution"));
+ }
+
+
+ public ProbabilityDistribution createProbabilityDistribution()
+ {
+ ParametersPanel panel = parameterPanels.get(distributionCombo.getSelectedItem().toString());
+ return panel.createProbabilityDistribution();
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/ExponentialDistribution.java b/maths/demo/src/java/main/org/uncommons/maths/demo/ExponentialDistribution.java
new file mode 100644
index 0000000..1e34c65
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/ExponentialDistribution.java
@@ -0,0 +1,97 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import org.uncommons.maths.random.ExponentialGenerator;
+
+/**
+ * @author Daniel Dyer
+ */
+class ExponentialDistribution extends ProbabilityDistribution
+{
+ private final double rate;
+
+
+ public ExponentialDistribution(double rate)
+ {
+ this.rate = rate;
+ }
+
+
+ protected ExponentialGenerator createValueGenerator(Random rng)
+ {
+ return new ExponentialGenerator(rate, rng);
+ }
+
+
+ public Map<Double, Double> getExpectedValues()
+ {
+ Map<Double, Double> values = new HashMap<Double, Double>();
+ double p;
+ double x = 0;
+ do
+ {
+ p = getExpectedProbability(x);
+ values.put(x, p);
+ x += (1 / (2 * rate));
+ } while (p > 0.001);
+ return values;
+ }
+
+
+ /**
+ * This is the probability density function for the Exponential
+ * distribution.
+ */
+ private double getExpectedProbability(double x)
+ {
+ if (x < 0)
+ {
+ return 0;
+ }
+ else
+ {
+ return rate * Math.exp(-rate * x);
+ }
+ }
+
+
+ public double getExpectedMean()
+ {
+ return Math.pow(rate, -1);
+ }
+
+
+ public double getExpectedStandardDeviation()
+ {
+ return Math.sqrt(Math.pow(rate, -2));
+ }
+
+
+ public String getDescription()
+ {
+ return "Exponential Distribution (\u03bb = " + rate + ")";
+ }
+
+
+ public boolean isDiscrete()
+ {
+ return false;
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/ExponentialParametersPanel.java b/maths/demo/src/java/main/org/uncommons/maths/demo/ExponentialParametersPanel.java
new file mode 100644
index 0000000..587ff07
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/ExponentialParametersPanel.java
@@ -0,0 +1,47 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.awt.BorderLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SpringLayout;
+import org.uncommons.swing.SpringUtilities;
+
+/**
+ * @author Daniel Dyer
+ */
+class ExponentialParametersPanel extends ParametersPanel
+{
+ private final SpinnerNumberModel rateNumberModel = new SpinnerNumberModel(2.0d, 0.1d, 100.0d, 0.1d);
+
+ public ExponentialParametersPanel()
+ {
+ JPanel wrapper = new JPanel(new SpringLayout());
+ wrapper.add(new JLabel("Rate: "));
+ wrapper.add(new JSpinner(rateNumberModel));
+ SpringUtilities.makeCompactGrid(wrapper, 2, 1, 6, 6, 6, 6);
+ add(wrapper, BorderLayout.NORTH);
+ }
+
+
+ public ExponentialDistribution createProbabilityDistribution()
+ {
+ return new ExponentialDistribution(rateNumberModel.getNumber().doubleValue());
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/GaussianDistribution.java b/maths/demo/src/java/main/org/uncommons/maths/demo/GaussianDistribution.java
new file mode 100644
index 0000000..0bc6422
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/GaussianDistribution.java
@@ -0,0 +1,95 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import org.uncommons.maths.random.GaussianGenerator;
+
+/**
+ * @author Daniel Dyer
+ */
+class GaussianDistribution extends ProbabilityDistribution
+{
+ private final double mean;
+ private final double standardDeviation;
+
+
+ public GaussianDistribution(double mean, double standardDeviation)
+ {
+ this.mean = mean;
+ this.standardDeviation = standardDeviation;
+ }
+
+
+ protected GaussianGenerator createValueGenerator(Random rng)
+ {
+ return new GaussianGenerator(mean, standardDeviation, rng);
+ }
+
+
+ public Map<Double, Double> getExpectedValues()
+ {
+ Map<Double, Double> values = new HashMap<Double, Double>();
+ double p;
+ double x = 0;
+ do
+ {
+ p = getExpectedProbability(mean + x);
+ values.put(mean + x, p);
+ values.put(mean - x, p);
+ x += (3 * standardDeviation / 10); // 99.7% of values are within 3 standard deviations of the mean.
+ } while (p > 0.001);
+ return values;
+ }
+
+
+ /**
+ * This is the probability density function for the Gaussian
+ * distribution.
+ */
+ private double getExpectedProbability(double x)
+ {
+ double y = 1 / (standardDeviation * Math.sqrt(Math.PI * 2));
+ double z = -(Math.pow(x - mean, 2) / (2 * Math.pow(standardDeviation, 2)));
+ return y * Math.exp(z);
+ }
+
+
+ public double getExpectedMean()
+ {
+ return mean;
+ }
+
+
+ public double getExpectedStandardDeviation()
+ {
+ return standardDeviation;
+ }
+
+
+ public String getDescription()
+ {
+ return "Gaussian Distribution (\u03bc = " + mean + ", \u03c3 = " + standardDeviation +")";
+ }
+
+
+ public boolean isDiscrete()
+ {
+ return false;
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/GaussianParametersPanel.java b/maths/demo/src/java/main/org/uncommons/maths/demo/GaussianParametersPanel.java
new file mode 100644
index 0000000..3b9571a
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/GaussianParametersPanel.java
@@ -0,0 +1,51 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.awt.BorderLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SpringLayout;
+import org.uncommons.swing.SpringUtilities;
+
+/**
+ * @author Daniel Dyer
+ */
+class GaussianParametersPanel extends ParametersPanel
+{
+ private final SpinnerNumberModel meanNumberModel = new SpinnerNumberModel(0, -1000, 1000, 1);
+ private final SpinnerNumberModel deviationNumberModel = new SpinnerNumberModel(1d, 0.01d, 1000d, 1d);
+
+ public GaussianParametersPanel()
+ {
+ JPanel wrapper = new JPanel(new SpringLayout());
+ wrapper.add(new JLabel("Mean: "));
+ wrapper.add(new JSpinner(meanNumberModel));
+ wrapper.add(new JLabel("Standard Deviation: "));
+ wrapper.add(new JSpinner(deviationNumberModel));
+ SpringUtilities.makeCompactGrid(wrapper, 4, 1, 6, 6, 6, 6);
+ add(wrapper, BorderLayout.NORTH);
+ }
+
+
+ public GaussianDistribution createProbabilityDistribution()
+ {
+ return new GaussianDistribution(meanNumberModel.getNumber().doubleValue(),
+ deviationNumberModel.getNumber().doubleValue());
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/GraphPanel.java b/maths/demo/src/java/main/org/uncommons/maths/demo/GraphPanel.java
new file mode 100644
index 0000000..330d32a
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/GraphPanel.java
@@ -0,0 +1,94 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.awt.BorderLayout;
+import java.util.Map;
+import javax.swing.JPanel;
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.chart.renderer.xy.XYSplineRenderer;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+/**
+ * @author Daniel Dyer
+ */
+class GraphPanel extends JPanel
+{
+ private final ChartPanel chartPanel = new ChartPanel(null);
+
+ public GraphPanel()
+ {
+ super(new BorderLayout());
+ add(chartPanel, BorderLayout.CENTER);
+ }
+
+
+ public void generateGraph(String title,
+ Map<Double, Double> observedValues,
+ Map<Double, Double> expectedValues,
+ double expectedMean,
+ double expectedStandardDeviation,
+ boolean discrete)
+ {
+ XYSeriesCollection dataSet = new XYSeriesCollection();
+ XYSeries observedSeries = new XYSeries("Observed");
+ dataSet.addSeries(observedSeries);
+ XYSeries expectedSeries = new XYSeries("Expected");
+ dataSet.addSeries(expectedSeries);
+
+ for (Map.Entry<Double, Double> entry : observedValues.entrySet())
+ {
+ observedSeries.add(entry.getKey(), entry.getValue());
+ }
+
+ for (Map.Entry<Double, Double> entry : expectedValues.entrySet())
+ {
+ expectedSeries.add(entry.getKey(), entry.getValue());
+ }
+
+
+ JFreeChart chart = ChartFactory.createXYLineChart(title,
+ "Value",
+ "Probability",
+ dataSet,
+ PlotOrientation.VERTICAL,
+ true,
+ false,
+ false);
+ XYPlot plot = (XYPlot) chart.getPlot();
+ if (discrete)
+ {
+ // Render markers at each data point (these discrete points are the
+ // distibution, not the lines between them).
+ plot.setRenderer(new XYLineAndShapeRenderer());
+ }
+ else
+ {
+ // Render smooth lines between points for a continuous distribution.
+ XYSplineRenderer renderer = new XYSplineRenderer();
+ renderer.setBaseShapesVisible(false);
+ plot.setRenderer(renderer);
+ }
+
+ chartPanel.setChart(chart);
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/ParametersPanel.java b/maths/demo/src/java/main/org/uncommons/maths/demo/ParametersPanel.java
new file mode 100644
index 0000000..e47ba12
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/ParametersPanel.java
@@ -0,0 +1,33 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.awt.BorderLayout;
+import javax.swing.JPanel;
+
+/**
+ * @author Daniel Dyer
+ */
+abstract class ParametersPanel extends JPanel
+{
+ protected ParametersPanel()
+ {
+ super(new BorderLayout());
+ }
+
+
+ public abstract ProbabilityDistribution createProbabilityDistribution();
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/PoissonDistribution.java b/maths/demo/src/java/main/org/uncommons/maths/demo/PoissonDistribution.java
new file mode 100644
index 0000000..edf638f
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/PoissonDistribution.java
@@ -0,0 +1,100 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import org.uncommons.maths.Maths;
+import org.uncommons.maths.random.PoissonGenerator;
+
+/**
+ * @author Daniel Dyer
+ */
+class PoissonDistribution extends ProbabilityDistribution
+{
+ private final double mean;
+
+ public PoissonDistribution(double mean)
+ {
+ this.mean = mean;
+ }
+
+
+ public Map<Double, Double> getExpectedValues()
+ {
+ Map<Double, Double> values = new HashMap<Double, Double>();
+ int index = 0;
+ double p;
+ do
+ {
+ p = getExpectedProbability(index);
+ values.put((double) index, p);
+ ++index;
+ } while (p > 0.001);
+ return values;
+ }
+
+
+ /**
+ * This is the probability mass function
+ * (http://en.wikipedia.org/wiki/Probability_mass_function) of
+ * the Poisson distribution represented by this number generator.
+ * @param events The number of occurrences to determine the
+ * probability for.
+ * @return The probability of the specified number of events
+ * occurring given the current value of lamda.
+ */
+ private double getExpectedProbability(int events)
+ {
+ BigDecimal kFactorial = new BigDecimal(Maths.bigFactorial(events));
+ double numerator = Math.exp(-mean) * Math.pow(mean, events);
+ return new BigDecimal(numerator).divide(kFactorial, RoundingMode.HALF_UP).doubleValue();
+ }
+
+
+
+ protected PoissonGenerator createValueGenerator(Random rng)
+ {
+ return new PoissonGenerator(mean, rng);
+ }
+
+
+ public double getExpectedMean()
+ {
+ return mean;
+ }
+
+
+ public double getExpectedStandardDeviation()
+ {
+ return Math.sqrt(mean);
+ }
+
+
+ public String getDescription()
+ {
+ return "Poisson Distribution (\u03bb = " + mean + ")";
+ }
+
+
+ public boolean isDiscrete()
+ {
+ return true;
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/PoissonParametersPanel.java b/maths/demo/src/java/main/org/uncommons/maths/demo/PoissonParametersPanel.java
new file mode 100644
index 0000000..83484fb
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/PoissonParametersPanel.java
@@ -0,0 +1,47 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.awt.BorderLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SpringLayout;
+import org.uncommons.swing.SpringUtilities;
+
+/**
+ * @author Daniel Dyer
+ */
+class PoissonParametersPanel extends ParametersPanel
+{
+ private final SpinnerNumberModel meanNumberModel = new SpinnerNumberModel(5.0d, 0.1d, 100.0d, 0.1d);
+
+ public PoissonParametersPanel()
+ {
+ JPanel wrapper = new JPanel(new SpringLayout());
+ wrapper.add(new JLabel("Mean: "));
+ wrapper.add(new JSpinner(meanNumberModel));
+ SpringUtilities.makeCompactGrid(wrapper, 2, 1, 6, 6, 6, 6);
+ add(wrapper, BorderLayout.NORTH);
+ }
+
+
+ public PoissonDistribution createProbabilityDistribution()
+ {
+ return new PoissonDistribution(meanNumberModel.getNumber().doubleValue());
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/ProbabilityDistribution.java b/maths/demo/src/java/main/org/uncommons/maths/demo/ProbabilityDistribution.java
new file mode 100644
index 0000000..f9339ef
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/ProbabilityDistribution.java
@@ -0,0 +1,125 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import org.uncommons.maths.number.NumberGenerator;
+
+/**
+ * Encapsulates a probability distribution. Provides both theoretical
+ * values for the distribution as well as a way of randomly generating
+ * values that follow this distribution.
+ * @author Daniel Dyer
+ */
+abstract class ProbabilityDistribution
+{
+ protected abstract NumberGenerator<?> createValueGenerator(Random rng);
+
+ public Map<Double, Double> generateValues(int count,
+ Random rng)
+ {
+ Map<Double, Double> values = isDiscrete()
+ ? generateDiscreteValues(count, rng)
+ : generateContinuousValues(count, rng);
+
+ double sum = 0;
+ for (Double key : values.keySet())
+ {
+ Double value = values.get(key);
+ values.put(key, value / count);
+ sum += value;
+ }
+ assert Math.round(sum) == count : "Wrong total: " + sum;
+ return values;
+ }
+
+
+ private Map<Double, Double> generateDiscreteValues(int count,
+ Random rng)
+ {
+ NumberGenerator<?> generator = createValueGenerator(rng);
+ Map<Double, Double> values = new HashMap<Double, Double>();
+ for (int i = 0; i < count; i++)
+ {
+ double value = generator.nextValue().doubleValue();
+ Double aggregate = values.get(value);
+ aggregate = aggregate == null ? 0 : aggregate;
+ values.put(value, ++aggregate);
+ }
+ return values;
+ }
+
+
+ private Map<Double, Double> generateContinuousValues(int count,
+ Random rng)
+ {
+ NumberGenerator<?> generator = createValueGenerator(rng);
+ double[] values = new double[count];
+ double min = Double.MAX_VALUE;
+ double max = Double.MIN_VALUE;
+ for (int i = 0; i < count; i++)
+ {
+ double value = generator.nextValue().doubleValue();
+ min = Math.min(value, min);
+ max = Math.max(value, max);
+ values[i] = value;
+ }
+ return doQuantization(max, min, values);
+ }
+
+
+ /**
+ * Convert the continuous values into discrete values by chopping up
+ * the distribution into several equally-sized intervals.
+ */
+ protected static Map<Double, Double> doQuantization(double max,
+ double min,
+ double[] values)
+ {
+ double range = max - min;
+ int noIntervals = 20;
+ double intervalSize = range / noIntervals;
+ int[] intervals = new int[noIntervals];
+ for (double value : values)
+ {
+ int interval = Math.min(noIntervals - 1,
+ (int) Math.floor((value - min) / intervalSize));
+ assert interval >= 0 && interval < noIntervals : "Invalid interval: " + interval;
+ ++intervals[interval];
+ }
+ Map<Double, Double> discretisedValues = new HashMap<Double, Double>();
+ for (int i = 0; i < intervals.length; i++)
+ {
+ // Correct the value to take into account the size of the interval.
+ double value = (1 / intervalSize) * (double) intervals[i];
+ discretisedValues.put(min + ((i + 0.5) * intervalSize), value);
+ }
+ return discretisedValues;
+ }
+
+
+ public abstract Map<Double, Double> getExpectedValues();
+
+ public abstract double getExpectedMean();
+
+ public abstract double getExpectedStandardDeviation();
+
+ public abstract String getDescription();
+
+ public abstract boolean isDiscrete();
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/RNGPanel.java b/maths/demo/src/java/main/org/uncommons/maths/demo/RNGPanel.java
new file mode 100644
index 0000000..44d7235
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/RNGPanel.java
@@ -0,0 +1,96 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.util.Random;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import javax.swing.BorderFactory;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SpringLayout;
+import org.uncommons.maths.random.AESCounterRNG;
+import org.uncommons.maths.random.CMWC4096RNG;
+import org.uncommons.maths.random.CellularAutomatonRNG;
+import org.uncommons.maths.random.JavaRNG;
+import org.uncommons.maths.random.MersenneTwisterRNG;
+import org.uncommons.maths.random.XORShiftRNG;
+import org.uncommons.swing.SpringUtilities;
+
+/**
+ * Controls for selecing a random number generator and a number of values
+ * to generate.
+ * @author Daniel Dyer
+ */
+class RNGPanel extends JPanel
+{
+ private final JComboBox rngCombo = new JComboBox();
+ private final SpinnerNumberModel iterationsNumberModel = new SpinnerNumberModel(10000, 10, 1000000, 100);
+
+ private final SortedMap<String, Random> rngs = new TreeMap<String, Random>();
+ {
+ try
+ {
+ rngs.put("AES", new AESCounterRNG());
+ rngs.put("Cellular Automaton", new CellularAutomatonRNG());
+ rngs.put("CMWC 4096", new CMWC4096RNG());
+ rngs.put("JDK RNG", new JavaRNG());
+ rngs.put("Mersenne Twister", new MersenneTwisterRNG());
+ rngs.put("SecureRandom", new SecureRandom());
+ rngs.put("XOR Shift", new XORShiftRNG());
+ }
+ catch (GeneralSecurityException ex)
+ {
+ throw new IllegalStateException("Failed to initialise RNGs.", ex);
+ }
+ }
+
+
+ public RNGPanel()
+ {
+ super(new SpringLayout());
+ for (String name : rngs.keySet())
+ {
+ rngCombo.addItem(name);
+ }
+ rngCombo.setSelectedIndex(3); // Mersenne Twister.
+ add(rngCombo);
+ add(new JLabel("No. Values: "));
+ add(new JSpinner(iterationsNumberModel));
+ setBorder(BorderFactory.createTitledBorder("RNG"));
+ SpringUtilities.makeCompactGrid(this, 3, 1, 6, 6, 6, 6);
+ }
+
+
+ public Random getRNG()
+ {
+ return rngs.get((String) rngCombo.getSelectedItem());
+ }
+
+
+ /**
+ * Returns the number of values to be generated, as specified by the user.
+ */
+ public int getIterations()
+ {
+ return iterationsNumberModel.getNumber().intValue();
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/RandomDemo.java b/maths/demo/src/java/main/org/uncommons/maths/demo/RandomDemo.java
new file mode 100644
index 0000000..e4a6965
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/RandomDemo.java
@@ -0,0 +1,157 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.awt.BorderLayout;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Map;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.SwingUtilities;
+import org.uncommons.swing.SwingBackgroundTask;
+
+/**
+ * Demo application that demonstrates the generation of random values using
+ * different probability distributions.
+ * @author Daniel Dyer
+ */
+public class RandomDemo extends JFrame
+{
+ private final DistributionPanel distributionPanel = new DistributionPanel();
+ private final RNGPanel rngPanel = new RNGPanel();
+ private final GraphPanel graphPanel = new GraphPanel();
+
+ public RandomDemo()
+ {
+ super("Uncommons Maths - Random Numbers Demo");
+ setLayout(new BorderLayout());
+ add(createControls(), BorderLayout.WEST);
+ add(graphPanel, BorderLayout.CENTER);
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ setSize(700, 500);
+ setMinimumSize(new Dimension(500, 250));
+ validate();
+ }
+
+
+ private JComponent createControls()
+ {
+ Box controls = new Box(BoxLayout.Y_AXIS);
+ controls.add(distributionPanel);
+ controls.add(rngPanel);
+
+ JButton executeButton = new JButton("Go");
+ executeButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent actionEvent)
+ {
+ RandomDemo.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+ new SwingBackgroundTask<GraphData>()
+ {
+ private ProbabilityDistribution distribution;
+
+ protected GraphData performTask()
+ {
+ distribution = distributionPanel.createProbabilityDistribution();
+
+ Map<Double, Double> observedValues = distribution.generateValues(rngPanel.getIterations(),
+ rngPanel.getRNG());
+ Map<Double, Double> expectedValues = distribution.getExpectedValues();
+ return new GraphData(observedValues,
+ expectedValues,
+ distribution.getExpectedMean(),
+ distribution.getExpectedStandardDeviation());
+ }
+
+ protected void postProcessing(GraphData data)
+ {
+ graphPanel.generateGraph(distribution.getDescription(),
+ data.getObservedValues(),
+ data.getExpectedValues(),
+ data.getExpectedMean(),
+ data.getExpectedStandardDeviation(),
+ distribution.isDiscrete());
+ RandomDemo.this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+ }.execute();
+ }
+ });
+ controls.add(executeButton);
+ return controls;
+ }
+
+
+ public static void main(String[] args)
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ new RandomDemo().setVisible(true);
+ }
+ });
+ }
+
+
+ private static class GraphData
+ {
+ private final Map<Double, Double> observedValues;
+ private final Map<Double, Double> expectedValues;
+ private final double expectedMean;
+ private final double expectedStandardDeviation;
+
+
+ public GraphData(Map<Double, Double> observedValues,
+ Map<Double, Double> expectedValues,
+ double expectedMean,
+ double expectedStandardDeviation)
+ {
+ this.observedValues = observedValues;
+ this.expectedValues = expectedValues;
+ this.expectedMean = expectedMean;
+ this.expectedStandardDeviation = expectedStandardDeviation;
+ }
+
+
+ public Map<Double, Double> getObservedValues()
+ {
+ return observedValues;
+ }
+
+
+ public Map<Double, Double> getExpectedValues()
+ {
+ return expectedValues;
+ }
+
+
+ public double getExpectedMean()
+ {
+ return expectedMean;
+ }
+
+ public double getExpectedStandardDeviation()
+ {
+ return expectedStandardDeviation;
+ }
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/UniformDistribution.java b/maths/demo/src/java/main/org/uncommons/maths/demo/UniformDistribution.java
new file mode 100644
index 0000000..489bd85
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/UniformDistribution.java
@@ -0,0 +1,77 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import org.uncommons.maths.random.DiscreteUniformGenerator;
+
+/**
+ * @author Daniel Dyer
+ */
+class UniformDistribution extends ProbabilityDistribution
+{
+ private final int min;
+ private final int max;
+
+ public UniformDistribution(int min, int max)
+ {
+ this.min = min;
+ this.max = max;
+ }
+
+
+ public Map<Double, Double> getExpectedValues()
+ {
+ Map<Double, Double> values = new HashMap<Double, Double>();
+ for (int i = min; i <= max; i++)
+ {
+ values.put((double) i, 1d / ((max - min) + 1));
+ }
+ return values;
+ }
+
+
+ protected DiscreteUniformGenerator createValueGenerator(Random rng)
+ {
+ return new DiscreteUniformGenerator(min, max, rng);
+ }
+
+
+ public double getExpectedMean()
+ {
+ return (max - min) / 2 + min;
+ }
+
+
+ public double getExpectedStandardDeviation()
+ {
+ return (max - min) / Math.sqrt(12);
+ }
+
+
+ public String getDescription()
+ {
+ return "Uniform Distribution (Range = " + min + "..." + max + ")";
+ }
+
+
+ public boolean isDiscrete()
+ {
+ return true;
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/maths/demo/UniformParametersPanel.java b/maths/demo/src/java/main/org/uncommons/maths/demo/UniformParametersPanel.java
new file mode 100644
index 0000000..58c1c3e
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/maths/demo/UniformParametersPanel.java
@@ -0,0 +1,51 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.maths.demo;
+
+import java.awt.BorderLayout;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.SpringLayout;
+import org.uncommons.swing.SpringUtilities;
+
+/**
+ * @author Daniel Dyer
+ */
+class UniformParametersPanel extends ParametersPanel
+{
+ private final SpinnerNumberModel minNumberModel = new SpinnerNumberModel(1, 0, 100, 1);
+ private final SpinnerNumberModel maxNumberModel = new SpinnerNumberModel(10, 1, 100, 1);
+
+ public UniformParametersPanel()
+ {
+ JPanel wrapper = new JPanel(new SpringLayout());
+ wrapper.add(new JLabel("Minimum: "));
+ wrapper.add(new JSpinner(minNumberModel));
+ wrapper.add(new JLabel("Maximum: "));
+ wrapper.add(new JSpinner(maxNumberModel));
+ SpringUtilities.makeCompactGrid(wrapper, 4, 1, 6, 6, 6, 6);
+ add(wrapper, BorderLayout.NORTH);
+ }
+
+
+ public UniformDistribution createProbabilityDistribution()
+ {
+ return new UniformDistribution(minNumberModel.getNumber().intValue(),
+ maxNumberModel.getNumber().intValue());
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/swing/SpringUtilities.java b/maths/demo/src/java/main/org/uncommons/swing/SpringUtilities.java
new file mode 100644
index 0000000..221d4f1
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/swing/SpringUtilities.java
@@ -0,0 +1,213 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.swing;
+
+import java.awt.Component;
+import java.awt.Container;
+import javax.swing.Spring;
+import javax.swing.SpringLayout;
+
+/**
+ * Utility methods for creating form-style or grid-style layouts with SpringLayout.
+ * Modified version of the class presented in the Sun Swing tutorial
+ * (http://java.sun.com/docs/books/tutorial/uiswing/layout/examples/SpringUtilities.java).
+ */
+public final class SpringUtilities
+{
+ private SpringUtilities()
+ {
+ // Private constructor prevents instantiation of utility class.
+ }
+
+
+ /**
+ * Aligns the first {@code rows} * {@code cols} components of {@code parent}
+ * in a grid. Each component is as big as the maximum preferred width and
+ * height of the components. The parent is made just big enough to fit them
+ * all.
+ * @param parent The container to layout.
+ * @param rows Number of rows
+ * @param cols Number of columns
+ * @param initialX x location to start the grid at
+ * @param initialY y location to start the grid at
+ * @param xPad x padding between cells
+ * @param yPad y padding between cells
+ */
+ public static void makeGrid(Container parent,
+ int rows,
+ int cols,
+ int initialX,
+ int initialY,
+ int xPad, int yPad)
+ {
+ if (!(parent.getLayout() instanceof SpringLayout))
+ {
+ throw new IllegalArgumentException("The first argument to makeGrid must use SpringLayout.");
+ }
+ SpringLayout layout = (SpringLayout) parent.getLayout();
+
+ Spring xPadSpring = Spring.constant(xPad);
+ Spring yPadSpring = Spring.constant(yPad);
+ Spring initialXSpring = Spring.constant(initialX);
+ Spring initialYSpring = Spring.constant(initialY);
+ int max = rows * cols;
+
+ // Calculate Springs that are the max of the width/height so that all
+ // cells have the same size.
+ Spring maxWidthSpring = layout.getConstraints(parent.getComponent(0)).getWidth();
+ Spring maxHeightSpring = layout.getConstraints(parent.getComponent(0)).getWidth();
+ for (int i = 1; i < max; i++)
+ {
+ SpringLayout.Constraints cons = layout.getConstraints(parent.getComponent(i));
+ maxWidthSpring = Spring.max(maxWidthSpring, cons.getWidth());
+ maxHeightSpring = Spring.max(maxHeightSpring, cons.getHeight());
+ }
+
+ // Apply the new width/height Spring. This forces all the
+ // components to have the same size.
+ for (int i = 0; i < max; i++)
+ {
+ SpringLayout.Constraints cons = layout.getConstraints(parent.getComponent(i));
+ cons.setWidth(maxWidthSpring);
+ cons.setHeight(maxHeightSpring);
+ }
+
+ // Then adjust the x/y constraints of all the cells so that they
+ // are aligned in a grid.
+ SpringLayout.Constraints lastConstraints = null;
+ SpringLayout.Constraints lastRowConstraints = null;
+ for (int i = 0; i < max; i++)
+ {
+ SpringLayout.Constraints constraints = layout.getConstraints(parent.getComponent(i));
+ if (i % cols == 0) // Start of new row.
+ {
+ lastRowConstraints = lastConstraints;
+ constraints.setX(initialXSpring);
+ }
+ else // X position depends on previous component.
+ {
+ constraints.setX(Spring.sum(lastConstraints.getConstraint(SpringLayout.EAST),
+ xPadSpring));
+ }
+
+ if (i / cols == 0) // First row.
+ {
+ constraints.setY(initialYSpring);
+ }
+ else // Y position depends on previous row.
+ {
+ constraints.setY(Spring.sum(lastRowConstraints.getConstraint(SpringLayout.SOUTH),
+ yPadSpring));
+ }
+ lastConstraints = constraints;
+ }
+
+ // Set the parent's size.
+ SpringLayout.Constraints pCons = layout.getConstraints(parent);
+ pCons.setConstraint(SpringLayout.SOUTH,
+ Spring.sum(Spring.constant(yPad),
+ lastConstraints.getConstraint(SpringLayout.SOUTH)));
+ pCons.setConstraint(SpringLayout.EAST,
+ Spring.sum(Spring.constant(xPad),
+ lastConstraints.getConstraint(SpringLayout.EAST)));
+ }
+
+
+ /**
+ * Aligns the first {@code rows} * {@code cols} components of {@code parent}
+ * in a grid. Each component in a column is as wide as the maximum preferred
+ * width of the components in that column; height is similarly determined for
+ * each row. The parent is made just big enough to fit them all.
+ * @param parent The container to layout.
+ * @param rows number of rows
+ * @param columns number of columns
+ * @param initialX x location to start the grid at
+ * @param initialY y location to start the grid at
+ * @param xPad x padding between cells
+ * @param yPad y padding between cells
+ */
+ public static void makeCompactGrid(Container parent,
+ int rows,
+ int columns,
+ int initialX,
+ int initialY,
+ int xPad,
+ int yPad)
+ {
+ if (!(parent.getLayout() instanceof SpringLayout))
+ {
+ throw new IllegalArgumentException("The first argument to makeCompactGrid must use SpringLayout.");
+ }
+ SpringLayout layout = (SpringLayout) parent.getLayout();
+
+ // Align all cells in each column and make them the same width.
+ Spring x = Spring.constant(initialX);
+ for (int c = 0; c < columns; c++)
+ {
+ Spring width = Spring.constant(0);
+ for (int r = 0; r < rows; r++)
+ {
+ width = Spring.max(width,
+ getConstraintsForCell(r, c, parent, columns).getWidth());
+ }
+ for (int r = 0; r < rows; r++)
+ {
+ SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, columns);
+ constraints.setX(x);
+ constraints.setWidth(width);
+ }
+ x = Spring.sum(x, Spring.sum(width, Spring.constant(xPad)));
+ }
+
+ // Align all cells in each row and make them the same height.
+ Spring y = Spring.constant(initialY);
+ for (int r = 0; r < rows; r++)
+ {
+ Spring height = Spring.constant(0);
+ for (int c = 0; c < columns; c++)
+ {
+ height = Spring.max(height,
+ getConstraintsForCell(r, c, parent, columns).getHeight());
+ }
+ for (int c = 0; c < columns; c++)
+ {
+ SpringLayout.Constraints constraints = getConstraintsForCell(r, c, parent, columns);
+ constraints.setY(y);
+ constraints.setHeight(height);
+ }
+ y = Spring.sum(y, Spring.sum(height, Spring.constant(yPad)));
+ }
+
+ // Set the parent's size.
+ SpringLayout.Constraints parentConstraints = layout.getConstraints(parent);
+ parentConstraints.setConstraint(SpringLayout.SOUTH, y);
+ parentConstraints.setConstraint(SpringLayout.EAST, x);
+ }
+
+
+ /**
+ * Helper method for {@link #makeCompactGrid(Container, int, int, int, int, int, int)}.
+ */
+ private static SpringLayout.Constraints getConstraintsForCell(int row,
+ int col,
+ Container parent,
+ int cols)
+ {
+ SpringLayout layout = (SpringLayout) parent.getLayout();
+ Component c = parent.getComponent(row * cols + col);
+ return layout.getConstraints(c);
+ }
+}
diff --git a/maths/demo/src/java/main/org/uncommons/swing/SwingBackgroundTask.java b/maths/demo/src/java/main/org/uncommons/swing/SwingBackgroundTask.java
new file mode 100644
index 0000000..728a441
--- /dev/null
+++ b/maths/demo/src/java/main/org/uncommons/swing/SwingBackgroundTask.java
@@ -0,0 +1,100 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.swing;
+
+import java.util.concurrent.CountDownLatch;
+import javax.swing.SwingUtilities;
+
+/**
+ * A task that is executed on a background thread and then updates
+ * a Swing GUI. A task may only be executed once.
+ * @author Daniel Dyer
+ * @param <V> Type of result generated by the task.
+ */
+public abstract class SwingBackgroundTask<V>
+{
+ // Used to assign thread IDs to make threads easier to identify when debugging.
+ private static int instanceCount = 0;
+
+ private final CountDownLatch latch = new CountDownLatch(1);
+ private final int id;
+
+ protected SwingBackgroundTask()
+ {
+ synchronized (SwingBackgroundTask.class)
+ {
+ this.id = instanceCount;
+ ++instanceCount;
+ }
+ }
+
+
+ /**
+ * Asynchronous call that begins execution of the task
+ * and returns immediately.
+ */
+ public void execute()
+ {
+ Runnable task = new Runnable()
+ {
+ public void run()
+ {
+ final V result = performTask();
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ postProcessing(result);
+ latch.countDown();
+ }
+ });
+ }
+ };
+ new Thread(task, "SwingBackgroundTask-" + id).start();
+ }
+
+
+ /**
+ * Waits for the execution of this task to complete. If the {@link #execute()}
+ * method has not yet been invoked, this method will block indefinitely.
+ * @throws InterruptedException If the thread executing the task
+ * is interrupted.
+ */
+ public void waitForCompletion() throws InterruptedException
+ {
+ latch.await();
+ }
+
+
+ /**
+ * Performs the processing of the task and returns a result.
+ * Implement in sub-classes to provide the task logic. This method will
+ * run on a background thread and not on the Event Dispatch Thread and
+ * therefore should not manipulate any Swing components.
+ * @return The result of executing this task.
+ */
+ protected abstract V performTask();
+
+
+ /**
+ * This method is invoked, on the Event Dispatch Thread, after the task
+ * has been executed.
+ * This should be implemented in sub-classes in order to provide GUI
+ * updates that should occur following task completion.
+ * @param result The result from the {@link #performTask()} method.
+ */
+ protected abstract void postProcessing(V result);
+}
diff --git a/maths/demo/src/java/test/org/uncommons/swing/SwingBackgroundTaskTest.java b/maths/demo/src/java/test/org/uncommons/swing/SwingBackgroundTaskTest.java
new file mode 100644
index 0000000..899eabe
--- /dev/null
+++ b/maths/demo/src/java/test/org/uncommons/swing/SwingBackgroundTaskTest.java
@@ -0,0 +1,58 @@
+// ============================================================================
+// Copyright 2006-2012 Daniel W. Dyer
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// ============================================================================
+package org.uncommons.swing;
+
+import javax.swing.SwingUtilities;
+import org.testng.annotations.Test;
+
+/**
+ * Unit test for {@link SwingBackgroundTask}. Ensures code is
+ * executed on correct threads.
+ * @author Daniel Dyer
+ */
+public class SwingBackgroundTaskTest
+{
+ private boolean taskExecuted;
+ private boolean taskOnEDT;
+ private boolean postProcessingExecuted;
+ private boolean postProcessingOnEDT;
+
+ @Test
+ public void testExecutionThreads() throws InterruptedException
+ {
+ SwingBackgroundTask<Object> testTask = new SwingBackgroundTask<Object>()
+ {
+ protected Object performTask()
+ {
+ taskExecuted = true;
+ taskOnEDT = SwingUtilities.isEventDispatchThread();
+ return null;
+ }
+
+ protected void postProcessing(Object result)
+ {
+ postProcessingExecuted = true;
+ postProcessingOnEDT = SwingUtilities.isEventDispatchThread();
+ }
+ };
+ testTask.execute();
+ testTask.waitForCompletion();
+ assert taskExecuted : "Task was not executed.";
+ assert postProcessingExecuted : "Post-processing was not executed.";
+ assert !taskOnEDT : "Task was executed on EDT.";
+ assert postProcessingOnEDT : "Post-processing was not executed on EDT.";
+ }
+}