diff options
author | Yohann Roussel <yroussel@google.com> | 2014-03-19 16:25:37 +0100 |
---|---|---|
committer | Yohann Roussel <yroussel@google.com> | 2014-03-20 15:13:33 +0100 |
commit | 4eceb95409e844fdc33c9c706e1dc307bfd40303 (patch) | |
tree | ee9f4f3fc79f757c79081c336bce4f1782c6ccd8 /maths/demo | |
parent | 3d2402901b1a6462e2cf47a6fd09711f327961c3 (diff) | |
download | toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.zip toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.gz toolchain_jack-4eceb95409e844fdc33c9c706e1dc307bfd40303.tar.bz2 |
Initial Jack import.
Change-Id: I953cf0a520195a7187d791b2885848ad0d5a9b43
Diffstat (limited to 'maths/demo')
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."; + } +} |