using StatisticalDistribution;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DiscreteChoiceModel.MDMHelpers
{
    class Lambda
    {
        public static double get(MDM mdm, double[] V)
        {
            switch (mdm.getUtiityFunctionType())
            {
                case MDM.UtilityFunctionType.Additive: return bisectionAdd(V, mdm.getMarginalDistributions(), mdm.getEpsilon(), mdm.getLambdaL(), mdm.getLambdaH());
                case MDM.UtilityFunctionType.Multiplicative: return bisectionMul(V, mdm.getMarginalDistributions(), mdm.getEpsilon(), mdm.getLambdaL(), mdm.getLambdaH());
            }
            throw new NotImplementedException();
        }


        // Bisection
        private static double bisectionAdd(double[] V, Distribution[] mds, double epsilon, double lambdaL, double lambdaH)
        {
            int iterator = 0;
            double lambda = (lambdaL + lambdaH) / 2.0;
            double probSum = getProbSumAdditive(lambda, mds, V);
            while (Math.Abs(1.0 - probSum) > epsilon)
            {
                iterator += 1;
                if (iterator > 1000) { throw new NotImplementedException(); }
                if (probSum > 1.0) { lambdaL = lambda; }
                else { lambdaH = lambda; }
                lambda = (lambdaL + lambdaH) / 2.0;
                probSum = getProbSumAdditive(lambda, mds, V);
            }
            return lambda;
        }
        private static double bisectionMul(double[] V, Distribution[] mds, double epsilon, double lambdaL, double lambdaH)
        {
            int iterator = 0;
            double lambda = (lambdaL + lambdaH) / 2.0;
            double probSum = getProbSumMultiplicative(lambda, mds, V);
            while (Math.Abs(1.0 - probSum) > epsilon)
            {
                iterator += 1;
                if (iterator > 1000) { throw new NotImplementedException(); }
                if (probSum > 1.0) { lambdaL = lambda; }
                else { lambdaH = lambda; }
                lambda = (lambdaL + lambdaH) / 2.0;
                probSum = getProbSumMultiplicative(lambda, mds, V);
            }
            return lambda;
        }

        // Prob Sum
        private static double getProbSumAdditive(double lambda, Distribution[] mds, double[] V)
        {
            double probSum = 0.0;
            for (int k = 0; k < V.Length; k++) { probSum += 1.0 - mds[k].cdf(lambda - V[k]); }
            return probSum;
        }
        private static double getProbSumMultiplicative(double lambda, Distribution[] mds, double[] V)
        {
            double probSum = 0.0;
            for (int k = 0; k < V.Length; k++) { probSum += mds[k].cdf(lambda / V[k]); }
            return probSum;
        }

    }
}
