public void ZH_L16_AscendDecsendTest_NoActiveGas_ThrowException()
        {
            ZH_L16 target = new ZH_L16();

            double start = 1;
            double finish = 1;
            double rate = 1;
            Assert.Throws<ArgumentException>(() => target.AscendDecsend(start, finish, rate));
        }
        public void ZH_L16_AscendDecsendTest_NegativeFinishDepth_ThrowException()
        {
            Gas gas = new Gas(0.11, 0.79, 0.1);
            ZH_L16 target = new ZH_L16();
            target.ActiveGas = gas;

            double start = 1;
            double finish = -1;
            double rate = 1;
            Assert.Throws<ArgumentException>(() => target.AscendDecsend(start, finish, rate));
        }
        private TimeSpan GotoDecoDepth(ZH_L16 algorithm, double currentDepth, double finalDepth, int runTime,
            ref Collection<DiveSegment> diveTable, out double stopDepth)
        {
            double minDepthAscent = algorithm.MinDepthAscent(CalcGradient(firstDecoDepth, currentDepth, pref));
            double decoDepth = GetDecoDepth(minDepthAscent, pref);

            if (decoDepth >= currentDepth)
            {
                stopDepth = decoDepth;
                return new TimeSpan();
            }

            Logger.Trace("GotoDecoDepth => start deco depth:" + decoDepth);
            algorithm.AscendDecsend(currentDepth, decoDepth, pref.AscRate);
            stopDepth = ContinueAscend(algorithm, finalDepth, decoDepth);

            TimeSpan timeSpentInGoingUp = CalcAscDescTime(currentDepth - stopDepth, pref.AscRate);
            int timespent = TimeHelper.MinutesRoundedUp(timeSpentInGoingUp);

            // Round to complete minute
            double diffTime = Math.Abs(timeSpentInGoingUp.TotalMinutes - timespent);
            double diffDepth = CalcHalfWayDepth(currentDepth, stopDepth);
            if (Math.Abs(diffTime - 0) > 0.001)
            {
                var algorithm2 = new ZH_L16(algorithm);
                algorithm2.AddRunTimeInMinutes(diffTime, diffDepth);
                    // Try adding additional runtime to verify if we should go up even more.
                double newStopDepth = ContinueAscend(algorithm2, finalDepth, stopDepth);
                if (newStopDepth < stopDepth)
                {
                    ContinueAscend(algorithm, finalDepth, stopDepth);

                    timeSpentInGoingUp = CalcAscDescTime(currentDepth - newStopDepth, pref.AscRate);
                    timespent = TimeHelper.MinutesRoundedUp(timeSpentInGoingUp);

                    // Round to complete minute
                    diffTime = Math.Abs(timeSpentInGoingUp.TotalMinutes - timespent);
                    diffDepth = CalcHalfWayDepth(currentDepth, stopDepth);
                    if (Math.Abs(diffTime - 0) > 0.001)
                    {
                        algorithm.AddRunTimeInMinutes(diffTime, diffDepth);
                    }

                    stopDepth = newStopDepth;
                }
            }

            double cns = CentralNervousSystem.AscendDescend(algorithm.ActiveGas, currentDepth, stopDepth, Math.Abs(pref.AscRate));
            double otu = OxygenToxicityUnit.AscendDescend(algorithm.ActiveGas, currentDepth, stopDepth, Math.Abs(pref.AscRate));
            timeSpentInGoingUp = TimeSpan.FromMinutes(timespent);
            var seg = new DiveSegment(stopDepth, timeSpentInGoingUp, DiveState.Ascend, runTime, runTime + timespent,
                                      algorithm.ActiveGas, cns, otu);
            Logger.Trace("GotoDecoDepth => Segment:" + seg);
            diveTable.Add(seg);

            return timeSpentInGoingUp;
        }
        private TimeSpan GoingUp(ZH_L16 algorithm, ref Collection<DiveSegment> divetable, int runTime, double depth, WayPoint waypoint)
        {
            var ascdescTime = new TimeSpan();

            double minDepthAscent = algorithm.MinDepthAscent(CalcGradient(firstDecoDepth, depth, pref));

            //// No deco needed
            if (minDepthAscent <= waypoint.Depth)
            {
                algorithm.AscendDecsend(depth, waypoint.Depth, pref.AscRate);
                ascdescTime = CalcAscDescTime(depth - waypoint.Depth, pref.AscRate);
                int timespent = TimeHelper.MinutesRoundedUp(ascdescTime);
                double cns = CentralNervousSystem.AscendDescend(algorithm.ActiveGas, depth, waypoint.Depth, Math.Abs(pref.AscRate));
                double otu = OxygenToxicityUnit.AscendDescend(algorithm.ActiveGas, depth, waypoint.Depth, Math.Abs(pref.AscRate));

                // Round to complete minute
                double diffTime = Math.Abs(ascdescTime.TotalMinutes - timespent);
                double diffDepth = CalcHalfWayDepth(depth, waypoint.Depth);
                algorithm.AddRunTimeInMinutes(diffTime, diffDepth);
                ascdescTime = TimeSpan.FromMinutes(timespent);
                cns += CentralNervousSystem.ConstantDepth(algorithm.ActiveGas, diffDepth, diffTime);
                otu += OxygenToxicityUnit.ConstantDepth(algorithm.ActiveGas, diffDepth, diffTime);

                var seg = new DiveSegment(waypoint.Depth, ascdescTime, DiveState.Ascend, runTime, runTime + timespent,
                                          algorithm.ActiveGas, cns, otu);
                Logger.Trace("Calc => Segment:" + seg);
                divetable.Add(seg);
            }
            else
            {
                ascdescTime = GotoDecoDepth(algorithm, depth, waypoint.Depth, runTime, ref divetable, out depth);
                runTime += TimeHelper.MinutesRoundedUp(ascdescTime);
                if (Math.Abs(depth - waypoint.Depth) > 0.001)
                {
                    TimeSpan decoTime = DoDeco(algorithm, depth, waypoint.Depth, runTime, ref divetable);
                    ascdescTime += decoTime;
                }
            }

            return ascdescTime;
        }
        private TimeSpan GoingDown(ZH_L16 algorithm, ref Collection<DiveSegment> divetable, int runTime,
            double currentDepth, WayPoint waypoint)
        {
            algorithm.AscendDecsend(currentDepth, waypoint.Depth, pref.DescRate);
            TimeSpan ascdescTime = CalcAscDescTime(waypoint.Depth - currentDepth, pref.DescRate);
            int timespent = TimeHelper.MinutesRoundedUp(ascdescTime);
            double cns = CentralNervousSystem.AscendDescend(algorithm.ActiveGas, currentDepth, waypoint.Depth, Math.Abs(pref.DescRate));
            double otu = OxygenToxicityUnit.AscendDescend(algorithm.ActiveGas, currentDepth, waypoint.Depth, Math.Abs(pref.DescRate));

            // Round to complete minute
            double diffTime = Math.Abs(ascdescTime.TotalMinutes - timespent);
            double diffDepth = CalcHalfWayDepth(currentDepth, waypoint.Depth);
            if (Math.Abs(diffTime - 0) > 0.001)
            {
                algorithm.AddRunTimeInMinutes(diffTime, diffDepth);
                ascdescTime = TimeSpan.FromMinutes(timespent);
                cns += CentralNervousSystem.ConstantDepth(algorithm.ActiveGas, diffDepth, diffTime);
                otu += OxygenToxicityUnit.ConstantDepth(algorithm.ActiveGas, diffDepth, diffTime);
            }

            var seg = new DiveSegment(waypoint.Depth, ascdescTime, DiveState.Desend, runTime, runTime + timespent,
                                      algorithm.ActiveGas, cns, otu);
            Logger.Trace("Calc => Segment:" + seg);
            divetable.Add(seg);

            return ascdescTime;
        }
        private double ContinueAscend(ZH_L16 algorithm, double finalDepth, double decoDepth)
        {
            double stopDepth;

            double minDepthAscent = algorithm.MinDepthAscent(CalcGradient(firstDecoDepth, decoDepth, pref));

            //// deco was not needed
            if (minDepthAscent <= finalDepth)
            {
                Logger.Trace("DoDeco => Deco not needed");
                algorithm.AscendDecsend(decoDepth, finalDepth, pref.AscRate);
                stopDepth = finalDepth;
            }
            else
            {
                bool done = false;
                //// Check if we during ascent did get rid of deco obligations for that depth
                while (!done)
                {
                    if (minDepthAscent < (decoDepth - pref.DepthBetweenDecoStops))
                    {
                        double oldDecoDepth = decoDepth;
                        decoDepth = GetDecoDepth(minDepthAscent, pref);

                        //To not go to far
                        if (decoDepth < finalDepth)
                        {
                            decoDepth = finalDepth;
                            done = true;
                        }

                        Logger.Trace(
                            "GotoDecoDepth => During ascent to depth:{0} we have changed celling to next deco depth:{1}",
                            oldDecoDepth, decoDepth);
                        algorithm.AscendDecsend(oldDecoDepth, decoDepth, pref.AscRate);
                        minDepthAscent = algorithm.MinDepthAscent(CalcGradient(firstDecoDepth, decoDepth, pref));
                    }
                    else
                    {
                        done = true;
                    }
                }

                stopDepth = decoDepth;
            }
            return stopDepth;
        }