public static CalendarOpPlusFastAddResult ComputeNextDue(
            long currentTime,
            TimePeriod timePeriod,
            DateTimeEx reference)
        {
            if (reference.TimeInMillis > currentTime)
            {
                return(new CalendarOpPlusFastAddResult(0, reference.DateTime));
            }

            // add one time period
            var work = new DateTimeEx(reference);

            if (DEBUG && Log.IsDebugEnabled)
            {
                Log.Debug("Work date is " + work.DateTime.Print());
            }

            CalendarOpPlusMinus.ActionSafeOverflow(work, 1, timePeriod);
            long inMillis = work.TimeInMillis;

            if (inMillis > currentTime)
            {
                return(new CalendarOpPlusFastAddResult(1, work.DateTime));
            }
            if (DEBUG && Log.IsDebugEnabled)
            {
                Log.Debug("Work date is " + work.DateTime.Print());
            }

            long factor = 1;

            // determine multiplier
            long   deltaCurrentToStart   = currentTime - reference.TimeInMillis;
            long   deltaAddedOne         = work.TimeInMillis - reference.TimeInMillis;
            double multiplierDbl         = (deltaCurrentToStart / deltaAddedOne) - 1;
            long   multiplierRoundedLong = (long)multiplierDbl;

            // handle integer max
            while (multiplierRoundedLong > int.MaxValue)
            {
                CalendarOpPlusMinus.ActionSafeOverflow(work, int.MaxValue, timePeriod);
                factor += int.MaxValue;
                multiplierRoundedLong -= int.MaxValue;
                if (DEBUG && Log.IsDebugEnabled)
                {
                    Log.Debug("Work date is " + work.DateTime.Print() + " factor " + factor);
                }
            }

            // add
            int multiplierRoundedInt = (int)multiplierRoundedLong;

            CalendarOpPlusMinus.ActionSafeOverflow(work, multiplierRoundedInt, timePeriod);
            factor += multiplierRoundedInt;

            // if below, add more
            if (work.TimeInMillis <= currentTime)
            {
                while (work.TimeInMillis <= currentTime)
                {
                    CalendarOpPlusMinus.ActionSafeOverflow(work, 1, timePeriod);
                    factor += 1;
                    if (DEBUG && Log.IsDebugEnabled)
                    {
                        Log.Debug("Work date is " + work.DateTime.Print() + " factor " + factor);
                    }
                }
                return(new CalendarOpPlusFastAddResult(factor, work.DateTime));
            }

            // we are over
            while (work.TimeInMillis > currentTime)
            {
                CalendarOpPlusMinus.ActionSafeOverflow(work, -1, timePeriod);
                factor -= 1;
                if (DEBUG && Log.IsDebugEnabled)
                {
                    Log.Debug("Work date is " + work.DateTime.Print() + " factor " + factor);
                }
            }
            CalendarOpPlusMinus.ActionSafeOverflow(work, 1, timePeriod);
            if (DEBUG && Log.IsDebugEnabled)
            {
                Log.Debug("Work date is " + work.DateTime.Print() + " factor " + factor);
            }
            return(new CalendarOpPlusFastAddResult(factor + 1, work.DateTime));
        }
        public static CalendarOpPlusFastAddResult ComputeNextDue(
            long currentTime,
            TimePeriod timePeriod,
            DateTimeEx reference,
            TimeAbacus timeAbacus,
            long remainder)
        {
            if (timeAbacus.CalendarGet(reference, remainder) > currentTime)
            {
                return new CalendarOpPlusFastAddResult(0, reference);
            }

            // add one time period
            DateTimeEx work = reference.Clone();
            if (DEBUG && Log.IsDebugEnabled)
            {
                Log.Debug("Work date is " + work);
            }

            CalendarOpPlusMinus.ActionSafeOverflow(work, 1, timePeriod);
            long inMillis = timeAbacus.CalendarGet(work, remainder);
            if (inMillis > currentTime)
            {
                return new CalendarOpPlusFastAddResult(1, work);
            }
            if (DEBUG && Log.IsDebugEnabled)
            {
                Log.Debug("Work date is {0}", work);
            }

            long factor = 1;

            // determine multiplier
            long refTime = timeAbacus.CalendarGet(reference, remainder);
            long deltaCurrentToStart = currentTime - refTime;
            long deltaAddedOne = timeAbacus.CalendarGet(work, remainder) - refTime;
            double multiplierDbl = (deltaCurrentToStart/deltaAddedOne) - 1;
            var multiplierRoundedLong = (long) multiplierDbl;

            // handle integer max
            while (multiplierRoundedLong > Int32.MaxValue)
            {
                CalendarOpPlusMinus.ActionSafeOverflow(work, Int32.MaxValue, timePeriod);
                factor += Int32.MaxValue;
                multiplierRoundedLong -= Int32.MaxValue;
                if (DEBUG && Log.IsDebugEnabled)
                {
                    Log.Debug("Work date is {0} factor {1}", work, factor);
                }
            }

            // add
            var multiplierRoundedInt = (int) multiplierRoundedLong;
            CalendarOpPlusMinus.ActionSafeOverflow(work, multiplierRoundedInt, timePeriod);
            factor += multiplierRoundedInt;

            // if below, add more
            if (timeAbacus.CalendarGet(work, remainder) <= currentTime)
            {
                while (timeAbacus.CalendarGet(work, remainder) <= currentTime)
                {
                    CalendarOpPlusMinus.ActionSafeOverflow(work, 1, timePeriod);
                    factor += 1;
                    if (DEBUG && Log.IsDebugEnabled)
                    {
                        Log.Debug("Work date is {0} factor {1}", work, factor);
                    }
                }
                return new CalendarOpPlusFastAddResult(factor, work);
            }

            // we are over
            while (timeAbacus.CalendarGet(work, remainder) > currentTime)
            {
                CalendarOpPlusMinus.ActionSafeOverflow(work, -1, timePeriod);
                factor -= 1;
                if (DEBUG && Log.IsDebugEnabled)
                {
                    Log.Debug("Work date is {0} factor {1}", work, factor);
                }
            }
            CalendarOpPlusMinus.ActionSafeOverflow(work, 1, timePeriod);
            if (DEBUG && Log.IsDebugEnabled)
            {
                Log.Debug("Work date is {0} factor {1}", work, factor);
            }
            return new CalendarOpPlusFastAddResult(factor + 1, work);
        }