Update Commons Math.
Code refactoring.
This commit is contained in:
parent
3adb216e56
commit
3852c49972
5 changed files with 107 additions and 90 deletions
|
|
@ -98,8 +98,8 @@
|
|||
|
||||
<!-- Commons Math-->
|
||||
<dependency>
|
||||
<groupId>commons-math</groupId>
|
||||
<artifactId>commons-math</artifactId>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-math3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>joda-time</groupId>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-ehcache</artifactId>
|
||||
</dependency>
|
||||
|
|
@ -99,8 +99,8 @@
|
|||
|
||||
<!-- Commons Math-->
|
||||
<dependency>
|
||||
<groupId>commons-math</groupId>
|
||||
<artifactId>commons-math</artifactId>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-math3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.math.fraction.Fraction;
|
||||
import org.apache.commons.math3.fraction.Fraction;
|
||||
|
||||
/**
|
||||
* @author Óscar González Fernández <ogonzalez@igalia.com>
|
||||
|
|
@ -32,24 +32,26 @@ import org.apache.commons.math.fraction.Fraction;
|
|||
public class ProportionalDistributor {
|
||||
|
||||
public static ProportionalDistributor create(int... initialShares) {
|
||||
return new ProportionalDistributor(toFractions(
|
||||
sumIntegerParts(initialShares), initialShares));
|
||||
return new ProportionalDistributor(toFractions(sumIntegerParts(initialShares), initialShares));
|
||||
}
|
||||
|
||||
private static int sumIntegerParts(int[] numbers) {
|
||||
int sum = 0;
|
||||
|
||||
for (int each : numbers) {
|
||||
sum += each;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static Fraction[] toFractions(int initialTotal, int... shares) {
|
||||
Fraction[] result = new Fraction[shares.length];
|
||||
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = initialTotal == 0 ? Fraction.ZERO : new Fraction(
|
||||
shares[i], initialTotal);
|
||||
result[i] = initialTotal == 0 ? Fraction.ZERO : new Fraction(shares[i], initialTotal);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -57,14 +59,15 @@ public class ProportionalDistributor {
|
|||
* Note: this class has a natural ordering that is inconsistent with equals.
|
||||
*
|
||||
*/
|
||||
private static class FractionWithPosition implements
|
||||
Comparable<FractionWithPosition> {
|
||||
private static class FractionWithPosition implements Comparable<FractionWithPosition> {
|
||||
|
||||
public static List<FractionWithPosition> transform(Fraction[] fractions) {
|
||||
List<FractionWithPosition> result = new ArrayList<FractionWithPosition>();
|
||||
List<FractionWithPosition> result = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < fractions.length; i++) {
|
||||
result.add(new FractionWithPosition(i, fractions[i]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -90,49 +93,56 @@ public class ProportionalDistributor {
|
|||
}
|
||||
|
||||
public int[] distribute(final int total) {
|
||||
if (fractions.length == 0) {
|
||||
if ( fractions.length == 0 ) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
int[] result = new int[fractions.length];
|
||||
int remaining = total - assignIntegerParts(total, result);
|
||||
if (remaining == 0) {
|
||||
|
||||
if ( remaining == 0 ) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Fraction[] currentFractions = toFractions(total, result);
|
||||
assignRemaining(result, currentFractions, remaining);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int assignIntegerParts(int current, int[] result) {
|
||||
Fraction currentAsFraction = new Fraction(current, 1);
|
||||
int substract = 0;
|
||||
|
||||
for (int i = 0; i < fractions.length; i++) {
|
||||
int intValue = fractions[i].multiply(currentAsFraction).intValue();
|
||||
if (intValue > 0) {
|
||||
|
||||
if ( intValue > 0 ) {
|
||||
result[i] = result[i] + intValue;
|
||||
substract += intValue;
|
||||
}
|
||||
}
|
||||
|
||||
return substract;
|
||||
}
|
||||
|
||||
private void assignRemaining(int[] result, Fraction[] currentProportions,
|
||||
int remaining) {
|
||||
List<FractionWithPosition> transform = FractionWithPosition
|
||||
.transform(difference(currentProportions));
|
||||
private void assignRemaining(int[] result, Fraction[] currentProportions, int remaining) {
|
||||
List<FractionWithPosition> transform = FractionWithPosition.transform(difference(currentProportions));
|
||||
Collections.sort(transform, Collections.reverseOrder());
|
||||
|
||||
for (int i = 0; i < remaining; i++) {
|
||||
FractionWithPosition proportionWithPosition = transform.get(i
|
||||
% currentProportions.length);
|
||||
FractionWithPosition proportionWithPosition = transform.get(i % currentProportions.length);
|
||||
result[proportionWithPosition.position] = result[proportionWithPosition.position] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private Fraction[] difference(Fraction[] pr) {
|
||||
Fraction[] result = new Fraction[fractions.length];
|
||||
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = fractions[i].subtract(pr[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,9 +26,8 @@ import java.util.List;
|
|||
|
||||
import org.apache.commons.lang.Validate;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.commons.math.FunctionEvaluationException;
|
||||
import org.apache.commons.math.analysis.SplineInterpolator;
|
||||
import org.apache.commons.math.analysis.UnivariateRealFunction;
|
||||
import org.apache.commons.math3.analysis.interpolation.SplineInterpolator;
|
||||
import org.apache.commons.math3.analysis.UnivariateFunction;
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.libreplan.business.common.ProportionalDistributor;
|
||||
|
|
@ -47,32 +46,30 @@ public enum StretchesFunctionTypeEnum {
|
|||
|
||||
@Override
|
||||
public void apply(ResourceAllocation<?> allocation,
|
||||
List<Interval> intervalsDefinedByStreches,
|
||||
LocalDate startInclusive, LocalDate endExclusive,
|
||||
int totalHours) {
|
||||
Interval.apply(allocation, intervalsDefinedByStreches,
|
||||
startInclusive, endExclusive, totalHours);
|
||||
List<Interval> intervalsDefinedByStreches,
|
||||
LocalDate startInclusive, LocalDate endExclusive,
|
||||
int totalHours) {
|
||||
|
||||
Interval.apply(allocation, intervalsDefinedByStreches, startInclusive, endExclusive, totalHours);
|
||||
}
|
||||
},
|
||||
INTERPOLATED {
|
||||
|
||||
@Override
|
||||
public void apply(ResourceAllocation<?> allocation,
|
||||
List<Interval> intervalsDefinedByStreches,
|
||||
LocalDate startInclusive, LocalDate endExclusive,
|
||||
int totalHours) {
|
||||
List<Interval> intervalsDefinedByStreches,
|
||||
LocalDate startInclusive, LocalDate endExclusive,
|
||||
int totalHours) {
|
||||
|
||||
final Task task = allocation.getTask();
|
||||
|
||||
double[] x = Interval.getDayPointsFor(task.getStartAsLocalDate(),
|
||||
intervalsDefinedByStreches);
|
||||
double[] x = Interval.getDayPointsFor(task.getStartAsLocalDate(), intervalsDefinedByStreches);
|
||||
assert x.length == 1 + intervalsDefinedByStreches.size();
|
||||
double[] y = Interval.getHoursPointsFor(totalHours,
|
||||
intervalsDefinedByStreches);
|
||||
|
||||
double[] y = Interval.getHoursPointsFor(totalHours, intervalsDefinedByStreches);
|
||||
assert y.length == 1 + intervalsDefinedByStreches.size();
|
||||
int[] hoursForEachDay = hoursForEachDayUsingSplines(x, y,
|
||||
startInclusive, endExclusive);
|
||||
|
||||
int[] hoursForEachDay = hoursForEachDayUsingSplines(x, y, startInclusive, endExclusive);
|
||||
|
||||
Days daysBetween = Days.daysBetween(startInclusive, endExclusive);
|
||||
assert hoursForEachDay.length == daysBetween.getDays();
|
||||
|
|
@ -84,10 +81,9 @@ public enum StretchesFunctionTypeEnum {
|
|||
// hours for each day specified by the interpolation. The remainder
|
||||
// must be distributed.
|
||||
int[] assignedHours = getAssignedHours(allocation, startInclusive, newEndDate);
|
||||
int[] remindingHours = distributeRemainder(allocation, startInclusive, totalHours, assignedHours);
|
||||
int[] remindingHours = distributeRemainder(totalHours, assignedHours);
|
||||
int[] hoursToAllocate = sum(assignedHours, remindingHours);
|
||||
allocateDaysFrom(allocation, asEffortDuration(hoursToAllocate),
|
||||
startInclusive);
|
||||
allocateDaysFrom(allocation, asEffortDuration(hoursToAllocate), startInclusive);
|
||||
|
||||
assignedHours = getAssignedHours(allocation, startInclusive, newEndDate);
|
||||
Validate.isTrue(sum(assignedHours) == totalHours);
|
||||
|
|
@ -98,132 +94,143 @@ public enum StretchesFunctionTypeEnum {
|
|||
for (int i = 0; i < assignedHours.length; i++) {
|
||||
assignedHours[i] += remindingHours[i];
|
||||
}
|
||||
|
||||
return assignedHours;
|
||||
}
|
||||
|
||||
private int[] getAssignedHours(ResourceAllocation<?> allocation,
|
||||
LocalDate startInclusive, LocalDate endExclusive) {
|
||||
LocalDate startInclusive,
|
||||
LocalDate endExclusive) {
|
||||
|
||||
final Days daysBetween = Days.daysBetween(startInclusive, endExclusive);
|
||||
int[] result = new int[daysBetween.getDays()];
|
||||
|
||||
LocalDate day = new LocalDate(startInclusive); int i = 0;
|
||||
|
||||
while (day.isBefore(endExclusive)) {
|
||||
result[i++] = allocation.getAssignedHours(day, day.plusDays(1));
|
||||
day = day.plusDays(1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void allocateDaysFrom(ResourceAllocation<?> allocation,
|
||||
List<EffortDuration> hoursToAllocate, LocalDate startInclusive) {
|
||||
List<EffortDuration> hoursToAllocate,
|
||||
LocalDate startInclusive) {
|
||||
|
||||
final LocalDate endExclusive = startInclusive.plusDays(hoursToAllocate.size());
|
||||
LOG.debug(String.format("allocate on interval (%s, %s): %s", startInclusive, endExclusive, hoursToAllocate));
|
||||
allocation.withPreviousAssociatedResources().onInterval(
|
||||
startInclusive, endExclusive).allocate(hoursToAllocate);
|
||||
|
||||
LOG.debug(String.format("allocate on interval (%s, %s): %s",
|
||||
startInclusive, endExclusive, hoursToAllocate));
|
||||
|
||||
allocation.withPreviousAssociatedResources()
|
||||
.onInterval(startInclusive, endExclusive).allocate(hoursToAllocate);
|
||||
}
|
||||
|
||||
private List<EffortDuration> asEffortDuration(int[] hoursPerDay) {
|
||||
List<EffortDuration> result = new ArrayList<EffortDuration>();
|
||||
List<EffortDuration> result = new ArrayList<>();
|
||||
|
||||
for (int hours: hoursPerDay) {
|
||||
result.add(EffortDuration.hours(hours));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private int[] distributeRemainder(ResourceAllocation<?> allocation,
|
||||
LocalDate startInclusive, int totalHours,
|
||||
int[] reallyAssigned) {
|
||||
private int[] distributeRemainder(int totalHours, int[] reallyAssigned) {
|
||||
final int remainder = totalHours - sum(reallyAssigned);
|
||||
if (remainder == 0) {
|
||||
|
||||
if ( remainder == 0 ) {
|
||||
return new int[reallyAssigned.length];
|
||||
}
|
||||
|
||||
return distributeRemainder(reallyAssigned, remainder);
|
||||
}
|
||||
|
||||
private int[] distributeRemainder(int[] hoursForEachDay,
|
||||
int remainder) {
|
||||
ProportionalDistributor remainderDistributor = ProportionalDistributor
|
||||
.create(hoursForEachDay);
|
||||
private int[] distributeRemainder(int[] hoursForEachDay, int remainder) {
|
||||
ProportionalDistributor remainderDistributor = ProportionalDistributor.create(hoursForEachDay);
|
||||
|
||||
return remainderDistributor.distribute(remainder);
|
||||
}
|
||||
|
||||
private int sum(int[] array) {
|
||||
int result = 0;
|
||||
|
||||
for (int each : array) {
|
||||
result += each;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private LocalDate lastDayAssignment(ResourceAllocation<?> allocation) {
|
||||
List<DayAssignment> assignments = (List<DayAssignment>) allocation.getAssignments();
|
||||
DayAssignment last = assignments.get(assignments.size() - 1);
|
||||
|
||||
return last.getDay();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static final org.apache.commons.logging.Log LOG = LogFactory
|
||||
.getLog(StretchesFunctionTypeEnum.class);
|
||||
private static final org.apache.commons.logging.Log LOG = LogFactory.getLog(StretchesFunctionTypeEnum.class);
|
||||
|
||||
public static int[] hoursForEachDayUsingSplines(double[] x, double[] y,
|
||||
LocalDate startInclusive, LocalDate endExclusive) {
|
||||
UnivariateRealFunction accumulatingFunction = new SplineInterpolator()
|
||||
.interpolate(x, y);
|
||||
int[] extractAccumulated = extractAccumulated(accumulatingFunction,
|
||||
startInclusive, endExclusive);
|
||||
return extractHoursShouldAssignForEachDay(ValleyFiller
|
||||
.fillValley(extractAccumulated));
|
||||
public static int[] hoursForEachDayUsingSplines(double[] x,
|
||||
double[] y,
|
||||
LocalDate startInclusive,
|
||||
LocalDate endExclusive) {
|
||||
|
||||
UnivariateFunction accumulatingFunction = new SplineInterpolator().interpolate(x, y);
|
||||
int[] extractAccumulated = extractAccumulated(accumulatingFunction, startInclusive, endExclusive);
|
||||
|
||||
return extractHoursShouldAssignForEachDay(ValleyFiller.fillValley(extractAccumulated));
|
||||
}
|
||||
|
||||
private static int[] extractAccumulated(
|
||||
UnivariateRealFunction accumulatedFunction,
|
||||
LocalDate startInclusive, LocalDate endExclusive) {
|
||||
int[] result = new int[Days.daysBetween(startInclusive,
|
||||
endExclusive).getDays()];
|
||||
private static int[] extractAccumulated(UnivariateFunction accumulatedFunction,
|
||||
LocalDate startInclusive,
|
||||
LocalDate endExclusive) {
|
||||
|
||||
int[] result = new int[Days.daysBetween(startInclusive, endExclusive).getDays()];
|
||||
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = evaluate(accumulatedFunction, i + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int[] extractHoursShouldAssignForEachDay(
|
||||
int[] accumulated) {
|
||||
private static int[] extractHoursShouldAssignForEachDay(int[] accumulated) {
|
||||
int[] result = new int[accumulated.length];
|
||||
int previous = 0;
|
||||
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
final int current = accumulated[i];
|
||||
result[i] = current - previous;
|
||||
previous = current;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static int evaluate(UnivariateRealFunction accumulatedFunction,
|
||||
int x) {
|
||||
try {
|
||||
return (int) accumulatedFunction.value(x);
|
||||
} catch (FunctionEvaluationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
private static int evaluate(UnivariateFunction accumulatedFunction, int x) {
|
||||
return (int) accumulatedFunction.value(x);
|
||||
}
|
||||
|
||||
public void applyTo(ResourceAllocation<?> resourceAllocation,
|
||||
StretchesFunction stretchesFunction) {
|
||||
public void applyTo(ResourceAllocation<?> resourceAllocation, StretchesFunction stretchesFunction) {
|
||||
|
||||
List<Interval> intervals = new ArrayList<Interval>();
|
||||
List<Interval> intervals = new ArrayList<>();
|
||||
intervals.addAll(stretchesFunction.getIntervalsDefinedByStreches());
|
||||
|
||||
LocalDate startInclusive = resourceAllocation.getFirstNonConsolidatedDate();
|
||||
LocalDate endExclusive = resourceAllocation.getIntraDayEndDate()
|
||||
.asExclusiveEnd();
|
||||
LocalDate endExclusive = resourceAllocation.getIntraDayEndDate().asExclusiveEnd();
|
||||
int totalHours = resourceAllocation.getNonConsolidatedHours();
|
||||
apply(resourceAllocation, intervals, startInclusive, endExclusive, totalHours);
|
||||
}
|
||||
|
||||
protected abstract void apply(ResourceAllocation<?> allocation,
|
||||
List<Interval> intervalsDefinedByStreches,
|
||||
LocalDate startInclusive, LocalDate endExclusive, int totalHours);
|
||||
List<Interval> intervalsDefinedByStreches,
|
||||
LocalDate startInclusive,
|
||||
LocalDate endExclusive,
|
||||
int totalHours);
|
||||
|
||||
}
|
||||
|
|
|
|||
6
pom.xml
6
pom.xml
|
|
@ -397,9 +397,9 @@
|
|||
|
||||
<!-- Commons Math-->
|
||||
<dependency>
|
||||
<groupId>commons-math</groupId>
|
||||
<artifactId>commons-math</artifactId>
|
||||
<version>1.2</version>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-math3</artifactId>
|
||||
<version>3.6.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Commons Logging (required by many frameworks)-->
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue