/*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* 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.optaplanner.core.api.score.buildin.simplebigdecimal;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.optaplanner.core.api.score.AbstractScore;
import org.optaplanner.core.api.score.Score;
/**
* This {@link Score} is based on 1 level of {@link BigDecimal} constraints.
* <p>
* This class is immutable.
* @see Score
*/
public final class SimpleBigDecimalScore extends AbstractScore<SimpleBigDecimalScore> {
public static final SimpleBigDecimalScore ZERO = new SimpleBigDecimalScore(0, BigDecimal.ZERO);
public static SimpleBigDecimalScore parseScore(String scoreString) {
String[] scoreTokens = parseScoreTokens(SimpleBigDecimalScore.class, scoreString, "");
int initScore = parseInitScore(SimpleBigDecimalScore.class, scoreString, scoreTokens[0]);
BigDecimal score = parseLevelAsBigDecimal(SimpleBigDecimalScore.class, scoreString, scoreTokens[1]);
return valueOfUninitialized(initScore, score);
}
public static SimpleBigDecimalScore valueOfUninitialized(int initScore, BigDecimal score) {
return new SimpleBigDecimalScore(initScore, score);
}
public static SimpleBigDecimalScore valueOf(BigDecimal score) {
return new SimpleBigDecimalScore(0, score);
}
// ************************************************************************
// Fields
// ************************************************************************
private final BigDecimal score;
/**
* Private default constructor for default marshalling/unmarshalling of unknown frameworks that use reflection.
* Such integration is always inferior to the specialized integration modules, such as
* optaplanner-persistence-jpa, optaplanner-persistence-xstream, optaplanner-persistence-jaxb, ...
*/
@SuppressWarnings("unused")
private SimpleBigDecimalScore() {
super(Integer.MIN_VALUE);
score = null;
}
private SimpleBigDecimalScore(int initScore, BigDecimal score) {
super(initScore);
this.score = score;
}
/**
* The total of the broken negative constraints and fulfilled positive constraints.
* Their weight is included in the total.
* The score is usually a negative number because most use cases only have negative constraints.
* @return higher is better, usually negative, 0 if no constraints are broken/fulfilled
*/
public BigDecimal getScore() {
return score;
}
// ************************************************************************
// Worker methods
// ************************************************************************
@Override
public SimpleBigDecimalScore toInitializedScore() {
return initScore == 0 ? this : new SimpleBigDecimalScore(0, score);
}
@Override
public SimpleBigDecimalScore withInitScore(int newInitScore) {
assertNoInitScore();
return new SimpleBigDecimalScore(newInitScore, score);
}
@Override
public SimpleBigDecimalScore add(SimpleBigDecimalScore augment) {
return new SimpleBigDecimalScore(
initScore + augment.getInitScore(),
score.add(augment.getScore()));
}
@Override
public SimpleBigDecimalScore subtract(SimpleBigDecimalScore subtrahend) {
return new SimpleBigDecimalScore(
initScore - subtrahend.getInitScore(),
score.subtract(subtrahend.getScore()));
}
@Override
public SimpleBigDecimalScore multiply(double multiplicand) {
// Intentionally not taken "new BigDecimal(multiplicand, MathContext.UNLIMITED)"
// because together with the floor rounding it gives unwanted behaviour
BigDecimal multiplicandBigDecimal = BigDecimal.valueOf(multiplicand);
// The (unspecified) scale/precision of the multiplicand should have no impact on the returned scale/precision
return new SimpleBigDecimalScore(
(int) Math.floor(initScore * multiplicand),
score.multiply(multiplicandBigDecimal).setScale(score.scale(), RoundingMode.FLOOR));
}
@Override
public SimpleBigDecimalScore divide(double divisor) {
// Intentionally not taken "new BigDecimal(multiplicand, MathContext.UNLIMITED)"
// because together with the floor rounding it gives unwanted behaviour
BigDecimal divisorBigDecimal = BigDecimal.valueOf(divisor);
// The (unspecified) scale/precision of the divisor should have no impact on the returned scale/precision
return new SimpleBigDecimalScore(
(int) Math.floor(initScore / divisor),
score.divide(divisorBigDecimal, score.scale(), RoundingMode.FLOOR));
}
@Override
public SimpleBigDecimalScore power(double exponent) {
// Intentionally not taken "new BigDecimal(multiplicand, MathContext.UNLIMITED)"
// because together with the floor rounding it gives unwanted behaviour
BigDecimal exponentBigDecimal = BigDecimal.valueOf(exponent);
// The (unspecified) scale/precision of the exponent should have no impact on the returned scale/precision
// TODO FIXME remove .intValue() so non-integer exponents produce correct results
// None of the normal Java libraries support BigDecimal.pow(BigDecimal)
return new SimpleBigDecimalScore(
(int) Math.floor(Math.pow(initScore, exponent)),
score.pow(exponentBigDecimal.intValue()).setScale(score.scale(), RoundingMode.FLOOR));
}
@Override
public SimpleBigDecimalScore negate() {
return new SimpleBigDecimalScore(-initScore, score.negate());
}
@Override
public Number[] toLevelNumbers() {
return new Number[]{score};
}
@Override
public boolean equals(Object o) {
// A direct implementation (instead of EqualsBuilder) to avoid dependencies
if (this == o) {
return true;
} else if (o instanceof SimpleBigDecimalScore) {
SimpleBigDecimalScore other = (SimpleBigDecimalScore) o;
return initScore == other.getInitScore()
&& score.stripTrailingZeros().equals(other.getScore().stripTrailingZeros());
} else {
return false;
}
}
@Override
public int hashCode() {
// A direct implementation (instead of HashCodeBuilder) to avoid dependencies
return ((17 * 37)
+ initScore) * 37
+ score.stripTrailingZeros().hashCode();
}
@Override
public int compareTo(SimpleBigDecimalScore other) {
// A direct implementation (instead of CompareToBuilder) to avoid dependencies
if (initScore != other.getInitScore()) {
return initScore < other.getInitScore() ? -1 : 1;
} else {
return score.compareTo(other.getScore());
}
}
@Override
public String toShortString() {
return buildShortString((n) -> ((BigDecimal) n).compareTo(BigDecimal.ZERO) != 0, "");
}
@Override
public String toString() {
return getInitPrefix() + score;
}
@Override
public boolean isCompatibleArithmeticArgument(Score otherScore) {
return otherScore instanceof SimpleBigDecimalScore;
}
}