HalfOpenInterval.h

// HalfOpenInterval.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007

#pragma once
#ifndef Ceda_cxUtils_HalfOpenInterval_H
#define Ceda_cxUtils_HalfOpenInterval_H

#include "Archive.h"
#include "xostream.h"
#include "CedaAssert.h"
#include <algorithm>

namespace ceda
{
///////////////////////////////////////////////////////////////////////////////////////////////////
// HalfOpenInterval<T>

template <typename T>
struct HalfOpenInterval
{
    typedef T value_type;
    
    HalfOpenInterval() : t1(0), t2(0) {}
    HalfOpenInterval(T _s1, T _s2) : t1(_s1), t2(_s2) {}

    // Shift interval
    HalfOpenInterval<T>& operator+=(T offset) { t1 += offset; t2 += offset; return *this; }
    HalfOpenInterval<T>& operator-=(T offset) { t1 -= offset; t2 -= offset; return *this; }

    const HalfOpenInterval<T> operator+(T offset) const { return HalfOpenInterval<T>(t1 + offset, t2 + offset); }
    const HalfOpenInterval<T> operator-(T offset) const { return HalfOpenInterval<T>(t1 - offset, t2 - offset); }

    // Note that empty intervals don't necessarily compare equal. The recommended approach is
    // for empty intervals to be recorded in normal form with t1 = t2 = 0.
    bool operator==(const HalfOpenInterval& rhs) const { return t1 == rhs.t1 && t2 == rhs.t2; }
    bool operator!=(const HalfOpenInterval& rhs) const { return t1 != rhs.t1 || t2 != rhs.t2; }
    
    // Returns true if this interval is before (and possibly adjacent to) rhs
    bool Before(const HalfOpenInterval& rhs) const
    {
        cxAssert(!empty());
        cxAssert(!rhs.empty());
        return t2 <= rhs.t1;
    }

    // Returns true if this interval is after (and possibly adjacent to) rhs
    bool After(const HalfOpenInterval& rhs) const
    {
        cxAssert(!empty());
        cxAssert(!rhs.empty());
        return t1 >= rhs.t2;
    }
    
    // Returns true if there is a non-empty gap between the two non-empty intervals.
    bool HasGap(const HalfOpenInterval& rhs) const
    {
        cxAssert(!empty());
        cxAssert(!rhs.empty());
        return t2 < rhs.t1 || t1 > rhs.t2;
    }

    // Returns true if this interval intersects the given interval.  Assumes that 
    // both the intervals are non-empty
    bool DoesIntersect(const HalfOpenInterval& rhs) const
    {
        cxAssert(!empty());
        cxAssert(!rhs.empty());
        return t2 > rhs.t1 && rhs.t2 > t1;
    }

    // Returns true if this interval contains the given value
    bool Contains(T t) const { return t1 <= t && t < t2; }

    // Returns true if this interval contains the given interval.  Assumes that 
    // both the given intervals are non-empty
    bool Contains(const HalfOpenInterval& rhs) const
    {
        cxAssert(!empty());
        cxAssert(!rhs.empty());
        return t1 <= rhs.t1 && rhs.t2 <= t2;
    }

    // Returns true if this interval is empty
    bool empty() const { return t1 >= t2; }

    T size() const { return t2 - t1; }
    
    T begin() const { return t1; }
    T end() const { return t2; }
    void setbegin(T x) { t1 = x; }
    void setend(T x) { t2 = x; }
    
    void ScalarMultiply(T f)
    {
        t1 *= f;
        t2 *= f;
    }

    /////////////////// State //////////////////

    // Represents the half open interval [t1,t2).  
    // The representation is normalised if either t1 < t2 or else t1 = t2 = 0.
    T t1, t2;
};

// Return h1 intersect h2
template <typename T>
HalfOpenInterval<T> Intersect(const HalfOpenInterval<T>& h1, const HalfOpenInterval<T>& h2)
{
    return HalfOpenInterval<T>(std::max(h1.t1, h2.t1), std::min(h1.t2, h2.t2));
}

// Return range of h1, h2 which equals h1 union h2 if h1,h2 overlap or are adjacent (i.e.
// there is no gap between them).
template <typename T>
HalfOpenInterval<T> GetRange(const HalfOpenInterval<T>& h1, const HalfOpenInterval<T>& h2)
{
    return HalfOpenInterval<T>(std::min(h1.t1, h2.t1), std::max(h1.t2, h2.t2));
}

template <typename Archive, typename T> 
inline void Serialise(Archive& ar, const HalfOpenInterval<T>& i)
{
    ar << i.t1 << i.t2;
}

template <typename Archive, typename T> 
inline void Deserialise(Archive& ar, HalfOpenInterval<T>& i)
{
    ar >> i.t1 >> i.t2;
}

template <typename T>
xostream& operator<<(xostream& os, const HalfOpenInterval<T>& i)
{
    os << '[' << i.t1 << ',' << i.t2 << ')';
    return os;
}

} // namespace ceda

#endif // include guard