NOTE: This a different bug from 1357474.
I would expect to be able to create an IntegerGene with lower and upper bounds of Integer.MIN_VALUE and Integer.MAX_VALUE respectively, but setToRandomValue() will not behave properly for any confuration where the difference between the upper and lower bounds is more than Integer.MAX_VALUE.
Here is a test that exposes the problem:
public void testIntegerGeneSupportsFullIntegerRange() throws Exception {
Gene gene = new IntegerGene(conf, Integer.MIN_VALUE, Integer.MAX_VALUE);
gene.setAllele(new Integer(5));
gene.setToRandomValue(new RandomGeneratorForTesting(0.2d));
int expectedValue = (int) (Integer.MIN_VALUE +
Math.round((0.2d *
((long) Integer.MAX_VALUE - (long) Integer.MIN_VALUE))));
assertEquals(new Integer(expectedValue), gene.getAllele());
}
A solution is to caste the upper and lower bounds to longs before subtracting them:
public void setToRandomValue(final RandomGenerator a_numberGenerator) {
double randomValue = ((long) m_upperBounds - (long) m_lowerBounds) *
a_numberGenerator.nextDouble() +
m_lowerBounds;
setAllele(new Integer( (int) Math.round(randomValue)));
}
I have had a closer look and can see other places where IntegerGene has the expression (m_upperBounds - m_lowerBounds) and do break without first casting them to longs.
mapValueToWithinBounds() will break when (m_upperBounds - m_lowerBounds) is greater than Integer.MAX_VALUE. e.g.
public void testMapValueToWithinBoundsSupportsFullIntegerRange() throws Exception {
conf.setRandomGenerator(new RandomGeneratorForTesting(0.2d));
int lower = Integer.MIN_VALUE + 1;
int upper = Integer.MAX_VALUE;
IntegerGene gene = new IntegerGene(conf, lower, upper);
gene.setAllele(Integer.MIN_VALUE);
int expectedValue = (int) (lower +
Math.round((0.2d *
((long) upper - (long) lower))));
assertEquals(new Integer(expectedValue), gene.getAllele());
}
To fix it, you could replace in mapValueToWithinBounds()
if (m_upperBounds - m_lowerBounds == 0) {
setAllele(new Integer(m_lowerBounds));
}
else {
setAllele(new Integer(rn.nextInt(m_upperBounds - m_lowerBounds) +
m_lowerBounds));
}
with:
if (m_upperBounds == m_lowerBounds) {
setAllele(new Integer(m_lowerBounds));
}
else {
setToRandomValue(rn);
}
(This will break existing tests as they will need to be modified to expect calls to get the nextDouble() instead of nextInt())
Similarly, applyMutation() breaks this test:
public void testApplyMutationSupportsFullIntegerRange()
throws Exception {
IntegerGene gene = new IntegerGene(conf, Integer.MIN_VALUE, Integer.MAX_VALUE);
gene.setAllele(null);
gene.applyMutation(0, 0.4d);
double range = ((long)Integer.MAX_VALUE - (long)Integer.MIN_VALUE) * 0.4d;
int expectedValue = (int) (range + Integer.MIN_VALUE);
assertEquals(expectedValue, gene.intValue());
}
This can be fixed by replacing in applyMutation()
double range = (m_upperBounds - m_lowerBounds) * a_percentage;
with:
double range = ((long) m_upperBounds - (long) m_lowerBounds) * a_percentage;
David, thanx a lot for your comprehensive report and your suggestions, which I followed. The corrections are checked in to CVS now. Also two unit tests were added as you wrote.
Klaus
NB: Sorry for the delayed answer