Exemplo n.º 1
0
 public LModeExt(LMode mode, double remoteLeft, double remoteRight) : base(mode.X, mode.H, mode.Found)
 {
     this.Remote = Tools.Combine(remoteLeft, remoteRight);
 }
Exemplo n.º 2
0
        } //# end of max.fastTrack

        internal LModeExt GetPoints()
        {
            //# Arguments:
            //# ----------------------------------------------
            //# o: same nature as o in dens.gen.icdf arguments
            //# start: EITHER a vector of length 2 [two potential starting points]
            //#        or a scalar
            //# f.ratio.remote: scalar
            //# epsilon: scalar
            //# range: vector of length 2
            //# --------------------------------------------------------------------------
            //# If range is finite and log.f not inestimable at lower end,
            //# then we first look for values at both ends before searching for a mode

            //mode = list(x = numeric(0), h = numeric(0), found = false) # modif_0.11
            LMode mode = new LMode();

            if ((this.Range[0].IsFinite() && this.Range[1].IsFinite() && !this.InestimableLowerLimit))
            {
                double[] logFPrime = new double[] { this.O.LogFPrime(this.Range[0], A), this.O.LogFPrime(this.Range[1], A) };
                //# If slope (f') is the same at both ends, then there is no mode between the 2 endpoints (assuming a unimodal distrn)
                //# and we then assume that the highest end value for log.f is hence the local mode
                double[] logFPrimeSign = new double[] { Math.Sign(logFPrime[0]), Math.Sign(logFPrime[1]) };
                if (logFPrime.Prod() > 0)
                {
                    double[] logF = new double[] { this.O.LogF(this.Range[0], A), this.O.LogF(this.Range[1], A) };
                    int      w    = (logF[0] >= logF[1]) ? 0 : 1;
                    mode = new LMode(w == 0 ? this.Range[0] : this.Range[1], logF[w], true);
                }
            } // if ((this.Range[0]

            SecuredNRSearch nr = null;
            double          startingPoint;

            if (!mode.Found)
            {
                //# Choose a starting point among suggested starting points, if more than one point was suggested
                if (this.Start.Length > 1)
                {
                    this.Start = Tools.Combine(this.Start, this.Start.Mean());
                    //# Add the middle point to the two potential starting points
                    double[] logF = new double[this.Start.Length];
                    for (int i = 0; i < this.Start.Length; i++)
                    {
                        logF[i] = this.O.LogF(this.Start[i], A);
                    }
                    //double[] logF = new double[] { this.O.LogF(this.Start[0], A), this.O.LogF(this.Start[1], A), this.O.LogF(this.Start.Mean(), A) };
                    //TODO peut planter
                    startingPoint = this.Start[logF.WhichMax()];
                } //if (this.Start.Length > 1)
                else
                {
                    startingPoint = this.Start[0];
                }
                //# Find mode by Newton-Raphson
                SecuredNRA oMode = new SecuredNRA(this.O.LogFPrime, this.O.LogFSecond, this.Range);
                nr = new SecuredNRSearch(oMode, A, startingPoint, this.Epsilon, this.Range);
                // 4 derniers paramètres prennent les valeurs par défaut:
                // maxPoint = null, target = 0, inestimableLowerLimit = false,  expectedHpSign = -1
                if (nr.Converged)
                {
                    double hs = this.O.LogFSecond(nr.X, A);
                    if (hs > 0)
                    {
                        //# we have found a mimimum point (between two local modes):
                        //# we redo the search on both sides of this dip
                        mode.X = HigherLocalMax(this.O, A, nr.X, this.Range, this.Epsilon);
                    } //if (hs > 0)
                    else
                    {
                        mode.X = nr.X;
                    } //if (hs > 0)

                    mode.Found = true;
                } //if (nr.Converged)
                else
                {
                    double[] xA           = nr.Bounds.X;
                    double[] hA           = nr.Bounds.H;
                    bool     hOnBothSides = hA.Aggregate(1, (x, y) => x * Math.Sign(y)) < 0;
                    double   diffX        = xA.Diff()[0];
                    if ((diffX < this.Epsilon) && hOnBothSides)
                    {
                        mode.X     = xA.Mean();
                        mode.Found = true;
                    } //if ((diffX < this.Epsilon) && hOnBothSides)
                    else
                    {
                        bool converged = false;
                        if ((nr.Bounds.X.Diff()[0] < this.Epsilon) && nr.Bounds.H.All(a => a < 0))
                        {
                            if (this.Range[0].IsFinite())
                            {
                                //# We try a little bit further to find the actual mode,
                                //# since it looks like we are almost there...
                                converged = true;
                                MaxFastTrackObject tmp = MaxFastTrack(this.O, A, this.Range[1], nr.Bounds.X[0], nr.Bounds.H[0]); //# yes: 4th argument is bounds$h (and not hp!)

                                if (tmp.Converged)
                                {
                                    mode.X     = tmp.X;
                                    mode.Found = true;
                                } //if (tmp.Converged)
                                else
                                {
                                    mode.X     = this.Range[1];
                                    mode.Found = !this.InestimableLowerLimit;
                                } //if (tmp.Converged)
                            }     //if (this.Range[0].IsFinite())
                            else
                            {
                                double x   = nr.Bounds.X[0];
                                double h   = nr.Bounds.H[0];
                                double tmp = Math.Abs(h / this.O.LogFSecond(x, this.A));
                                if (tmp < 1e-12)
                                {
                                    mode      = new LMode(x, h, true);
                                    converged = true;
                                } //if (tmp < 1e-12)
                            }     //if (this.Range[0].IsFinite())
                        }

                        if (!converged)
                        {
                            throw new WEException("Algorithm did not converge.");
                        } //if (!converged)
                    }     //if ((diffX < this.Epsilon) && hOnBothSides)
                }         //if (nr.Converged)

                if (mode.Found)
                {
                    mode.H = this.O.LogF(mode.X, this.A);
                }
            } //if (!mode.Found)

            double logFRatioRemote = Math.Log(this.FRatioRemote);// # new_0.11
            double target          = Tools.ND; // R n'a pas vraiment de notion de scope

            if (mode.Found)
            {
                target = mode.H + logFRatioRemote;
            }

            this.A.Mode = mode.X; //  # some 'remote.*' functions need that information
                                  //# modif_0.11
                                  //# Find remote points on both sides of mode
            double remoteLeft = 0, remoteRight = 0;

            bool[] modeOnBorder = new bool[2];
            double fs           = Tools.ND;

            if (mode.Found)
            {
                //TODO pourquoi la définition et l'affectation de target ne serait-elle pas ici?
                double[] x  = new double[] { Tools.NA, Tools.NA };
                double[] f  = new double[] { Tools.NA, Tools.NA };
                double[] fp = new double[] { Tools.NA, Tools.NA };
                fs = this.O.LogFSecond(mode.X, A);// # scalar
                double d = 10 / Math.Abs(fs);
                //# new_0.11
                double[] remoteStart = this.O.LogFInvRemote(target, this.A).ToArray();                 //  # vector
                                                                                                       // TODO start est un paramètre de la fonction en R, vérifier l'impact de modifier la référence d'objet ici.
                this.Start = SplitStartingValuesLeftRight(this.O, target, A, this.Range, remoteStart); // # vector of length 2

                modeOnBorder = new bool[2] {
                    this.Range[0] == mode.X, this.Range[1] == mode.X
                };                                                                               //# new_0.11
                List <int> jSeq = new List <int>();
                for (int i = 0; i < 2; i++)
                {
                    if (!modeOnBorder[i] && this.Start[i].IsNA())
                    {
                        jSeq.Add(i);
                    }
                }

                foreach (int j in jSeq)
                {
                    //# modif_0.11 (first block that appeared here was moved above)
                    // condition inutile
                    if (Tools.IsNA(this.Start[j]))
                    {
                        int    dir      = (j == 0) ? -1 : 1;
                        double tmp      = mode.X + dir * d;
                        bool   accepted = (tmp > this.Range[0]) && (tmp < this.Range[1]);
                        if (!accepted)
                        {
                            double myD = d;
                            while (!accepted)
                            {
                                myD      = myD / 2;
                                tmp      = mode.X + dir * myD;
                                accepted = (tmp > this.Range[0]) && (tmp < this.Range[1]);
                            }
                        }

                        fp[0]    = this.O.LogFPrime(tmp, A);
                        accepted = fp[0].IsFinite();
                        while (!accepted)
                        {
                            tmp      = (tmp + mode.X) / 2;
                            fp[0]    = this.O.LogFPrime(tmp, A);
                            accepted = fp[0].IsFinite();
                        }

                        double a  = fp[0] / d;                                                // # estimated rate of slope acceleration
                        double d2 = Math.Sqrt(2 * Math.Abs(Math.Log(this.FRatioRemote) / a)); // # estimated distance we need to get from mode,
                                                                                              //# at the rate above, to reach the f.ratio.remote distance
                        double x2 = mode.X + dir * d2;
                        accepted = (x2 > this.Range[0]) && (x2 < this.Range[1]);
                        while (!accepted)
                        {
                            d2       = d2 / 2;
                            x2       = mode.X + dir * d2;
                            accepted = x2 > this.Range[0] & x2 < this.Range[1];
                        }

                        double fp2 = this.O.LogFPrime(x2, A);
                        accepted = fp2.IsFinite();

                        while (!accepted)
                        {
                            d2       = d2 / 2;
                            x2       = mode.X + dir * d2;
                            fp2      = this.O.LogFPrime(x2, A);
                            accepted = Tools.IsFinite(fp2);
                        }

                        x = new double[2] {
                            tmp, x2
                        };
                        f = new double[2] {
                            Tools.NA, Tools.NA
                        };
                        for (int i = 0; i < 2; i++)
                        {
                            f[i] = this.O.LogF(x[i], A);
                        }
                        fp[1] = fp2;

                        //# Approximating the shape of log.f.prime by a straight line,
                        //# we can estimate the point where the distance
                        //# prescribed by f.ratio.remote is reached

                        double[,] xSolved = Matrix2x2.Solve2x2(new double[] { x[0], x[1], 1, 1 }, byCol: true); // # 2 x 2 matrix
                        double[] theta = Matrix2x2.Product(xSolved, fp);
                        double   qA    = theta[0];
                        double   qB    = theta[1];
                        // ifelse(xor(j==1, x[1]>x[2]), 2, 1)
                        int    innerSide = (j == 0) ^ (x[0] > x[1]) ? 1 : 0;
                        double innerX    = x[innerSide];
                        double qC        = -qA *Math.Pow(innerX, 2) - qB * innerX - (target - f[innerSide]);

                        double l;
                        double u;
                        if (j == 0)
                        {
                            l = this.Range[0];
                            u = mode.X;
                        }
                        else
                        {
                            l = mode.X;
                            u = this.Range[1];
                        }

                        List <double> roots = QuadraticEquation.GetRealRoots(new double[] { qC, qB, qA }, l: l, u: u, ROrder: true);// # modif_0.11
                        switch (roots.Count)
                        {
                        case 0:
                            this.Start[j] = x[1 - innerSide];
                            break;

                        case 1:
                            this.Start[j] = roots[0];
                            break;

                        default:
                            if (j == 1)
                            {
                                this.Start[j] = roots.Max();
                            }
                            else
                            {
                                this.Start[j] = roots.Min();
                            }
                            break;
                        }
                    }
                }
                //# new_0.11
                //# Suggest another starting point (on both sides)
                //# based on the 2nd degree polynomial approximation (Taylor series devpmt) of log.f

                f = new double[0];
                if (!modeOnBorder.Any(pX => pX))
                {
                    double   delta = Math.Sqrt(2 * logFRatioRemote / fs);
                    double[] altStart;

                    //# Left
                    double xScalar = mode.X - delta;
                    if (xScalar > this.Range[0])
                    {
                        altStart = new double[] { this.Start[0], xScalar };
                        f        = new double[] { Math.Abs(this.O.LogF(altStart[0], A) - target), Math.Abs(this.O.LogF(altStart[1], A) - target) };
                        IEnumerable <double> lstD = f.Substract(target).Abs();
                        int w = (lstD.First() < lstD.Last()) ? 0 : 1;
                        this.Start[0] = altStart[w];
                    }

                    //# Right side
                    xScalar = mode.X + delta;
                    if (xScalar < this.Range[1])
                    {
                        altStart = new double[] { this.Start[1], xScalar };
                        f        = new double[] { Math.Abs(this.O.LogF(altStart[0], A) - target), Math.Abs(this.O.LogF(altStart[1], A) - target) };
                        IEnumerable <double> lstD = f.Substract(target).Abs();
                        int w = (lstD.First() < lstD.Last()) ? 0 : 1;
                        this.Start[1] = altStart[w];
                    }
                }
                //oh <- list(h=o$log.f, hp=o$log.f.prime, hs=o$log.f.second)
                SecuredNRA oh = new SecuredNRA(this.O.LogF, this.O.LogFPrime, this.O.LogFSecond);// # modif_0.11
                if (Tools.IsNA(this.Start[0]))
                {
                    remoteLeft = mode.X;
                }
                else
                {
                    double[] range = new double[] { this.Range[0], mode.X };
                    //LMaxPoint lm = new LMaxPoint(mode);
                    nr = new SecuredNRSearch(oh, A, this.Start[0], this.Epsilon, range,
                                             maxPoint: mode, target: target, inestimableLowerLimit: this.InestimableLowerLimit);
                    // le dernier paramètre a une valeur par défaut: expectedHpSign = -1

                    if (nr.Converged)
                    {
                        remoteLeft = nr.X;
                    }
                    else if (this.Range[0].IsFinite())
                    {
                        remoteLeft = this.Range[0];
                    }
                    else
                    {
                        //TODO Exception
                        throw new WEException("Fin temporaire!");
                    }
                }

                if (Tools.IsNA(this.Start[1]))
                {
                    remoteRight = mode.X;
                } // if
                else
                {
                    this.Range = new double[] { mode.X, this.Range[1] };
                    nr         = new SecuredNRSearch(oh, A, this.Start[1], this.Epsilon, this.Range,
                                                     maxPoint: mode, target: target);
                    // les 2 derniers paramètres prennent des valeurs par défaut:
                    // inestimableLowerLimit = false, expectedHpSign = -1
                    if (nr.Converged)
                    {
                        remoteRight = nr.X;
                    }
                    else
                    {
                        throw new WEException("Algorithm did not converge.");// # should not happen
                    }
                }
            }
            else
            {
                //# new_0.11
                //# mode was not found
                remoteLeft  = Tools.NA;
                remoteRight = Tools.NA;
            }

            //list(mode = mode, remote = c(remote.left, remote.right))

            LModeExt lmext = new LModeExt(mode, remoteLeft, remoteRight);

            return(lmext);
        } //# end of ref.points
Exemplo n.º 3
0
        internal SecuredNRSearch(
            ISecuredNR o,
            SGNFnAParam A,
            double start,
            double epsilon,
            double[] range,
            LMode maxPoint             = null,
            double target              = 0,
            bool inestimableLowerLimit = false,
            // int maxNIter = 500,
            int expectedHpSign = -1)

        //max.point=list(x= numeric(0), h= numeric(0)),
        {
            //# Throughout this function, 'bounds' is a list with the following elements/dimensions:
            //# x, h, hp, range:   numeric (vectors of length 2)
            //# higherSide:       numeric (1)
            //# IncludeSoln:      logical (1)

            //TODO l'accès à h2Modeless est difficile à comprendre en R qui permet l'accès à des variables non initialisé ... quitte à planter!
            //pour ce faire on introduit ici h2Modeless ...

            //-->secured.NR.search ,start=9.24215468292111 ,target=0 ,range=( 0, Inf ) ,inestimable.lower.limit=FALSE ,max.point$x= ,max.point$h= ,expected.hpsign=-1

            double h2Modeless = Tools.NA;

            if (maxPoint == null)
            {
                maxPoint = new LMode();
            }
            bool   modeSearch = Tools.IsNA(maxPoint.X);
            bool   monotonic  = !modeSearch;
            LLimit limit      = new LLimit();

            limit.X        = range.Copy();          // les autres champs prennent les valeurs par défaut désirée
            limit.Chckd[0] = inestimableLowerLimit; //# new_0.11
            Bounds bounds = new Bounds(
                Tools.Rep(Tools.NA, 2),             //x
                Tools.Rep(Tools.NA, 2),             //h
                Tools.Rep(Tools.NA, 2),             //hp
                null,                               // range
                -1,                                 // higherSide
                false);                             // includeSoln)

            bool found2Bounds;
            bool correct4HpSign;
            int  higherSide;

            if (!Tools.IsNA(maxPoint.X))
            {
                higherSide            = (start < maxPoint.X) ? 1 : 0; // higherSide est un indice
                bounds.X[higherSide]  = maxPoint.X;
                bounds.H[higherSide]  = maxPoint.H;
                bounds.Hp[higherSide] = 0.0;
                found2Bounds          = true;
                expectedHpSign        = higherSide == 0 ? -1 : 1;
                correct4HpSign        = start > maxPoint.X;
                h2Modeless            = (maxPoint.H * 2.0) - target;
            }
            else
            {
                found2Bounds   = false;
                higherSide     = 0;
                correct4HpSign = true;
            }

            int    lowerSide = 1 - higherSide;
            double x         = start;

            //# make sure that x is not out of range
            if (x < limit.X[0])
            {
                x = (maxPoint.X + limit.X[0]) / 2;
            }


            bounds.Range      = range.Copy();
            bounds.HigherSide = higherSide;
            //# Register initial x into bounds
            double h  = o.H(x, A);
            double hp = o.HPrime(x, A);
            int    side;

            if (!Tools.IsNA(maxPoint.X))
            {
                bounds.IncludeSoln = h < target;
                side = lowerSide;
            }
            else
            {
                side = 0;
            }

            bounds.X[side]  = x;
            bounds.H[side]  = h;
            bounds.Hp[side] = hp;
            Visits visitedX = new Visits(SecuredNRSearch.MaxNIter);

            visitedX.Add(x);

            //# Do a first step
            bool cntn;

            if (correct4HpSign)
            {
                hp = expectedHpSign * Math.Abs(hp);
            }

            double change = (h - target) / hp;

            x = x - change;
            //# cntn until convergence
            bool unreachableMode = false;

            cntn = true;
            int  count     = 0;
            bool converged = false;
            int  debCount  = 0;

            while (cntn)
            {
                debCount++;
                if (x.IsNaN())
                {
                    if (1 == 1)
                    {
                    }
                }
                bool computedH = false;
                bool accepted  = false;
                count = count + 1;
                LWhere wb; // = WithinBounds(x, bounds);
                while (!accepted)
                {
                    wb = WithinBounds(x, bounds);
                    if (bounds.IncludeSoln)
                    {
                        if (wb.Within)
                        {
                            accepted = true;
                        }
                        else
                        {
                            x        = CubicExtrapolation.Extrapolate(target, bounds, monotonic, range);
                            accepted = !Tools.IsNA(x);
                        }
                    }
                    else if (wb.Above)
                    {
                        //# bounds do not include solution and we are looking above bounds
                        if (x > limit.X[1])
                        {
                            if (limit.Chckd[1])
                            {
                                accepted = true;
                                h        = limit.H[1];

                                if (UnreachedTarget(target, h, higherSide, monotonic, modeSearch))
                                {
                                    //# force end of while-loop, as target is not reached even at this end
                                    x         = limit.X[1];
                                    h         = target;
                                    computedH = true;
                                }
                                else if (limit.Usable[1])
                                {
                                    x         = bounds.Range[1];
                                    hp        = limit.Hp[1];
                                    computedH = true;
                                }
                                else
                                {
                                    x = (x + change + bounds.Range[1]) / 2;
                                }
                            }
                            else
                            {
                                limit.Chckd[1] = true;
                                h = o.H(limit.X[1], A);
                                if (UnreachedTarget(target, h, higherSide, monotonic, modeSearch))
                                {
                                    //# force end of while-loop, as target is not reached even at this end
                                    x         = limit.X[1];
                                    h         = target;
                                    accepted  = true;
                                    computedH = true;
                                }
                                else
                                {
                                    limit.H[1] = h;
                                    if (h.IsFinite())
                                    {
                                        hp              = o.HPrime(limit.X[1], A);
                                        limit.Hp[1]     = hp;
                                        limit.Usable[1] = hp.IsFinite();
                                        accepted        = limit.Usable[1];
                                    }
                                    else
                                    {
                                        accepted = false;
                                    }

                                    if (accepted)
                                    {
                                        x               = bounds.Range[1];
                                        computedH       = true;
                                        unreachableMode = modeSearch && h > 0;//# new_0.11
                                    }
                                    else
                                    {
                                        x = (x + change + bounds.Range[1]) / 2;
                                    }
                                }
                            }
                        }
                        else
                        {
                            ParamB01 tmp = NewBound(x, bounds, monotonic, o, A, target, range, wb.Above);
                            x         = tmp.X;
                            h         = tmp.H;
                            hp        = tmp.Hp;
                            computedH = true;
                            accepted  = true;
                        }
                    }
                    else
                    {
                        //# bounds do not include solution and we are looking below bounds
                        if (x <= limit.X[0])
                        {
                            //# we are looking out of the variable domain (bad!)
                            if (!limit.Chckd[0])
                            {
                                limit.Chckd[0] = true;
                                h          = o.H(limit.X[0], A);
                                limit.H[0] = h;
                                if (h.IsFinite())
                                {
                                    if (UnreachedTarget(target, h, higherSide, monotonic, modeSearch, leftSide: true))
                                    {
                                        //# force end of while-loop, as target is not reached even at this end
                                        x        = limit.X[0];
                                        h        = target;
                                        accepted = true;
                                    }
                                    else
                                    {
                                        hp              = o.HPrime(limit.X[0], A);
                                        limit.Hp[0]     = hp;
                                        limit.Usable[0] = hp.IsFinite();
                                        accepted        = limit.Usable[0];
                                    }

                                    computedH = accepted;
                                    if (accepted)
                                    {
                                        x = limit.X[0];
                                    }
                                }
                                else
                                {
                                    limit.Usable[0] = false;
                                    x = (bounds.X.Where(a => !Tools.IsNA(a)).Min() + limit.X[0]) / 2;
                                }
                            }
                            else if (limit.Usable[0])
                            {
                                x         = limit.X[0];
                                h         = limit.H[0];
                                hp        = limit.Hp[0];
                                computedH = true;
                                accepted  = true;
                            }
                            else
                            {
                                //# not limit$usable[1]
                                x = (bounds.X.Min() + limit.X[0]) / 2;
                            }
                        }
                        else
                        {
                            ParamB01 tmp = NewBound(x, bounds, monotonic, o, A, target, range, wb.Above);
                            x         = tmp.X;
                            h         = tmp.H;
                            hp        = tmp.Hp;
                            computedH = true;
                            accepted  = true;
                            //    # new_0.11
                            unreachableMode = modeSearch && (((x == limit.X[1]) && (h > 0)) || ((x == limit.X[0]) && (h < 0)));
                        }
                    }
                } // while(!accepted)

                if (!computedH)
                {
                    h  = o.H(x, A);
                    hp = o.HPrime(x, A);
                }

                converged = (Math.Abs(h - target) < epsilon) || unreachableMode;// # modif_0.11
                cntn      = !converged;
                if (cntn)
                {
                    //# register results in bounds
                    bool hLtTarget = h < target;
                    if (bounds.IncludeSoln)
                    {
                        side = hLtTarget ? lowerSide : higherSide;
                    }
                    else
                    {
                        bounds.IncludeSoln = hLtTarget ^ (bounds.H[0] < target);

                        if (found2Bounds)
                        {
                            bool changeOppositeSideBounds = true;
                            if (modeSearch)
                            {
                                side = hLtTarget ? lowerSide : higherSide;
                                changeOppositeSideBounds = true;
                            }
                            else if (bounds.IncludeSoln)
                            {
                                //# bounds newly include solution (happening for the first time with current x)
                                wb = WithinBounds(x, bounds);
                                if (wb.Within)
                                {
                                    if (higherSide == 1)
                                    {
                                        side = lowerSide;
                                        changeOppositeSideBounds = false;
                                        if (hp < 0)
                                        {
                                            bounds.X[lowerSide]  = x;
                                            bounds.H[lowerSide]  = h;
                                            bounds.Hp[lowerSide] = hp;
                                            ParamB01 tmp = LeftSideFastScan(bounds, o, A, target, range[0], h2Modeless);
                                            x    = tmp.X;
                                            h    = tmp.H;
                                            hp   = tmp.Hp;
                                            cntn = tmp.Cntn;
                                        }
                                    }
                                    else
                                    {
                                        throw new WEException("Scénario imprévu.");
                                    }
                                }
                                else if (wb.Below)
                                {
                                    side = 0;
                                    changeOppositeSideBounds = true;
                                }
                                else
                                {
                                    //# wb.above
                                    side = 1;
                                    changeOppositeSideBounds = true;
                                }
                            }
                            else
                            {
                                //# bounds still do not include solution
                                wb = WithinBounds(x, bounds);
                                if (wb.Within)
                                {
                                    changeOppositeSideBounds = false;
                                    if (h > bounds.H.Max())
                                    {
                                        side = higherSide;
                                    }
                                    else if (h < bounds.H.Min())
                                    {
                                        side = hp < 0 ? lowerSide : higherSide;
                                    }
                                    else
                                    {
                                        side = higherSide;
                                    }
                                }
                                else if (wb.Below)
                                {
                                    //Modifié à 1
                                    if (higherSide == 1)
                                    {
                                        if (hp < 0)
                                        {
                                            bounds.X[lowerSide]  = x;
                                            bounds.H[lowerSide]  = h;
                                            bounds.Hp[lowerSide] = hp;
                                            ParamB01 tmp = LeftSideFastScan(bounds, o, A, target, range[lowerSide], h2Modeless);
                                            x    = tmp.X;
                                            h    = tmp.H;
                                            hp   = tmp.Hp;
                                            cntn = tmp.Cntn;
                                            changeOppositeSideBounds = true;
                                        }
                                        else if (h < bounds.H.Min())
                                        {
                                            side = lowerSide;
                                            changeOppositeSideBounds = true;
                                        }
                                        else
                                        {
                                            L1       leftSidePotentialStartPoint = new L1(x, h, hp);
                                            double[] lim = new double[] { x, bounds.X[lowerSide] };
                                            x  = lim.Mean();
                                            hp = o.HPrime(x, A);
                                            while (hp > 0)
                                            {
                                                h         = o.H(x, A);
                                                side      = h > bounds.H[lowerSide] ? 0 : 1;
                                                lim[side] = x;
                                                x         = lim.Mean();
                                                hp        = o.HPrime(x, A);
                                            }
                                            h = o.H(x, A);

                                            bounds.X[higherSide]  = bounds.X[lowerSide];
                                            bounds.H[higherSide]  = bounds.H[lowerSide];
                                            bounds.Hp[higherSide] = bounds.Hp[lowerSide];
                                            bounds.X[lowerSide]   = x;
                                            bounds.H[lowerSide]   = h;
                                            bounds.Hp[lowerSide]  = hp;
                                            ParamB01 tmp = LeftSideFastScan(bounds, o, A, target, range[lowerSide], h2Modeless, leftSidePotentialStartPoint);
                                            x    = tmp.X;
                                            h    = tmp.H;
                                            hp   = tmp.Hp;
                                            cntn = tmp.Cntn;
                                            side = higherSide;
                                            changeOppositeSideBounds = false;
                                        }
                                    }
                                    else
                                    {
                                        throw new WEException("Scénario imprévu.");
                                    }
                                }
                                else
                                {
                                    //# wb.above
                                    side = lowerSide;
                                    changeOppositeSideBounds = true;
                                }
                            }

                            if (changeOppositeSideBounds)
                            {
                                int oppositeSide = 1 - side;
                                bounds.X[oppositeSide]  = bounds.X[side];
                                bounds.H[oppositeSide]  = bounds.H[side];
                                bounds.Hp[oppositeSide] = bounds.Hp[side];
                            }
                        }//if(found2bounds)
                        else
                        {
                            //# !found2bounds
                            found2Bounds = true;
                            side         = h < bounds.H[0] ? lowerSide : higherSide;
                            if (side == 0)
                            {
                                bounds.X[1]  = bounds.X[0];
                                bounds.H[1]  = bounds.H[0];
                                bounds.Hp[1] = bounds.Hp[0];
                            }
                        }
                    }

                    bounds.X[side]  = x;
                    bounds.H[side]  = h;
                    bounds.Hp[side] = hp;
                    visitedX.Add(x);
                    converged = (Math.Abs(h - target) < epsilon) || unreachableMode;// # modif_0.11
                    if (!converged)
                    {
                        cntn = count < SecuredNRSearch.MaxNIter;// # new_0.11: changed <= for <

                        if (!cntn)
                        {
                            //# Before we give up, we check one last thing:
                            //# if the last few steps were all leaning in the same direction,
                            //# then convergence may only be a question of time & patience!
                            //# Give it (yet) another chance!

                            //TODO BIEN VÉRIFIER LA LOGIQUE
                            Visits.DirectionChange[] directionChanges = visitedX.GetDirectionChanges();
                            if (directionChanges.Length > 0)
                            {
                                Visits.DirectionChange lastChangeDirection = directionChanges.Last(); //
                                IEnumerable <Visits.DirectionChange> inOppositeDirection = directionChanges.Where(a => a.Direction == -lastChangeDirection.Direction);
                                if (inOppositeDirection.Count() == 0)
                                {
                                    cntn = true;
                                }
                                else
                                {
                                    Visits.DirectionChange lastInOppositeDirection = inOppositeDirection.Last();
                                    if ((count - lastInOppositeDirection.Index) > 20)
                                    {
                                        cntn = true;
                                    }
                                }
                            }
                            else
                            {
                                //TODO ne devrait pas se produire.
                            }

                            if (cntn)
                            {
                                count    = 0;
                                visitedX = new Visits(SecuredNRSearch.MaxNIter);
                            }
                        } //if(!cntn)
                    }     // if (!converged)

                    if (cntn)
                    {
                        found2Bounds = true;
                        double absHp = Math.Abs(hp);
                        if (correct4HpSign)
                        {
                            hp = expectedHpSign * absHp;
                        }
                        change = (h - target) / hp;
                        double pctChange = Math.Abs(change) / bounds.X.Diff()[0];

                        if ((pctChange < 1e-4) && absHp > 1000)
                        {
                            double[] p;
                            if (change < 0)
                            {
                                p = new double[] { 0.9, 0.1 };
                            }
                            else
                            {
                                p = new double[] { 0.1, 0.9 };
                            }
                            x = p.Multiply(bounds.X).Sum();
                        }
                        else
                        {
                            x = x - change;
                        }
                    } //if (cntn)

                    //#cat('count=', count, '\n')
                    //#cat('x = ', x, '\n')
                    //#cat('h = ', h, '\n')
                    //#cat('target = ', target, '\n')
                    //#cat('abs(h-target)', abs(h-target), '\n')
                    //#cat('bounds.x = ', bounds.x, '\n')
                    //#cat('diff(bounds.x) = ', diff(bounds.x), '\n')
                    //#if (diff(bounds.x) < 0) cat('***************\n')
                } // if (cntn)
            }     //while (cntn)

            this.X         = x;
            this.Converged = converged;
            this.Bounds    = bounds;
            //list(x = x, converged = converged, bounds = bounds);
        } //# end of secured.NR.search