com.comphenix.xp.SampleRange.java Source code

Java tutorial

Introduction

Here is the source code for com.comphenix.xp.SampleRange.java

Source

/*
 *  ExperienceMod - Bukkit server plugin for modifying the experience system in Minecraft.
 *  Copyright (C) 2012 Kristian S. Stangeland
 *
 *  This program is free software; you can redistribute it and/or modify it under the terms of the 
 *  GNU General Public License as published by the Free Software Foundation; either version 2 of 
 *  the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; 
 *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with this program; 
 *  if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 *  02111-1307 USA
 */

package com.comphenix.xp;

import java.util.Random;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

public class SampleRange {
    private double start;
    private double end;

    public static SampleRange DEFAULT = new SampleRange(0);

    /**
     * Constructs a range of the form [value, value].
     * @param value The end value.
     */
    public SampleRange(double value) {
        this.start = value;
        this.end = value;
    }

    public SampleRange(double start, double end) {
        if (end < start)
            throw new IllegalArgumentException("Illegal range. The first value must be less than the last.");

        this.start = start;
        this.end = end;
    }

    public double getStart() {
        return start;
    }

    public double getEnd() {
        return end;
    }

    public SampleRange multiply(double multiply) {
        // Consider the average value of a uniform distribution
        return new SampleRange(start * multiply, end * multiply);
    }

    public double sampleDouble(Random rnd) {
        if (start == end)
            return start;
        else
            return start + (end - start) * rnd.nextDouble();
    }

    public int getMinimum() {
        return (int) Math.floor(start);
    }

    public int getMaximum() {
        return (int) Math.ceil(end);
    }

    public int sampleInt(Random rnd) {

        /*
         * Imagine our range is 0.7 - 5.3:
         *
         * 0.7  1          2          3          4          5  5.3
          *   
         *  |---|----------|----------|----------|----------|---|
         *  |   |          |          |          |          |   |
         *  |   |          |          |          |          |   |
         *  |---|----------|----------|----------|----------|---|
         *
         * The integer part, 1 - 5, is easy. To get a random number between and
         * including 1 and 5, we simply get a random number between 0 and 4 
         * and add one.
          * 
         * The beginning, 0.7 - 1.0, covers 30% of an integer. One interpretation is
         * that this indicates the probability of getting that integer. 
         *
         * So, we end up with a 30% probability of getting 0 and 5.3 - 5 = 30% 
         * probability of getting 4.
         */

        int value = 0;

        // Convert the range to an integer equivalent. 
        // Notice that we round to shrink the range.
        int a = (int) Math.ceil(start);
        int b = (int) Math.floor(end);

        // Special case
        if ((int) start == (int) end) {
            return sampleIntReduced(rnd);
        }

        // The decimal leftover
        double dA = a - start;
        double dB = end - b;

        // Sample an integer from the range [a, b] (inclusive)
        if (b > a) {
            value = a + rnd.nextInt(b - a + 1); // Add one since nextInt is exclusive
        }

        // The remainder is the probability of choosing the previous value
        if (dA > 0 && rnd.nextDouble() < dA)
            value--;

        // And here it is the probability of choosing the next value
        if (dB > 0 && rnd.nextDouble() < dB)
            value++;

        return value;
    }

    private int sampleIntReduced(Random rnd) {
        double value = sampleDouble(rnd);

        // Probability of adding the fraction
        double fraction = value - Math.floor(value);
        double toAdd = rnd.nextDouble() < fraction ? Math.signum(value) : 0;

        return (int) value + (int) toAdd;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 31).append(start).append(end).toHashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null)
            return false;
        if (obj == this)
            return true;
        if (obj.getClass() != getClass())
            return false;

        SampleRange other = (SampleRange) obj;
        return new EqualsBuilder().append(start, other.start).append(end, other.end).isEquals();
    }

    @Override
    public String toString() {
        if (start == end)
            return "{" + start + "}";
        else
            return "{start: " + start + ", end: " + end + "}";
    }
}