org.libreplan.business.common.ProportionalDistributor.java Source code

Java tutorial

Introduction

Here is the source code for org.libreplan.business.common.ProportionalDistributor.java

Source

/*
 * This file is part of LibrePlan
 *
 * Copyright (C) 2009-2010 Fundacin para o Fomento da Calidade Industrial e
 *                         Desenvolvemento Tecnolxico de Galicia
 * Copyright (C) 2010-2011 Igalia, S.L.
 *
 * 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 <http://www.gnu.org/licenses/>.
 */
package org.libreplan.business.common;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.math3.fraction.Fraction;

/**
 * @author scar Gonzlez Fernndez <ogonzalez@igalia.com>
 */
public class ProportionalDistributor {

    public static ProportionalDistributor create(int... 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);
        }

        return result;
    }

    /**
     * Note: this class has a natural ordering that is inconsistent with equals.
     *
     */
    private static class FractionWithPosition implements Comparable<FractionWithPosition> {

        public static List<FractionWithPosition> transform(Fraction[] fractions) {
            List<FractionWithPosition> result = new ArrayList<>();

            for (int i = 0; i < fractions.length; i++) {
                result.add(new FractionWithPosition(i, fractions[i]));
            }

            return result;
        }

        final int position;
        final Fraction fraction;

        FractionWithPosition(int position, Fraction fraction) {
            this.position = position;
            this.fraction = fraction;
        }

        @Override
        public int compareTo(FractionWithPosition other) {
            return fraction.compareTo(other.fraction);
        }

    }

    private final Fraction[] fractions;

    private ProportionalDistributor(Fraction[] fractions) {
        this.fractions = fractions;
    }

    public int[] distribute(final int total) {
        if (fractions.length == 0) {
            return new int[0];
        }

        int[] result = new int[fractions.length];
        int remaining = total - assignIntegerParts(total, result);

        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) {
                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));
        Collections.sort(transform, Collections.reverseOrder());

        for (int i = 0; i < remaining; i++) {
            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;
    }

}