interval_sequence.hpp

Back to Interval sequences

pastel/sys/

#ifndef PASTELSYS_INTERVAL_SEQUENCE_HPP
#define PASTELSYS_INTERVAL_SEQUENCE_HPP

#include "pastel/sys/interval_sequence.h"
#include "pastel/sys/math/number_tests.h"

namespace Pastel
{

    template <
        typename A_Range, 
        typename B_Range,
        typename Output>
    void difference(
        const A_Range& aSet,
        const B_Range& bSet,
        Output report)
    {
        PENSURE(isIntervalSequence(aSet));
        PENSURE(isIntervalSequence(bSet));

        auto a = boost::begin(aSet);
        auto aEnd = boost::end(aSet);

        if (a == aEnd)
        {
            // Since A is empty, there is nothing to report.
            return;
        }

        auto b = boost::begin(bSet);
        auto bEnd = boost::end(bSet);

        integer t = *a;
        while(a != aEnd)
        {
            // Skip over those intervals in A which
            // have already been reported.
            while(a != aEnd && *std::next(a) <= t)
            {                
                std::advance(a, 2);
            }

            if (a == aEnd)
            {
                // No more intervals in A left.
                // There is nothing more to report.
                break;
            }

            auto aNext = std::next(a);

            if (t < *a)
            {
                t = *a;
            }

            ASSERT_OP(*a, <=, t)
            ASSERT_OP(t, <, *aNext);

            // Skip over those intervals in B
            // which precede the current 
            // time position t.
            while(b != bEnd && *std::next(b) <= t)
            {
                std::advance(b, 2);
            }

            if (b == bEnd)
            {
                // No more intervals in B left.
                // We may simply report all the
                // intervals left in A.
                break;
            }

            auto bNext = std::next(b);

            bool bActive = (*b <= t);
            ASSERT_OP(t, <, *bNext);

            integer tNext = t;
            if (bActive)
            {
                // Both interval A and interval B are active.
                // Skip over; no reporting.
                if (*aNext < *bNext)
                {
                    // Interval A ends first.
                    tNext = *aNext;
                }
                else
                {
                    // Interval B ends first.
                    tNext = *bNext;
                }
            }
            else
            {
                // Interval A is active, interval B is not active.
                // Report a fragment of interval A.
                tNext = std::min(*aNext, *b);

                report(std::make_pair(t, tNext));
            }

            t = tNext;
        }

        if (a != aEnd && t < *std::next(a))
        {
            // Report a possible remaining fragment.
            report(std::make_pair(t, *std::next(a)));

            std::advance(a, 2);
        }

        while (a != aEnd)
        {
            // Report every non-empty interval left in A.
            auto aNext = std::next(a);
            if (*a  < *aNext)
            {
                report(std::make_pair(*a, *aNext));
            }

            std::advance(a, 2);
        }
    }

    template <typename A_Range>
    bool isIntervalSequence(
        const A_Range& aSet)
    {
        auto a = boost::begin(aSet);
        auto aEnd = boost::end(aSet);

        while(a != aEnd)
        {
            auto aNext = std::next(a);
            if (aNext == aEnd)
            {
                // The sequence has an odd number of elements.
                return false;
            }

            if (*a > *aNext)
            {
                // The sequence is not non-decreasing.
                return false;
            }

            std::advance(a, 2);
        }

        return true;
    }

}

#endif