diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/ProportionalDistributor.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/ProportionalDistributor.java
new file mode 100644
index 000000000..fce5eabb3
--- /dev/null
+++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/ProportionalDistributor.java
@@ -0,0 +1,132 @@
+/*
+ * This file is part of ###PROJECT_NAME###
+ *
+ * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
+ * Desenvolvemento Tecnolóxico de Galicia
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.navalplanner.business.common;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Óscar González Fernández
+ */
+public class ProportionalDistributor {
+
+ public static ProportionalDistributor create(int... initialShares) {
+ return new ProportionalDistributor(toProportions(
+ sumIntegerParts(initialShares), initialShares));
+ }
+
+ private static int sumIntegerParts(int[] numbers) {
+ int sum = 0;
+ for (Number each : numbers) {
+ sum += each.intValue();
+ }
+ return sum;
+ }
+
+ private static BigDecimal[] toProportions(int initialTotal, int... shares) {
+ BigDecimal total = new BigDecimal(initialTotal);
+ BigDecimal[] result = new BigDecimal[shares.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = new BigDecimal(shares[i]).divide(total, 4,
+ RoundingMode.DOWN);
+ }
+ return result;
+ }
+
+ private static class ProportionWithPosition implements
+ Comparable {
+
+ public static List transform(
+ BigDecimal[] proportions) {
+ List result = new ArrayList();
+ for (int i = 0; i < proportions.length; i++) {
+ result.add(new ProportionWithPosition(i, proportions[i]));
+ }
+ return result;
+ }
+
+ final int position;
+ final BigDecimal proportion;
+
+ ProportionWithPosition(int position, BigDecimal proportion) {
+ this.position = position;
+ this.proportion = proportion;
+ }
+
+ @Override
+ public int compareTo(ProportionWithPosition other) {
+ return proportion.compareTo(other.proportion);
+ }
+
+ }
+
+ private final BigDecimal[] proportions;
+
+ private ProportionalDistributor(BigDecimal[] proportions) {
+ this.proportions = proportions;
+ }
+
+ public int[] distribute(final int total) {
+ int[] result = new int[proportions.length];
+ int remaining = total - assignIntegerParts(total, result);
+ if (remaining == 0) {
+ return result;
+ }
+ BigDecimal[] currentProportions = toProportions(total, result);
+ assignRemaining(result, currentProportions, remaining);
+ return result;
+ }
+
+ private int assignIntegerParts(int current, int[] result) {
+ int substract = 0;
+ for (int i = 0; i < proportions.length; i++) {
+ int intValue = proportions[i].multiply(new BigDecimal(current))
+ .intValue();
+ if (intValue > 0) {
+ result[i] = result[i] + intValue;
+ substract += intValue;
+ }
+ }
+ return substract;
+ }
+
+ private void assignRemaining(int[] result, BigDecimal[] currentProportions,
+ int remaining) {
+ List transform = ProportionWithPosition
+ .transform(difference(currentProportions));
+ Collections.sort(transform, Collections.reverseOrder());
+ for (int i = 0; i < remaining; i++) {
+ ProportionWithPosition proportionWithPosition = transform.get(i);
+ result[proportionWithPosition.position] = result[proportionWithPosition.position] + 1;
+ }
+ }
+
+ private BigDecimal[] difference(BigDecimal[] pr) {
+ BigDecimal[] result = new BigDecimal[proportions.length];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = proportions[i].subtract(pr[i]);
+ }
+ return result;
+ }
+
+}
diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/common/ProportionalDistributorTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/common/ProportionalDistributorTest.java
new file mode 100644
index 000000000..f8be386fe
--- /dev/null
+++ b/navalplanner-business/src/test/java/org/navalplanner/business/common/ProportionalDistributorTest.java
@@ -0,0 +1,129 @@
+/*
+ * This file is part of ###PROJECT_NAME###
+ *
+ * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e
+ * Desenvolvemento Tecnolóxico de Galicia
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+package org.navalplanner.business.common;
+
+import static org.junit.Assert.assertThat;
+
+import java.util.Arrays;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Test;
+
+/**
+ * @author Óscar González Fernández
+ *
+ */
+public class ProportionalDistributorTest {
+
+ @Test
+ public void mustGiveTheSameDistributionForSameTotal() {
+ ProportionalDistributor distributor = ProportionalDistributor.create(
+ 100, 200);
+ assertThat(distributor.distribute(300), equalToDistribution(100, 200));
+ }
+
+ @Test
+ public void exactDivisionsWorkOk() {
+ ProportionalDistributor distributor = ProportionalDistributor.create(
+ 100, 100, 100);
+ assertThat(distributor.distribute(600), equalToDistribution(200, 200,
+ 200));
+ }
+
+ @Test
+ public void distributingZeroGivesZeroShares() {
+ ProportionalDistributor distributor = ProportionalDistributor.create(
+ 100, 100, 100);
+ assertThat(distributor.distribute(0), equalToDistribution(0, 0, 0));
+ }
+
+ @Test
+ public void ifOneOfTheProportionsIsZeroAlwaysGivesZeros() {
+ ProportionalDistributor distributor = ProportionalDistributor.create(
+ 100, 100, 0);
+ assertThat(distributor.distribute(100), equalToDistribution(50, 50, 0));
+ }
+
+ @Test
+ public void disputedPartGoesToFirstIfEqualWeight() {
+ ProportionalDistributor distributor = ProportionalDistributor.create(
+ 10, 10, 10);
+ assertThat(distributor.distribute(10), equalToDistribution(4, 3, 3));
+ }
+
+ @Test
+ public void distributionIsKept() {
+ ProportionalDistributor distributor = ProportionalDistributor.create(2,
+ 3, 5);
+ assertThat(distributor.distribute(1), equalToDistribution(0, 0, 1));
+ assertThat(distributor.distribute(2), equalToDistribution(0, 1, 1));
+ assertThat(distributor.distribute(3), equalToDistribution(1, 1, 1));
+ assertThat(distributor.distribute(4), equalToDistribution(1, 1, 2));
+ assertThat(distributor.distribute(5), equalToDistribution(1, 2, 2));
+ assertThat(distributor.distribute(6), equalToDistribution(1, 2, 3));
+ assertThat(distributor.distribute(10), equalToDistribution(2, 3, 5));
+ assertThat(distributor.distribute(7), equalToDistribution(1, 2, 4));
+ }
+
+ @Test
+ public void addingOneEachTime() {
+ ProportionalDistributor distributor = ProportionalDistributor.create(
+ 99, 101, 800);
+ assertThat(distributor.distribute(1), equalToDistribution(0, 0, 1));
+ assertThat(distributor.distribute(3), equalToDistribution(0, 0, 3));
+ assertThat(distributor.distribute(6), equalToDistribution(0, 1, 5));
+ assertThat(distributor.distribute(7), equalToDistribution(1, 1, 5));
+ assertThat(distributor.distribute(8), equalToDistribution(1, 1, 6));
+ assertThat(distributor.distribute(9), equalToDistribution(1, 1, 7));
+ assertThat(distributor.distribute(10), equalToDistribution(1, 1, 8));
+ assertThat(distributor.distribute(11), equalToDistribution(1, 1, 9));
+ assertThat(distributor.distribute(12), equalToDistribution(1, 1, 10));
+ assertThat(distributor.distribute(13), equalToDistribution(1, 1, 11));
+ assertThat(distributor.distribute(14), equalToDistribution(1, 2, 11));
+ assertThat(distributor.distribute(15), equalToDistribution(1, 2, 12));
+ assertThat(distributor.distribute(16), equalToDistribution(1, 2, 13));
+ assertThat(distributor.distribute(17), equalToDistribution(2, 2, 13));
+ assertThat(distributor.distribute(20), equalToDistribution(2, 2, 16));
+ }
+
+ private static Matcher equalToDistribution(final int... distribution) {
+ return new BaseMatcher() {
+
+ @Override
+ public boolean matches(Object object) {
+ if (object instanceof int[]) {
+ int[] arg = (int[]) object;
+ return Arrays.equals(arg, distribution);
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("must equal "
+ + Arrays.toString(distribution));
+ }
+ };
+ }
+
+
+}