package inventory;

/**
 * Created on 11.03.2016 3:48:06
 * @author Alexander Mikhailovich Kovshov
 */
public class InventoryModel2 {


    static int n = 3;                                               //число промежутков времени (месяцев)
    static int max = 4;                                             //наибольшее значение для остатка на складе
    static double[] inprice =  {1, 2, 5};                           //цены закупочные за каждый месяц              
    static double[] outprice = {3, 2, 5};                           //цены отпускные за каждый месяц            
    static double[] store =    {1, 1, 1};                           //стоимость хранения единицы товара за каждый месяц
    static int[] need =        {2, 2, 2};                           //потребность за каждый месяц
    static int[] add = new int[n];                                  //приобретение в каждом месяце

    static double[][][] profit = new double[n][max + 1][];          //Прибыль за каждый месяц и все последующие за ним месяцы в зависимости от остатка на начало месяца.
    static int[][][] restFromProfit = new int[n][max + 1][];        //Наилучший остаток на конец каждого месяца в зависимости от остатка на начало каждого месяца и продаж.
    static int[][][] output = new int[n][max + 1][];                //Наилучшие продажи в следующем месяце в зависимости от остатка на начало каждого месяца и продаж.

    public static void main(String[] args) {
        //Вычисление возможной прибыли за конечный (n-й) месяц в зависимости от остатка на складе.
        //r --- возможный остаток на складе от предыдущего месяца
        for (int r = 0; r <= max; r++) {
            profit[n - 1][r] = new double[need[n - 1] + 1];
            restFromProfit[n - 1][r] = new int[need[n - 1] + 1];
            output[n - 1][r] = new int[need[n - 1] + 1];
            for (int ni = 0; ni <= need[n - 1]; ni++) {

                int add_r = ni - r; //Приобретение за конечный (n-й) месяц.
                if (add_r < 0) {
                    profit[n - 1][r][ni] = Integer.MIN_VALUE;
                    continue;
                }
                profit[n - 1][r][ni]
                        = outprice[n - 1] * ni
                        - inprice[n - 1] * add_r
                        - store[n - 1] * ((double) r / 2);

            }
        }
        for (int m = n - 1; m > 0; m--) {

            //Вычисление возможной прибыли за m-й месяц в зависимости от остатка на складе.
            //r  --- возможный остаток на складе на начало месяца. 
            //rr --- возможный остаток на складе на конец месяца.
            for (int r = 0; r <= max; r++) {
                    profit[m - 1][r] = new double[need[m - 1] + 1];
                    restFromProfit[m - 1][r] = new int[need[m - 1] + 1];
                    output[m - 1][r] = new int[need[m - 1] + 1];
                for (int ni = 0; ni <= need[m - 1]; ni++) {
                    for (int rr = 0; rr <= max; rr++) {
                        //Прибыль за m-й и последующие месяцы в зависимости от остатка на конец месяца
                        //и объёмах продаж в следующем месяце.
                        double profitTemp;
                        //Приобретение за m-й месяц
                        int add_r = ni + rr - r;
                        if (add_r < 0) {
                            profitTemp = Integer.MIN_VALUE;
                        } else {
                            
                        //i --- возможный сбыт за m-й месяц. 
                        for (int i = 0; i < profit[m][rr].length; i++) {
                                profitTemp
                                        = outprice[m - 1] * ni
                                        - inprice[m - 1] * add_r
                                        - store[m - 1] * ((double) (r + rr) / 2) + profit[m][rr][i];

                                //Если данный остаток на конец месяца даёт наибольшую прибыль
                                //за этот и последующие месяцы при данном остатке на начало месяца,
                                //то значение прибыли и конечный остаток сохраняются.
                                if (profitTemp > profit[m - 1][r][ni]) {
                                    profit[m - 1][r][ni] = profitTemp;
                                    restFromProfit[m - 1][r][ni] = rr;
                                    output[m - 1][r][ni] = i;
                                }
                            }
                        }
                    }

                }
            }
        }
        int output0 = -1; //Продажи за первый месяц.
        //Выбор наилучшего объёма продаж за первый месяц.
        double profitTemp = 0;
        for (int ni = 0; ni <= need[0]; ni++) {

            if(profit[0][0][ni] > profitTemp) {
                profitTemp = profit[0][0][ni];
                output0 = ni;
            }
        }

        System.out.println("=========== План закупок ===============");
        for (int m = 0, r = 0, o = output0; m < n; m++) {

            System.out.println((m + 1)
                    + "; Остаток на начало: " + r
                    + "; Остаток на конец: " + restFromProfit[m][r][o]
                    + "; Потребление: " + o
                    + "; Закупки: " + (o - r + restFromProfit[m][r][o])
            );
            r = restFromProfit[m][r][o];
            o = output[m][r][o];
        }

    }
}