namespace MathModEduPr02
{
    internal class Investment
    {
        int banks;
        int investCount;
        double investStep;
        List<List<double>> incomeTable;
        List<List<double>> originalTable;
        List<List<double>> quantityInBanks;
        public double TotalIncome { get => CalcIncome(); }
        public List<double> Distribution { get => GetOptimal(); }
        public Investment(int banks, int investmentsCount, double investmentsStep,
            List<List<double>> incomeTable)
        {
            this.banks = banks;
            investCount = investmentsCount;
            investStep = investmentsStep;
            this.incomeTable = incomeTable;
            originalTable = incomeTable.Select(x => x.ToList()).ToList();
            quantityInBanks = incomeTable.Select(x => Enumerable.Repeat(0, x.Count).Select(x => Convert.ToDouble(x)).ToList()).ToList();
        }
        public void Distribute(int curStep = 1, double curInvestment = 0, List<double> curMaxes = null, List<double> curQuantities = null)
        {
            double curFirstValue = 0;
            if (curStep == banks - 1)
            {
                curMaxes = new List<double>(Enumerable.Repeat(0, banks).Select(Convert.ToDouble).ToList());
                curQuantities = new List<double>(Enumerable.Repeat(0, banks - 1).Select(Convert.ToDouble).ToList());
                curFirstValue = curInvestment;
            }
            if (curInvestment == 0)
            {
                curMaxes = new List<double>();
                curQuantities = new List<double>();
            }
            double curMax = 0;
            double curQuantity = 0;
            for (;curFirstValue <= curInvestment; curFirstValue+=50)
            {
                double newMax = incomeTable[(int)(curFirstValue / investStep)][banks - curStep - 1]
                    + incomeTable[(int)((curInvestment - curFirstValue) / investStep)][banks - curStep];
                if (newMax > curMax)
                {
                    curMax = newMax;
                    curQuantity = curFirstValue;
                }
            }
            curMaxes.Add(curMax);
            curQuantities.Add(curQuantity);
            if (curStep == banks - 1) return;
            if (curInvestment == investStep * (investCount - 1))
            {
                for (int i = 0; i < curMaxes.Count; ++i)
                {
                    incomeTable[i][banks - curStep - 1] = curMaxes[i];
                    quantityInBanks[i][banks - curStep - 1] = curQuantities[i];
                }
                Distribute(curStep + 1, curInvestment = 0);
            }
            else Distribute(curStep, curInvestment + investStep, curMaxes, curQuantities);
        }
        public List<double> GetOptimal()
        {
            List<double> optimal = new List<double>(Enumerable.Repeat(0, banks).Select(Convert.ToDouble).ToList());
            int curInd = investCount - 1;
            for (int i = 0; i < banks - 1; ++i)
            {
                optimal[i] = quantityInBanks[curInd][i];
                if (optimal[i] != 0) --curInd;
            }
            optimal[banks - 1] = (investCount - 1) * investStep - optimal.Sum();
            return optimal;
        }
        public double CalcIncome()
        {
            double income = 0;
            for (int i = 0; i < Distribution.Count; ++i)
                income += originalTable[(int)(Distribution[i] / 50)][i];
            return income;
        }
    }
}
