/*************************************************************************
 *  Clears request fileds (to be sure that we don't forgot to clear something)
 *************************************************************************/
 private static void lmclearrequestfields(ref lmstate state)
 {
     state.needf   = false;
     state.needfg  = false;
     state.needfgh = false;
     state.needfij = false;
 }
        /*************************************************************************
        *   CLASSIC LEVENBERG-MARQUARDT METHOD FOR NON-LINEAR OPTIMIZATION
        *
        *  Optimization using Jacobi matrix. Algorithm  -  classic Levenberg-Marquardt
        *  method.
        *
        *  Function F is represented as sum of squares:
        *
        *   F = f[0]^2(x[0],...,x[n-1]) + ... + f[m-1]^2(x[0],...,x[n-1])
        *
        *  EXAMPLE
        *
        *  See HTML-documentation.
        *
        *  INPUT PARAMETERS:
        *   N       -   dimension, N>1
        *   M       -   number of functions f[i]
        *   X       -   initial solution, array[0..N-1]
        *   EpsF    -   stopping criterion. Algorithm stops if
        |F(k+1)-F(k)| <= EpsF*max{|F(k)|, |F(k+1)|, 1}
        *   EpsX    -   stopping criterion. Algorithm stops if
        |X(k+1)-X(k)| <= EpsX*(1+|X(k)|)
        *   MaxIts  -   stopping criterion. Algorithm stops after MaxIts iterations.
        *               MaxIts=0 means no stopping criterion.
        *
        *  OUTPUT PARAMETERS:
        *   State   -   structure which stores algorithm state between subsequent
        *               calls of MinLMIteration. Used for reverse communication.
        *               This structure should be passed to MinLMIteration subroutine.
        *
        *  See also MinLMIteration, MinLMResults.
        *
        *  NOTE
        *
        *  Passing EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic
        *  stopping criterion selection (small EpsX).
        *
        *  -- ALGLIB --
        *    Copyright 30.03.2009 by Bochkanov Sergey
        *************************************************************************/
        public static void minlmfj(int n,
                                   int m,
                                   ref double[] x,
                                   double epsf,
                                   double epsx,
                                   int maxits,
                                   ref lmstate state)
        {
            int i_ = 0;


            //
            // Prepare RComm
            //
            state.rstate.ia    = new int[3 + 1];
            state.rstate.ba    = new bool[0 + 1];
            state.rstate.ra    = new double[8 + 1];
            state.rstate.stage = -1;

            //
            // prepare internal structures
            //
            lmprepare(n, m, true, ref state);

            //
            // initialize, check parameters
            //
            state.xupdated = false;
            state.n        = n;
            state.m        = m;
            state.epsf     = epsf;
            state.epsx     = epsx;
            state.maxits   = maxits;
            state.flags    = 0;
            if (state.epsf == 0 & state.epsx == 0 & state.maxits == 0)
            {
                state.epsx = 1.0E-6;
            }
            state.usermode    = lmmodefj;
            state.wrongparams = false;
            if (n < 1 | m < 1 | epsf < 0 | epsx < 0 | maxits < 0)
            {
                state.wrongparams = true;
                return;
            }
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                state.x[i_] = x[i_];
            }
        }
        /*************************************************************************
        *  Levenberg-Marquardt algorithm results
        *
        *  Called after MinLMIteration returned False.
        *
        *  Input parameters:
        *   State   -   algorithm state (used by MinLMIteration).
        *
        *  Output parameters:
        *   X       -   array[0..N-1], solution
        *   Rep     -   optimization report:
        * Rep.TerminationType completetion code:
        * -1    incorrect parameters were specified
        *  1    relative function improvement is no more than
        *                           EpsF.
        *  2    relative step is no more than EpsX.
        *  4    gradient norm is no more than EpsG
        *  5    MaxIts steps was taken
        * Rep.IterationsCount contains iterations count
        * Rep.NFunc     - number of function calculations
        * Rep.NJac      - number of Jacobi matrix calculations
        * Rep.NGrad     - number of gradient calculations
        * Rep.NHess     - number of Hessian calculations
        * Rep.NCholesky - number of Cholesky decomposition calculations
        *
        *  -- ALGLIB --
        *    Copyright 10.03.2009 by Bochkanov Sergey
        *************************************************************************/
        public static void minlmresults(ref lmstate state,
                                        ref double[] x,
                                        ref lmreport rep)
        {
            int i_ = 0;

            x = new double[state.n - 1 + 1];
            for (i_ = 0; i_ <= state.n - 1; i_++)
            {
                x[i_] = state.x[i_];
            }
            rep.iterationscount = state.repiterationscount;
            rep.terminationtype = state.repterminationtype;
            rep.nfunc           = state.repnfunc;
            rep.njac            = state.repnjac;
            rep.ngrad           = state.repngrad;
            rep.nhess           = state.repnhess;
            rep.ncholesky       = state.repncholesky;
        }
 /*************************************************************************
 *  Prepare internal structures (except for RComm).
 *
 *  Note: M must be zero for FGH mode, non-zero for FJ/FGJ mode.
 *************************************************************************/
 private static void lmprepare(int n,
                               int m,
                               bool havegrad,
                               ref lmstate state)
 {
     state.repiterationscount = 0;
     state.repterminationtype = 0;
     state.repnfunc           = 0;
     state.repnjac            = 0;
     state.repngrad           = 0;
     state.repnhess           = 0;
     state.repncholesky       = 0;
     if (n < 0 | m < 0)
     {
         return;
     }
     if (havegrad)
     {
         state.g = new double[n - 1 + 1];
     }
     if (m != 0)
     {
         state.j  = new double[m - 1 + 1, n - 1 + 1];
         state.fi = new double[m - 1 + 1];
         state.h  = new double[0 + 1, 0 + 1];
     }
     else
     {
         state.j  = new double[0 + 1, 0 + 1];
         state.fi = new double[0 + 1];
         state.h  = new double[n - 1 + 1, n - 1 + 1];
     }
     state.x        = new double[n - 1 + 1];
     state.rawmodel = new double[n - 1 + 1, n - 1 + 1];
     state.model    = new double[n - 1 + 1, n - 1 + 1];
     state.xbase    = new double[n - 1 + 1];
     state.xprec    = new double[n - 1 + 1];
     state.gbase    = new double[n - 1 + 1];
     state.xdir     = new double[n - 1 + 1];
     state.work     = new double[Math.Max(n, m) + 1];
 }
        /*************************************************************************
        *  One Levenberg-Marquardt iteration.
        *
        *  Called after inialization of State structure with MinLMXXX subroutine.
        *  See HTML docs for examples.
        *
        *  Input parameters:
        *   State   -   structure which stores algorithm state between subsequent
        *               calls and which is used for reverse communication. Must be
        *               initialized with MinLMXXX call first.
        *
        *  If subroutine returned False, iterative algorithm has converged.
        *
        *  If subroutine returned True, then:
        * if State.NeedF=True,      -   function value F at State.X[0..N-1]
        *                               is required
        * if State.NeedFG=True      -   function value F and gradient G
        *                               are required
        * if State.NeedFiJ=True     -   function vector f[i] and Jacobi matrix J
        *                               are required
        * if State.NeedFGH=True     -   function value F, gradient G and Hesian H
        *                               are required
        *
        *  One and only one of this fields can be set at time.
        *
        *  Results are stored:
        * function value            -   in LMState.F
        * gradient                  -   in LMState.G[0..N-1]
        * Jacobi matrix             -   in LMState.J[0..M-1,0..N-1]
        * Hessian                   -   in LMState.H[0..N-1,0..N-1]
        *
        *  -- ALGLIB --
        *    Copyright 10.03.2009 by Bochkanov Sergey
        *************************************************************************/
        public static bool minlmiteration(ref lmstate state)
        {
            bool   result     = new bool();
            int    n          = 0;
            int    m          = 0;
            int    i          = 0;
            double xnorm      = 0;
            double stepnorm   = 0;
            bool   spd        = new bool();
            double fbase      = 0;
            double fnew       = 0;
            double lambda     = 0;
            double nu         = 0;
            double lambdaup   = 0;
            double lambdadown = 0;
            int    lbfgsflags = 0;
            double v          = 0;
            int    i_         = 0;


            //
            // Reverse communication preparations
            // I know it looks ugly, but it works the same way
            // anywhere from C++ to Python.
            //
            // This code initializes locals by:
            // * random values determined during code
            //   generation - on first subroutine call
            // * values from previous call - on subsequent calls
            //
            if (state.rstate.stage >= 0)
            {
                n          = state.rstate.ia[0];
                m          = state.rstate.ia[1];
                i          = state.rstate.ia[2];
                lbfgsflags = state.rstate.ia[3];
                spd        = state.rstate.ba[0];
                xnorm      = state.rstate.ra[0];
                stepnorm   = state.rstate.ra[1];
                fbase      = state.rstate.ra[2];
                fnew       = state.rstate.ra[3];
                lambda     = state.rstate.ra[4];
                nu         = state.rstate.ra[5];
                lambdaup   = state.rstate.ra[6];
                lambdadown = state.rstate.ra[7];
                v          = state.rstate.ra[8];
            }
            else
            {
                n          = -983;
                m          = -989;
                i          = -834;
                lbfgsflags = 900;
                spd        = true;
                xnorm      = 364;
                stepnorm   = 214;
                fbase      = -338;
                fnew       = -686;
                lambda     = 912;
                nu         = 585;
                lambdaup   = 497;
                lambdadown = -271;
                v          = -581;
            }
            if (state.rstate.stage == 0)
            {
                goto lbl_0;
            }
            if (state.rstate.stage == 1)
            {
                goto lbl_1;
            }
            if (state.rstate.stage == 2)
            {
                goto lbl_2;
            }
            if (state.rstate.stage == 3)
            {
                goto lbl_3;
            }
            if (state.rstate.stage == 4)
            {
                goto lbl_4;
            }
            if (state.rstate.stage == 5)
            {
                goto lbl_5;
            }
            if (state.rstate.stage == 6)
            {
                goto lbl_6;
            }

            //
            // Routine body
            //
            System.Diagnostics.Debug.Assert(state.usermode == lmmodefj | state.usermode == lmmodefgj | state.usermode == lmmodefgh, "LM: internal error");
            if (state.wrongparams)
            {
                state.repterminationtype = -1;
                result = false;
                return(result);
            }

            //
            // prepare params
            //
            n          = state.n;
            m          = state.m;
            lambdaup   = 10;
            lambdadown = 0.3;
            nu         = 2;
            lbfgsflags = 0;

            //
            // if we have F and G
            //
            if (!((state.usermode == lmmodefgj | state.usermode == lmmodefgh) & state.flags / lmflagnoprelbfgs % 2 == 0))
            {
                goto lbl_7;
            }

            //
            // First stage of the hybrid algorithm: LBFGS
            //
            lbfgs.minlbfgs(n, Math.Min(n, lmprelbfgsm), ref state.x, 0.0, 0.0, 0.0, Math.Max(25, n), 0, ref state.internalstate);
lbl_9:
            if (!lbfgs.minlbfgsiteration(ref state.internalstate))
            {
                goto lbl_10;
            }

            //
            // RComm
            //
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                state.x[i_] = state.internalstate.x[i_];
            }
            lmclearrequestfields(ref state);
            state.needfg       = true;
            state.rstate.stage = 0;
            goto lbl_rcomm;
lbl_0:
            state.repnfunc = state.repnfunc + 1;
            state.repngrad = state.repngrad + 1;

            //
            // Call LBFGS
            //
            state.internalstate.f = state.f;
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                state.internalstate.g[i_] = state.g[i_];
            }
            goto lbl_9;
lbl_10:
            lbfgs.minlbfgsresults(ref state.internalstate, ref state.x, ref state.internalrep);
lbl_7:

            //
            // Second stage of the hybrid algorithm: LM
            // Initialize quadratic model.
            //
            if (state.usermode != lmmodefgh)
            {
                goto lbl_11;
            }

            //
            // RComm
            //
            lmclearrequestfields(ref state);
            state.needfgh      = true;
            state.rstate.stage = 1;
            goto lbl_rcomm;
lbl_1:
            state.repnfunc = state.repnfunc + 1;
            state.repngrad = state.repngrad + 1;
            state.repnhess = state.repnhess + 1;

            //
            // generate raw quadratic model
            //
            for (i = 0; i <= n - 1; i++)
            {
                for (i_ = 0; i_ <= n - 1; i_++)
                {
                    state.rawmodel[i, i_] = state.h[i, i_];
                }
            }
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                state.gbase[i_] = state.g[i_];
            }
            fbase = state.f;
lbl_11:
            if (!(state.usermode == lmmodefgj | state.usermode == lmmodefj))
            {
                goto lbl_13;
            }

            //
            // RComm
            //
            lmclearrequestfields(ref state);
            state.needfij      = true;
            state.rstate.stage = 2;
            goto lbl_rcomm;
lbl_2:
            state.repnfunc = state.repnfunc + 1;
            state.repnjac  = state.repnjac + 1;

            //
            // generate raw quadratic model
            //
            blas.matrixmatrixmultiply(ref state.j, 0, m - 1, 0, n - 1, true, ref state.j, 0, m - 1, 0, n - 1, false, 1.0, ref state.rawmodel, 0, n - 1, 0, n - 1, 0.0, ref state.work);
            blas.matrixvectormultiply(ref state.j, 0, m - 1, 0, n - 1, true, ref state.fi, 0, m - 1, 1.0, ref state.gbase, 0, n - 1, 0.0);
            fbase = 0.0;
            for (i_ = 0; i_ <= m - 1; i_++)
            {
                fbase += state.fi[i_] * state.fi[i_];
            }
lbl_13:
            lambda = 0.001;
lbl_15:
            if (false)
            {
                goto lbl_16;
            }

            //
            // 1. Model = RawModel+lambda*I
            // 2. Try to solve (RawModel+Lambda*I)*dx = -g.
            //    Increase lambda if left part is not positive definite.
            //
            for (i = 0; i <= n - 1; i++)
            {
                for (i_ = 0; i_ <= n - 1; i_++)
                {
                    state.model[i, i_] = state.rawmodel[i, i_];
                }
                state.model[i, i] = state.model[i, i] + lambda;
            }
            spd = cholesky.spdmatrixcholesky(ref state.model, n, true);
            state.repncholesky = state.repncholesky + 1;
            if (!spd)
            {
                lambda = lambda * lambdaup * nu;
                nu     = nu * 2;
                goto lbl_15;
            }
            if (!spdsolve.spdmatrixcholeskysolve(ref state.model, state.gbase, n, true, ref state.xdir))
            {
                lambda = lambda * lambdaup * nu;
                nu     = nu * 2;
                goto lbl_15;
            }
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                state.xdir[i_] = -1 * state.xdir[i_];
            }

            //
            // Candidate lambda found.
            // 1. Save old w in WBase
            // 1. Test some stopping criterions
            // 2. If error(w+wdir)>error(w), increase lambda
            //
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                state.xbase[i_] = state.x[i_];
            }
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                state.x[i_] = state.x[i_] + state.xdir[i_];
            }
            xnorm = 0.0;
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                xnorm += state.xbase[i_] * state.xbase[i_];
            }
            stepnorm = 0.0;
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                stepnorm += state.xdir[i_] * state.xdir[i_];
            }
            xnorm    = Math.Sqrt(xnorm);
            stepnorm = Math.Sqrt(stepnorm);
            if (stepnorm <= state.epsx * (1 + xnorm))
            {
                //
                // step size if small enough
                //
                state.repterminationtype = 2;
                goto lbl_16;
            }
            lmclearrequestfields(ref state);
            state.needf        = true;
            state.rstate.stage = 3;
            goto lbl_rcomm;
lbl_3:
            state.repnfunc = state.repnfunc + 1;
            fnew           = state.f;
            if (Math.Abs(fnew - fbase) <= state.epsf * Math.Max(1, Math.Max(Math.Abs(fbase), Math.Abs(fnew))))
            {
                //
                // function change is small enough
                //
                state.repterminationtype = 1;
                goto lbl_16;
            }
            if (fnew > fbase)
            {
                //
                // restore state and continue out search for lambda
                //
                for (i_ = 0; i_ <= n - 1; i_++)
                {
                    state.x[i_] = state.xbase[i_];
                }
                lambda = lambda * lambdaup * nu;
                nu     = nu * 2;
                goto lbl_15;
            }
            if (!((state.usermode == lmmodefgj | state.usermode == lmmodefgh) & state.flags / lmflagnointlbfgs % 2 == 0))
            {
                goto lbl_17;
            }
            System.Diagnostics.Debug.Assert(state.usermode == lmmodefgh);

            //
            // Optimize using inv(cholesky(H)) as preconditioner
            //
            if (!trinverse.rmatrixtrinverse(ref state.model, n, true, false))
            {
                goto lbl_19;
            }

            //
            // if matrix can be inverted use it.
            // just silently move to next iteration otherwise.
            // (will be very, very rare, mostly for specially
            // designed near-degenerate tasks)
            //
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                state.xbase[i_] = state.x[i_];
            }
            for (i = 0; i <= n - 1; i++)
            {
                state.xprec[i] = 0;
            }
            lbfgs.minlbfgs(n, Math.Min(n, lmintlbfgsits), ref state.xprec, 0.0, 0.0, 0.0, lmintlbfgsits, lbfgsflags, ref state.internalstate);
lbl_21:
            if (!lbfgs.minlbfgsiteration(ref state.internalstate))
            {
                goto lbl_22;
            }

            //
            // convert XPrec to unpreconditioned form, then call RComm.
            //
            for (i = 0; i <= n - 1; i++)
            {
                v = 0.0;
                for (i_ = i; i_ <= n - 1; i_++)
                {
                    v += state.internalstate.x[i_] * state.model[i, i_];
                }
                state.x[i] = state.xbase[i] + v;
            }
            lmclearrequestfields(ref state);
            state.needfg       = true;
            state.rstate.stage = 4;
            goto lbl_rcomm;
lbl_4:
            state.repnfunc = state.repnfunc + 1;
            state.repngrad = state.repngrad + 1;

            //
            // 1. pass State.F to State.InternalState.F
            // 2. convert gradient back to preconditioned form
            //
            state.internalstate.f = state.f;
            for (i = 0; i <= n - 1; i++)
            {
                state.internalstate.g[i] = 0;
            }
            for (i = 0; i <= n - 1; i++)
            {
                v = state.g[i];
                for (i_ = i; i_ <= n - 1; i_++)
                {
                    state.internalstate.g[i_] = state.internalstate.g[i_] + v * state.model[i, i_];
                }
            }

            //
            // next iteration
            //
            goto lbl_21;
lbl_22:

            //
            // change LBFGS flags to NoRealloc.
            // L-BFGS subroutine will use memory allocated from previous run.
            // it is possible since all subsequent calls will be with same N/M.
            //
            lbfgsflags = lbfgsnorealloc;

            //
            // back to unpreconditioned X
            //
            lbfgs.minlbfgsresults(ref state.internalstate, ref state.xprec, ref state.internalrep);
            for (i = 0; i <= n - 1; i++)
            {
                v = 0.0;
                for (i_ = i; i_ <= n - 1; i_++)
                {
                    v += state.xprec[i_] * state.model[i, i_];
                }
                state.x[i] = state.xbase[i] + v;
            }
lbl_19:
lbl_17:

            //
            // Accept new position.
            // Calculate Hessian
            //
            if (state.usermode != lmmodefgh)
            {
                goto lbl_23;
            }

            //
            // RComm
            //
            lmclearrequestfields(ref state);
            state.needfgh      = true;
            state.rstate.stage = 5;
            goto lbl_rcomm;
lbl_5:
            state.repnfunc = state.repnfunc + 1;
            state.repngrad = state.repngrad + 1;
            state.repnhess = state.repnhess + 1;

            //
            // Update raw quadratic model
            //
            for (i = 0; i <= n - 1; i++)
            {
                for (i_ = 0; i_ <= n - 1; i_++)
                {
                    state.rawmodel[i, i_] = state.h[i, i_];
                }
            }
            for (i_ = 0; i_ <= n - 1; i_++)
            {
                state.gbase[i_] = state.g[i_];
            }
            fbase = state.f;
lbl_23:
            if (!(state.usermode == lmmodefgj | state.usermode == lmmodefj))
            {
                goto lbl_25;
            }

            //
            // RComm
            //
            lmclearrequestfields(ref state);
            state.needfij      = true;
            state.rstate.stage = 6;
            goto lbl_rcomm;
lbl_6:
            state.repnfunc = state.repnfunc + 1;
            state.repnjac  = state.repnjac + 1;

            //
            // generate raw quadratic model
            //
            blas.matrixmatrixmultiply(ref state.j, 0, m - 1, 0, n - 1, true, ref state.j, 0, m - 1, 0, n - 1, false, 1.0, ref state.rawmodel, 0, n - 1, 0, n - 1, 0.0, ref state.work);
            blas.matrixvectormultiply(ref state.j, 0, m - 1, 0, n - 1, true, ref state.fi, 0, m - 1, 1.0, ref state.gbase, 0, n - 1, 0.0);
            fbase = 0.0;
            for (i_ = 0; i_ <= m - 1; i_++)
            {
                fbase += state.fi[i_] * state.fi[i_];
            }
lbl_25:
            state.repiterationscount = state.repiterationscount + 1;
            if (state.repiterationscount >= state.maxits & state.maxits > 0)
            {
                state.repterminationtype = 5;
                goto lbl_16;
            }

            //
            // Update lambda
            //
            lambda = lambda * lambdadown;
            nu     = 2;
            goto lbl_15;
lbl_16:
            result = false;
            return(result);

            //
            // Saving state
            //
lbl_rcomm:
            result             = true;
            state.rstate.ia[0] = n;
            state.rstate.ia[1] = m;
            state.rstate.ia[2] = i;
            state.rstate.ia[3] = lbfgsflags;
            state.rstate.ba[0] = spd;
            state.rstate.ra[0] = xnorm;
            state.rstate.ra[1] = stepnorm;
            state.rstate.ra[2] = fbase;
            state.rstate.ra[3] = fnew;
            state.rstate.ra[4] = lambda;
            state.rstate.ra[5] = nu;
            state.rstate.ra[6] = lambdaup;
            state.rstate.ra[7] = lambdadown;
            state.rstate.ra[8] = v;
            return(result);
        }