// The code is open source under the MIT license.
// Copyright 2019-2020, Phillip Keldenich, TU Braunschweig, Algorithms Group
// https://ibr.cs.tu-bs.de/alg
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

#include <chrono>
#include <thread>
#include "ivarp/prover/cuboid_queue.hpp"
#include "ivarp/bound_propagation.hpp"
#include "ivarp/splitter.hpp"
#include <boost/range/iterator_range.hpp>

namespace ivarp {
namespace impl {
    template<typename NumberType, std::size_t NumArgs> struct ProofDriverQueueEntry {
        IVARP_DEFAULT_CM(ProofDriverQueueEntry);

        ProofDriverQueueEntry() = default;
        explicit IVARP_HD ProofDriverQueueEntry(const Array<NumberType,NumArgs>& a) : depth(0) {
            for(std::size_t i = 0; i < NumArgs; ++i) {
                elements[i] = a[i];
            }
        }

        NumberType elements[NumArgs];
        std::size_t depth;
    };

    struct CuboidCounts {
        std::size_t num_cuboids{0};
        std::size_t num_leaf_cuboids{0};
        std::size_t num_critical_cuboids{0};
        std::size_t num_repeated_nodes{0};

        IVARP_HD CuboidCounts &operator+=(const CuboidCounts& c) noexcept {
            num_cuboids += c.num_cuboids;
            num_leaf_cuboids += c.num_leaf_cuboids;
            num_critical_cuboids += c.num_critical_cuboids;
            num_repeated_nodes += c.num_repeated_nodes;
            return *this;
        }

#if defined(__CUDA_ARCH__)
        IVARP_D void gpu_atomic_add(const CuboidCounts& c) noexcept {
			ivarp::gpu_atomic_add(&num_cuboids, c.num_cuboids);
			ivarp::gpu_atomic_add(&num_leaf_cuboids, c.num_leaf_cuboids);
			ivarp::gpu_atomic_add(&num_critical_cuboids, c.num_critical_cuboids);
			ivarp::gpu_atomic_add(&num_repeated_nodes, c.num_repeated_nodes);
        }
#endif
    };

    static inline IVARP_HD ProofInformation& operator+=(ProofInformation& p, const CuboidCounts& c) noexcept {
        p.num_cuboids += c.num_cuboids;
        p.num_leaf_cuboids += c.num_leaf_cuboids;
        p.num_critical_cuboids += c.num_critical_cuboids;
        p.num_repeated_nodes += c.num_repeated_nodes;
        return p;
    }

    /**
     * An object that captures the state of the proof in a proof driver.
     */
    class ProofDriverState {
    public:
        /**
         * Create a new ProofDriverState in its initial state.
         *
         * @param num_threads
         * @param queue_mutex
         */
        ProofDriverState(std::size_t num_threads, std::mutex* queue_mutex) noexcept :
            mutex(queue_mutex), num_threads(num_threads)
        {}

        /**
         * Re-throws the error, if there was any, on the thread calling this method.
         * Does not have to take a lock.
         */
        void reraise_error_if_any() const {
            if(have_error.load() == 2) {
                std::rethrow_exception(error);
            }
        }

        /**
         * Set an error. The error is only ever set once.
         * @param error The error to set.
         * @param error_thread The thread ID raising the error.
         */
        void set_error(std::exception_ptr error, std::size_t error_thread) noexcept {
            int expected = 0;
            if(have_error.compare_exchange_strong(expected, 1)) {
                this->error = error;
                this->error_thread = error_thread;
                have_error.store(2);
                state_changed.notify_all();
            }
        }

        /**
         * Check whether there is an error. Does not need to lock.
         * @return
         */
        bool error_is_set() const noexcept {
            return have_error.load() == 2;
        }

        std::size_t get_error_thread() const noexcept {
            return error_thread;
        }

        /**
         * Declare that the thread calling this method is done with proof initialization.
         */
        void init_done() noexcept {
            std::unique_lock<std::mutex> lock(*mutex);
            if(++threads_done_init >= num_threads) {
                state_changed.notify_all();
            }
        }

        /**
         * Wait for all threads to be done with proof initialization.
         * Reraises the error if any is set.
         */
        void wait_for_init() const {
            std::unique_lock<std::mutex> lock(*mutex);
            state_changed.wait(lock, [&]() -> bool {
                return error_is_set() || threads_done_init >= num_threads;
            });
            reraise_error_if_any();
        }

        /**
         * Declare that queue compaction is done.
         */
        void queue_compaction_done() noexcept {
            std::unique_lock<std::mutex> lock(*mutex);
            compaction_done = true;
            state_changed.notify_all();
        }

        /**
         * Wait for queue compaction to complete.
         * Re-raises the error if any is set.
         */
         void wait_for_queue_compaction() const {
            std::unique_lock<std::mutex> lock(*mutex);
            state_changed.wait(lock, [&]() -> bool {
                return error_is_set() || compaction_done;
            });
            reraise_error_if_any();
         }

         /**
          * Declare that the proof is done. Only called by the main thread.
          */
         void declare_proof_done() noexcept {
            std::unique_lock<std::mutex> lock(*mutex);
            proof_done = true;
            state_changed.notify_all();
         }

         /**
          * The method used by the progress observer to wait for:
          *   * a specific time limit to occur,
          *   * an error being set,
          *   * the proof being done.
          * Uses an external lock. Returns true if the proof was done in time.
          * Re-raises the error if any is set.
          */
         template<typename TimePoint>
            bool wait_until_done(std::unique_lock<std::mutex>& lock, TimePoint wait_until)
         {
            bool status = state_changed.wait_until(lock, wait_until, [&] () -> bool {
                return error_is_set() || proof_done;
            });

            if(!status) {
                return false;
            }
            reraise_error_if_any();
            return true;
         }

    private:
        std::mutex* mutex; ///< The mutex to use for waiting and other situations where locking is necessary.
        mutable std::condition_variable state_changed; ///< A condition variable to wait for state changes.
        std::size_t num_threads; ///< The number of threads running the proof.
        std::size_t threads_done_init{0}; ///< The number of threads done with proof initialization.
        std::atomic<int> have_error{0}; ///< Whether we have encountered an exception on any thread. 0: no error, 1: error is being set, 2: error is set.
        std::exception_ptr error; ///< The error, if any.
        std::size_t error_thread; ///< The thread that set the error.
        bool compaction_done{false}; ///< Whether queue compaction after initialization is done.
        bool proof_done{false}; ///< Whether the proof is done.
    };

    template<typename ProverInputType, typename CoreProver, typename OnCritical, typename ProgressReporter>
    class ProofDriver
    {
    private:
        // Number of variables and args.
        static constexpr std::size_t num_args = ProverInputType::num_args;
        static constexpr std::size_t num_vars = ProverInputType::num_vars;
        static constexpr std::size_t initial_queue_size = ProverInputType::initial_queue_size;

        // Prover input types
        using Context = typename ProverInputType::Context;
        using DynamicSplitInfo = typename ProverInputType::DynamicSplitInfo;
        using StaticSplitInfo = typename ProverInputType::StaticSplitInfo;
        using RBT = typename ProverInputType::RuntimeBoundTable;
        using RCT = typename ProverInputType::RuntimeConstraintTable;

        // Propagation information
        using DBA = DynamicBoundApplication<RBT, Context>;

        // Number-type and queue related types
        using NumberType = typename Context::NumberType;
        using QueueElement = ProofDriverQueueEntry<NumberType, num_args>;
        using QueueType = CuboidQueue<QueueElement, true, CuboidQueueOrder::LIFO>;
        using Lock = std::unique_lock<std::mutex>;

        struct ProofDriverThread {
            ProofDriverThread(ProofDriver* driver, std::size_t id) :
                driver(driver),
                id(id),
                handle()
            {
                dequeue_buffer.reserve(driver->settings.dequeue_buffer_size);
            }

            ProofDriverThread(const ProofDriverThread&) = delete;
            ProofDriverThread &operator=(const ProofDriverThread&) = delete;

            ProofDriverThread(ProofDriverThread&& o) noexcept :
                driver(o.driver),
                id(o.id),
                handle(std::move(o.handle)),
                dequeue_buffer(std::move(o.dequeue_buffer)),
                result_buffer(std::move(o.result_buffer))
            {}

            ProofDriverThread &operator=(ProofDriverThread&& o) noexcept {
                driver = o.driver;
                id = o.id;
                handle = std::move(o.handle);
                dequeue_buffer.swap(o.dequeue_buffer);
                result_buffer.swap(o.result_buffer);
                return *this;
            }

            void join() {
                if(id != 0 && handle.joinable()) {
                    handle.join();
                }
            }

            ~ProofDriverThread() {
                if(handle.joinable()) {
                    handle.join();
                }
            }

            void entry_point() noexcept {
                try {
                    proof_init();
                    driver->state.init_done();
                    driver->state.wait_for_queue_compaction();
                    proof_main();
                } catch(...) {
                    std::exception_ptr cexp = std::current_exception();
                    driver->state.set_error(cexp, id);
                }
            }

            void spawn() {
                assert(!handle.joinable());
                handle = std::thread(&ProofDriverThread::entry_point, this);
                assert(handle.joinable());
            }

            void proof_init() {
                proof_init_begin(driver->initial_cuboid, DynamicSplitInfo{});
            }

            static constexpr auto rec_limit = static_cast<std::uint8_t>(ivarp::min(num_args,255));
            static constexpr auto it_limit = 2*num_args;

            void proof_init_set_to_empty(std::size_t count, std::size_t* offset) const noexcept {
                // mark as empty by making the first interval empty; all empty entries will be compacted
                // after the initial phase to allow parallel initialization
                for(std::size_t j = 0; j < count; ++j) {
                    QueueElement& qe = driver->queue[*offset];
                    qe.elements[0].set_lb(1);
                    qe.elements[0].set_ub(0);
                    *offset += 1;
                }
            }

            template<typename S1, typename... Splits>
                void proof_init_begin(const Array<NumberType,num_args>& initial, const SplitInfoSequence<S1,Splits...>&)
            {
                using BoundEv = BoundEvent<S1::arg, BoundID::BOTH>;
                const auto& rbt = driver->rbt;
                const auto& dba = driver->dba;
                const auto num_threads = static_cast<std::size_t>(driver->settings.thread_count);
                constexpr std::size_t s1s = S1::initial;
                constexpr std::size_t rem_initial = initial_queue_size / s1s;

                const std::size_t s1s_per_thread = ivarp::max(s1s / num_threads, 1);
                if(id >= s1s) {
                    return;
                }

                const std::size_t beg = id * s1s_per_thread;
                const std::size_t end = (id != num_threads-1) ? (id+1) * s1s_per_thread : s1s;
                Splitter<NumberType> splitter(initial[S1::arg], static_cast<int>(s1s));
                std::size_t offset = beg * rem_initial;
                for(int i = static_cast<int>(beg); i < static_cast<int>(end); ++i) {
                    ++counts.num_cuboids;
                    QueueElement qsub{initial};
                    qsub.elements[S1::arg] = splitter.subrange(i);
                    if(propagate_iterated_recursive::propagate<BoundEv,Context>(rbt, qsub.elements, dba,
                                                                                it_limit, rec_limit).empty)
                    {
                        ++counts.num_leaf_cuboids;
                        proof_init_set_to_empty(rem_initial, &offset);
                    } else {
                        proof_init_continue<rem_initial>(qsub, &offset, SplitInfoSequence<Splits...>{});
                    }
                }
            }

            template<std::size_t EntriesPerElement, typename SN, typename... Splits>
            void proof_init_continue(const QueueElement& e, std::size_t* offset, const SplitInfoSequence<SN,Splits...>&)
            {
                using BoundEv = BoundEvent<SN::arg, BoundID::BOTH>;
                const auto& rbt = driver->rbt;
                const auto& dba = driver->dba;
                constexpr std::size_t sns = SN::initial;
                constexpr std::size_t next_epe = EntriesPerElement / sns;
                Splitter<NumberType> splitter(e.elements[SN::arg], static_cast<int>(sns));
                for(NumberType sub : splitter) {
                    ++counts.num_cuboids;
                    QueueElement qsub{e};
                    qsub.elements[SN::arg] = sub;
                    if(propagate_iterated_recursive::propagate<BoundEv, Context>(rbt, qsub.elements, dba,
                                                                                it_limit, rec_limit).empty)
                    {
                        ++counts.num_leaf_cuboids;
                        proof_init_set_to_empty(next_epe, offset);
                    } else {
                        proof_init_continue<next_epe>(qsub, offset, SplitInfoSequence<Splits...>{});
                    }
                }
            }

            template<std::size_t EntriesPerElement>
            void proof_init_continue(const QueueElement& e, std::size_t* offset, const SplitInfoSequence<>&) {
                static_assert(EntriesPerElement == 1, "Something is wrong with the initial queue size!");
                QueueElement& qe = driver->queue[*offset];
                *offset += 1;

                if(!driver->satisfies_all_constraints(e)) {
                    qe.elements[0].set_lb(1);
                    qe.elements[0].set_ub(0);
                    ++counts.num_leaf_cuboids;
                } else {
                    qe = e;
                }
            }

            void proof_main() {
                driver->core->initialize_per_thread(id, static_cast<std::size_t>(driver->settings.thread_count));
                while(refill_buffer()) {
                    driver->state.reraise_error_if_any();
                    counts += driver->core->handle_cuboids_nonfinal(id, dequeue_buffer, &result_buffer);
                    dequeue_buffer.clear();
                    if(!result_buffer.empty()) {
                        handle_core_result();
                    }
                }
            }

            bool refill_buffer() {
                driver->queue.pop_into(dequeue_buffer, driver->settings.dequeue_buffer_size);
                return !dequeue_buffer.empty();
            }

            void handle_core_result() {
                std::vector<QueueElement> final_elements;
                for(const QueueElement& q : result_buffer) {
                    if(q.depth >= driver->settings.generation_count) {
                        final_elements.push_back(q);
                    } else {
                        dequeue_buffer.push_back(q);
                    }
                }
                result_buffer.clear();

                if(!final_elements.empty()) {
                    counts += driver->core->handle_cuboids_final(id, final_elements);
                }
                if(!dequeue_buffer.empty()) {
                    split_and_requeue();
                }
            }

            void split_and_requeue() {
                for(const QueueElement& e : dequeue_buffer) {
                    split_into(e, result_buffer);
                }
                dequeue_buffer.clear();
                if(!result_buffer.empty()) {
                    driver->queue.enqueue_bulk(result_buffer.begin(), result_buffer.end());
                }
                result_buffer.clear();
            }

            struct SplitIntoImpl;
            void split_into(QueueElement e, std::vector<QueueElement>& output);

            ProofDriver* driver;
            std::size_t id;
            std::thread handle;
            std::vector<QueueElement> dequeue_buffer, result_buffer;
            CuboidCounts counts;
        };

    public:
        explicit ProofDriver(ProverInputType input, const OnCritical* on_c,
                             ProgressReporter* rep, ProofInformation* info,
                             CoreProver* core, ProverSettings s) noexcept :
            rbt(ivarp::move(input.runtime_bounds)),
            dba(&rbt),
            rct(ivarp::move(input.runtime_constraints)),
            on_critical(on_c), reporter(rep),
            information(info), core(core), settings(ivarp::move(s)),
            initial_cuboid(ivarp::move(input.initial_runtime_bounds)),
            queue(initial_queue_size, static_cast<unsigned>(settings.thread_count)),
            state(static_cast<std::size_t>(settings.thread_count), &queue.mutex())
        {
            core->set_runtime_bounds(&rbt);
            core->set_runtime_constraints(&rct);
            core->set_dynamic_bound_application(&dba);
            core->set_on_critical(on_critical);
            core->set_settings(&settings);
            core->initialize(static_cast<std::size_t>(settings.thread_count));
            p_init_threads();
        }

        bool run() {
            try {
                p_start_progress();
                p_spawn_threads();
                threads.front().proof_init();
                p_compact_initial_queue();
                threads.front().proof_main();
                p_join_threads();
                state.declare_proof_done();
            } catch(...) {
                state.set_error(std::current_exception(), 0);
                p_join_threads();
            }

            progress_observer.join();
            state.reraise_error_if_any();
            return information->num_critical_cuboids == 0;
        }

        bool satisfies_all_constraints(const QueueElement& e) const {
            return satisfies_all_constraints(e.elements);
        }

        template<typename ArrayType>
        bool satisfies_all_constraints(const ArrayType& values) const {
            const auto& all_c = ProverInputType::all_constraints(rct);
            bool all_sat = true;
            auto visitor = [&] (const auto& constr) -> TupleVisitationControl {
                auto cres = constr.template array_evaluate<Context>(values);
                if(!possibly(cres)) {
                    all_sat = false;
                    return TupleVisitationControl::STOP;
                }
                return TupleVisitationControl::CONTINUE;
            };
            visit_tuple(visitor, all_c);
            return all_sat;
        }

    private:
        void p_spawn_threads() {
            for(ProofDriverThread& t : boost::make_iterator_range(threads.begin() + 1, threads.end())) {
                t.spawn();
            }
        }

        void p_compact_initial_queue() {
            state.init_done();
            state.wait_for_init();
            {
                Lock lock(queue.mutex());
                auto b = queue.begin();
                auto e = queue.end();
                auto new_end = std::remove_if(b, e, [] (const auto& e) { return e.elements[0].empty(); });
                queue.unlocked_erase(new_end, e);
            }
            state.queue_compaction_done();
        }

        void p_join_threads() {
            information->num_critical_cuboids = 0;
            information->num_cuboids = 0;
            information->num_leaf_cuboids = 0;
            information->num_repeated_nodes = 0;
            for(ProofDriverThread& t : threads) {
                t.join();
                *information += t.counts;
            }
        }

        void p_init_threads() {
            const auto nt = static_cast<std::size_t>(settings.thread_count);
            threads.reserve(nt);
            for(std::size_t i = 0; i < nt; ++i) {
                threads.emplace_back(this, i);
            }
        }

        // Progress observation.
        void p_start_progress() {
            progress_observer = std::thread(&ProofDriver::p_progress_main, this);
        }

        void p_progress_main() noexcept {
            try {
                auto begun_at = std::chrono::steady_clock::now();
                auto begin_progress_at = begun_at + settings.start_progress_after;

                // wait for initialization & compaction
                state.wait_for_queue_compaction();

                // take lock
                Lock lock{queue.mutex()};
                if(state.wait_until_done(lock, begin_progress_at)) {
                    p_call_progress_observer_done();
                    return;
                }

                for(;;) {
                    p_call_progress_observer();
                    auto next_progress_at = std::chrono::steady_clock::now() + settings.progress_every;
                    if(state.wait_until_done(lock, next_progress_at)) {
                        p_call_progress_observer_done();
                        return;
                    }
                }
            } catch(...) {
                p_call_progress_observer_error(std::current_exception(), state.get_error_thread());
            }
        }

        void p_call_progress_observer() {
            ProgressInfo info;
            info.queue_size = queue.unlocked_size();
            for(const ProofDriverThread& t : threads) {
                info.leaf_count += t.counts.num_leaf_cuboids;
                info.critical_count += t.counts.num_critical_cuboids;
                info.cuboid_count += t.counts.num_cuboids;
            }
            QueueElement element;
            if(queue.unlocked_peek(element)) {
                reporter->observe_progress(Context{}, element, info);
            }
        }

        void p_call_progress_observer_done() {
            reporter->observe_done();
        }

        void p_call_progress_observer_error(std::exception_ptr error, std::size_t error_thread_id) {
            reporter->observe_error(error, error_thread_id);
        }

        // Progress observer thread.
        std::thread progress_observer;

        // Immutable information, such as settings, bounds/constraints.
        const RBT rbt;
        const DBA dba;
        const RCT rct;
        const OnCritical* const on_critical;
        ProgressReporter* const reporter;
        ProofInformation* const information;
        CoreProver* const core;
        ProverSettings const settings;
        Array<NumberType, num_args> const initial_cuboid;

        // The queue and per-thread information.
        QueueType queue;

        // The global state of the proof, i.e., what stage is running, etc.
        ProofDriverState state;

        // The threads running the proof, along with their state.
        std::vector<ProofDriverThread> threads;
    };
}
}

#include "pd_impl_split_into.hpp"


//////////////////////////////////////////////////////////////////////////////////
// MIT License
//
// Copyright (c) 2017 Vicon Motion Systems Ltd
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//////////////////////////////////////////////////////////////////////////////////

#include "ClientUtils.h"

#include <boost/asio.hpp>
#include <boost/format.hpp>
#include <boost/array.hpp>

#include <numeric>

namespace ClientUtils
{
  // These functions clear a value
  void Clear( bool & o_rValue )
  {
    o_rValue = false;
  }

  void Clear( unsigned int & o_rValue )
  {
    o_rValue = 0;
  }

  void Clear( double & o_rValue )
  {
    o_rValue = 0.0;
  }

  void Clear( std::string & o_rValue )
  {
    o_rValue.clear();
  }

  void Clear( ViconDataStreamSDK::Core::TimecodeStandard::Enum & o_rValue )
  {
    o_rValue = ViconDataStreamSDK::Core::TimecodeStandard::None;
  }

  void Clear( ViconDataStreamSDK::Core::DeviceType::Enum & o_rValue )
  {
    o_rValue = ViconDataStreamSDK::Core::DeviceType::Unknown;
  }

  void Clear( ViconDataStreamSDK::Core::Unit::Enum & o_rValue )
  {
    o_rValue = ViconDataStreamSDK::Core::Unit::Unknown;
  }

  // Determine whether an axis is positive or negative
  double ComponentSign( ViconDataStreamSDK::Core::Direction::Enum i_Direction )
  {
    switch( i_Direction )
    {
    case ViconDataStreamSDK::Core::Direction::Forward :
    case ViconDataStreamSDK::Core::Direction::Left    :
    case ViconDataStreamSDK::Core::Direction::Up      : return 1.0;
    case ViconDataStreamSDK::Core::Direction::Backward:
    case ViconDataStreamSDK::Core::Direction::Right   :
    case ViconDataStreamSDK::Core::Direction::Down    : return -1.0;
    default                                           : assert( !"Unknown enum value" );
                                                        return 0.0;
    }
  }

  // Determine the index of an enum
  unsigned int ComponentIndex( ViconDataStreamSDK::Core::Direction::Enum i_Direction )
  {
    switch( i_Direction )
    {
    case ViconDataStreamSDK::Core::Direction::Forward :
    case ViconDataStreamSDK::Core::Direction::Backward: return 0;
    case ViconDataStreamSDK::Core::Direction::Left    :
    case ViconDataStreamSDK::Core::Direction::Right   : return 1;
    case ViconDataStreamSDK::Core::Direction::Up      :
    case ViconDataStreamSDK::Core::Direction::Down    : return 2;
    default                                           : assert( !"Unknown enum value" );
                                                        return 0;
    }
  }

  std::string ComponentName( ViconDataStreamSDK::Core::Direction::Enum i_Direction )
  {
    switch( i_Direction )
    {
    case ViconDataStreamSDK::Core::Direction::Forward: return "Forward";
    case ViconDataStreamSDK::Core::Direction::Backward: return "Backward";
    case ViconDataStreamSDK::Core::Direction::Left: return "Left";
    case ViconDataStreamSDK::Core::Direction::Right: return "Right";
    case ViconDataStreamSDK::Core::Direction::Up: return "Up";
    case ViconDataStreamSDK::Core::Direction::Down: return "Down";
    default: assert( !"Unknown enum value" );
      return "";
    }

  }

  std::string AdaptCameraName( const std::string & i_rCameraName,
                               const std::string & i_rDisplayType,
                               const unsigned int  i_CameraID )
  {
    // Append Camera info to ensure uniqueness, since this name is used as keys in the DSSDK Camera APIs
    return str( boost::format( "%s (%d)" ) %( i_rCameraName.empty()? i_rDisplayType : i_rCameraName ) %i_CameraID );
  }

  std::string AdaptDeviceName( const std::string & i_rDeviceName,
                               const unsigned int  i_DeviceID )
  {
    if( !i_rDeviceName.empty() )
    {
      return i_rDeviceName;
    }
    else
    {
      return str( boost::format( "Unnamed Device %d" ) % i_DeviceID );
    }
  }

  std::string AdaptDeviceOutputName( const std::string & i_rDeviceOutputName,
                                     const unsigned int  i_DeviceOutputIndex )
  {
    if( !i_rDeviceOutputName.empty() )
    {
      return i_rDeviceOutputName;
    }
    else
    {
      return str( boost::format( "Unnamed Device Output %d" ) % ( i_DeviceOutputIndex + 1 ) );
    }
  }

  bool IsValidMulticastIP( const std::string & i_MulticastIPAddress )
  {
    boost::asio::io_service Service;
    boost::system::error_code Error;
    boost::asio::ip::address_v4 Address = boost::asio::ip::address_v4::from_string( i_MulticastIPAddress, Error );
    if( Error )
    {
      boost::asio::ip::tcp::resolver Resolver( Service );
      boost::asio::ip::tcp::resolver::query Query( i_MulticastIPAddress, "" );

      boost::asio::ip::tcp::resolver::iterator It = Resolver.resolve( Query, Error );
      boost::asio::ip::tcp::resolver::iterator End;

      if( ! Error )
      {
        for( ; It != End; ++It )
        {
          Error = boost::system::error_code();
          boost::asio::ip::tcp::endpoint EndPoint( *It );

          // Currently we only handle IPv4
          if( EndPoint.address().is_v4() )
          {
            Address = EndPoint.address().to_v4();
            break;
          }
        }
      }
      else
      {
        Address = boost::asio::ip::address_v4();
      }
    }

    return( Address.is_multicast() || ( Address.to_ulong() == 0xFFFFFFFF ) );
  }

  void MatrixToQuaternion( const double i_M[9], double (&o_Q)[4])
  {
    // Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
    // article "Quaternion Calculus and Fast Animation".

    double Trace = i_M[0*3+0]+i_M[1*3+1]+i_M[2*3+2];
    double Root;

    if( Trace > 0 )
    {
      // |w| > 1/2, may as well choose w > 1/2
      Root = std::sqrt(Trace + 1.0);  // 2w
      o_Q[3] = 0.5*Root;
      Root = 0.5/Root;  // 1/(4w)
      o_Q[0] = (i_M[2*3+1]-i_M[1*3+2])*Root;
      o_Q[1] = (i_M[0*3+2]-i_M[2*3+0])*Root;
      o_Q[2] = (i_M[1*3+0]-i_M[0*3+1])*Root;
    }
    else
    {
      // |w| <= 1/2
      int s_Next[3] = { 1, 2, 0 };
      int i = 0;
      if ( i_M[1*3+1] > i_M[0*3+0] )
          i = 1;
      if ( i_M[2*3+2] > i_M[i*3+i] )
          i = 2;
      int j = s_Next[i];
      int k = s_Next[j];

      Root = std::sqrt(i_M[i*3+i]-i_M[j*3+j]-i_M[k*3+k] + 1.0);

      o_Q[i] = 0.5*Root;
      Root = 0.5/Root;
      o_Q[3] = (i_M[k*3+j]-i_M[j*3+k])*Root;
      o_Q[j] = (i_M[j*3+i]+i_M[i*3+j])*Root;
      o_Q[k] = (i_M[k*3+i]+i_M[i*3+k])*Root;
    }

    // Normalize
    const double InnerProduct = std::inner_product( o_Q, o_Q + 4, o_Q, 0.0 );
    const double Magnitude = std::sqrt( InnerProduct );

    o_Q[0] /= Magnitude;
    o_Q[1] /= Magnitude;
    o_Q[2] /= Magnitude;
    o_Q[3] /= Magnitude;
  }

  void MatrixToHelical( const double i_rM[9], double (&o_rAA)[3])
  {
    double Q[4];
    MatrixToQuaternion( i_rM, Q );

    const double Real = Q[3];

    double Len = sqrt( std::inner_product( Q, Q+3, Q, 0.0 ) );
    if( Len > std::numeric_limits< double >::epsilon() * 10 )
    {
      const double Angle = 2.0 * atan2( Len, Real );
      const double Scale = Angle / Len;
      std::transform( Q, Q+3, o_rAA, [ Scale ]( double X ){ return Scale * X; } );
    }
    else
    {
      std::copy( Q, Q+3, o_rAA );
    }

  }

  void MatrixToEulerXYZ( const double i_M[9], double (&o_rE)[3])
  {
    //  This original algorithm is the XYZ Euler order and is
    //  the default input order argument, so no existing code
    //  should need changing or will give different results

    //  Algorithm: GraphicsGems II - Matrix Techniques VII.1 p 320

    o_rE[1] = asin(i_M[0*3+2]);

    if( fabs( cos(o_rE[1]) ) > std::numeric_limits< double >::epsilon() * 10 )
    {
      o_rE[0] = std::atan2(-i_M[1*3+2], i_M[2*3+2]);
      o_rE[2] = std::atan2(-i_M[0*3+1], i_M[0*3+0]);
    }
    else
    {
      // cos(y) ~= 0 Gimbal-Lock
      o_rE[0] = (o_rE[1] > 0) ? std::atan2(i_M[1*3+0], i_M[1*3+1]) : -std::atan2(i_M[0*3+1], i_M[1*3+1]);
      o_rE[2] = 0;
    }
  }

    boost::array< double, 3 > operator*( const boost::array< double, 9 > & i_rM, const boost::array< double, 3 > & i_rX )
  {
    boost::array< double, 3 > Result;

    for( size_t i = 0; i < 3; i++)
    {
      double Sum = 0;
      for( unsigned k = 0; k < 3; k++ )
      {
        Sum += i_rM[i*3+k] * i_rX[k];
      }
      Result[i] = Sum;
    }

  return Result;
  }

  boost::array< double, 3 > & operator/=( boost::array< double, 3 > & i_rX, double i_Val )
  {
    std::transform( i_rX.begin(), i_rX.end(), i_rX.begin(), [ i_Val ]( double x ){ return x / i_Val; } );
    return i_rX;
  }

  boost::array< double, 3 > & operator*=( boost::array< double, 3 > & i_rX, double i_Val )
  {
    std::transform( i_rX.begin(), i_rX.end(), i_rX.begin(), [ i_Val ]( double x ){ return x * i_Val; } );
    return i_rX;
  }

  boost::array< double, 3 > operator+( const boost::array< double, 3 > & i_rX, const boost::array< double, 3 > & i_rY )
  {
    boost::array< double, 3 > Result;
    std::transform( i_rX.begin(), i_rX.end(), i_rY.begin(), Result.begin(), []( double x, double y ){ return x + y; } );
    return Result;
  }

  boost::array< double, 3 > operator-( const boost::array< double, 3 > & i_rX, const boost::array< double, 3 > & i_rY )
  {
    boost::array< double, 3 > Result;
    std::transform( i_rX.begin(), i_rX.end(), i_rY.begin(), Result.begin(), []( double x, double y ){ return x - y; } );
    return Result;
  }

  boost::array< double, 9 > operator*( const boost::array< double, 9 > & i_rM, const boost::array< double, 9 > & i_rN )
  {
    boost::array< double, 9 > Result;

    for( size_t i = 0; i < 3; i++)
    {
      for( size_t j = 0; j < 3; j++)
      {
        double Sum = 0;
        for( unsigned k = 0; k < 3; k++ )
        {
          Sum += i_rM[i*3+k] * i_rN[k*3+j];
        }
        Result[i*3+j] = Sum;
      }
    }

    return Result;
  }

  boost::array< double, 9 > Transpose( const boost::array< double, 9 > & i_rM )
  {
    boost::array< double, 9 > Result;

    for( size_t i = 0; i < 3; i++)
    {
      for( size_t j = 0; j < 3; j++)
      {
        Result[i*3+j] = i_rM[j*3+i];
      }
    }

    return Result;
  }

  boost::array< double, 16 > Transpose( const boost::array< double, 16 > & i_rM )
  {
    boost::array< double, 16 > Result;

    for( size_t i = 0; i < 4; i++)
    {
      for( size_t j = 0; j < 4; j++)
      {
        Result[i*4+j] = i_rM[j*4+i];
      }
    }

    return Result;
  }

  boost::array< double, 3 > CrossProduct( boost::array< double, 3 > & i_rA, boost::array< double, 3 > & i_rB )
  {
    boost::array< double, 3 > Result;
    Result[0] = i_rA[1] * i_rB[2] - i_rA[2] * i_rB[1];
    Result[1] = i_rA[2] * i_rB[0] - i_rA[0] * i_rB[2];
    Result[2] = i_rA[0] * i_rB[1] - i_rA[1] * i_rB[0];
    return Result;
  }

  double DotProduct( boost::array<double, 3> & i_rA, boost::array<double, 3> & i_rB )
  {
    double Result = 0;
    for( unsigned i = 0; i < 3; ++i )
    {
      Result += i_rA[i] * i_rB[i];
    }
    return Result;
  }
}


/*-----------------------------------------------------------------------------+
Copyright (c) 2010-2010: Joachim Faulhaber
+------------------------------------------------------------------------------+
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENCE.txt or copy at
           http://www.boost.org/LICENSE_1_0.txt)
+-----------------------------------------------------------------------------*/
#ifndef BOOST_ICL_CONCEPT_INTERVAL_HPP_JOFA_100323
#define BOOST_ICL_CONCEPT_INTERVAL_HPP_JOFA_100323

#include <boost/assert.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/or.hpp>
#include <boost/mpl/not.hpp>
#include <boost/icl/detail/design_config.hpp>
#include <boost/icl/type_traits/unit_element.hpp>
#include <boost/icl/type_traits/identity_element.hpp>
#include <boost/icl/type_traits/infinity.hpp>
#include <boost/icl/type_traits/succ_pred.hpp>
#include <boost/icl/type_traits/is_numeric.hpp>
#include <boost/icl/type_traits/is_discrete.hpp>
#include <boost/icl/type_traits/is_continuous.hpp>
#include <boost/icl/type_traits/is_asymmetric_interval.hpp>
#include <boost/icl/type_traits/is_discrete_interval.hpp>
#include <boost/icl/type_traits/is_continuous_interval.hpp>

#include <boost/icl/concept/interval_bounds.hpp>
#include <boost/icl/interval_traits.hpp>
#include <boost/icl/dynamic_interval_traits.hpp>

#include <algorithm>

namespace boost{namespace icl
{

//==============================================================================
//= Ordering
//==============================================================================
template<class Type>
inline typename enable_if<is_interval<Type>, bool>::type
domain_less(const typename interval_traits<Type>::domain_type& left,
            const typename interval_traits<Type>::domain_type& right)
{
    return typename interval_traits<Type>::domain_compare()(left, right);
}

template<class Type>
inline typename enable_if<is_interval<Type>, bool>::type
domain_less_equal(const typename interval_traits<Type>::domain_type& left,
                  const typename interval_traits<Type>::domain_type& right)
{
    return !(typename interval_traits<Type>::domain_compare()(right, left));
}

template<class Type>
inline typename enable_if<is_interval<Type>, bool>::type
domain_equal(const typename interval_traits<Type>::domain_type& left,
             const typename interval_traits<Type>::domain_type& right)
{
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    return !(domain_compare()(left, right)) && !(domain_compare()(right, left));
}

template<class Type>
inline typename enable_if< is_interval<Type>
                         , typename interval_traits<Type>::domain_type>::type
domain_next(const typename interval_traits<Type>::domain_type value)
{
    typedef typename interval_traits<Type>::domain_type domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    return icl::successor<domain_type,domain_compare>::apply(value);
}

template<class Type>
inline typename enable_if< is_interval<Type>
                         , typename interval_traits<Type>::domain_type>::type
domain_prior(const typename interval_traits<Type>::domain_type value)
{
    typedef typename interval_traits<Type>::domain_type domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    return icl::predecessor<domain_type,domain_compare>::apply(value);
}

//==============================================================================
//= Construct<Interval> singleton
//==============================================================================
template<class Type>
typename enable_if
<
    mpl::and_< is_static_right_open<Type>
             , is_discrete<typename interval_traits<Type>::domain_type> >
  , Type
>::type
singleton(const typename interval_traits<Type>::domain_type& value)
{
    //ASSERT: This always creates an interval with exactly one element
    return interval_traits<Type>::construct(value, domain_next<Type>(value));
}

template<class Type>
typename enable_if
<
    mpl::and_< is_static_left_open<Type>
             , is_discrete<typename interval_traits<Type>::domain_type> >
  , Type
>::type
singleton(const typename interval_traits<Type>::domain_type& value)
{
    //ASSERT: This always creates an interval with exactly one element
    typedef typename interval_traits<Type>::domain_type    domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                 ::is_less_than(value) ));

    return interval_traits<Type>::construct(domain_prior<Type>(value), value);
}

template<class Type>
typename enable_if<is_discrete_static_open<Type>, Type>::type
singleton(const typename interval_traits<Type>::domain_type& value)
{
    //ASSERT: This always creates an interval with exactly one element
    typedef typename interval_traits<Type>::domain_type    domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                 ::is_less_than(value)));

    return interval_traits<Type>::construct( domain_prior<Type>(value)
                                           , domain_next<Type>(value));
}

template<class Type>
typename enable_if<is_discrete_static_closed<Type>, Type>::type
singleton(const typename interval_traits<Type>::domain_type& value)
{
    //ASSERT: This always creates an interval with exactly one element
    return interval_traits<Type>::construct(value, value);
}

template<class Type>
typename enable_if<has_dynamic_bounds<Type>, Type>::type
singleton(const typename interval_traits<Type>::domain_type& value)
{
    return dynamic_interval_traits<Type>::construct(value, value, interval_bounds::closed());
}

namespace detail
{

//==============================================================================
//= Construct<Interval> unit_trail == generalized singleton
// The smallest interval on an incrementable (and decrementable) type that can
// be constructed using ++ and -- and such that it contains a given value.
// If 'Type' is discrete, 'unit_trail' and 'singleton' are identical. So we
// can view 'unit_trail' as a generalized singleton for static intervals of
// continuous types.
//==============================================================================
template<class Type>
typename enable_if
<
    mpl::and_< is_static_right_open<Type>
             , boost::detail::is_incrementable<typename interval_traits<Type>::domain_type> >
  , Type
>::type
unit_trail(const typename interval_traits<Type>::domain_type& value)
{
    return interval_traits<Type>::construct(value, domain_next<Type>(value));
}

template<class Type>
typename enable_if
<
    mpl::and_< is_static_left_open<Type>
             , boost::detail::is_incrementable<typename interval_traits<Type>::domain_type> >
  , Type
>::type
unit_trail(const typename interval_traits<Type>::domain_type& value)
{
    typedef typename interval_traits<Type>::domain_type    domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                 ::is_less_than(value) ));

    return interval_traits<Type>::construct(domain_prior<Type>(value), value);
}

template<class Type>
typename enable_if
<
    mpl::and_< is_static_open<Type>
             , is_discrete<typename interval_traits<Type>::domain_type> >
  , Type
>::type
unit_trail(const typename interval_traits<Type>::domain_type& value)
{
    typedef typename interval_traits<Type>::domain_type    domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                 ::is_less_than(value)));

    return interval_traits<Type>::construct( domain_prior<Type>(value)
                                           ,  domain_next<Type>(value));
}

template<class Type>
typename enable_if
<
    mpl::and_< is_static_closed<Type>
             , is_discrete<typename interval_traits<Type>::domain_type> >
  , Type
>::type
unit_trail(const typename interval_traits<Type>::domain_type& value)
{
    return interval_traits<Type>::construct(value, value);
}

//NOTE: statically bounded closed or open intervals of continuous domain types
// are NOT supported by ICL. They can not be used with interval containers
// consistently.

template<class Type>
typename enable_if<has_dynamic_bounds<Type>, Type>::type
unit_trail(const typename interval_traits<Type>::domain_type& value)
{
    return dynamic_interval_traits<Type>::construct(value, value, interval_bounds::closed());
}

} //namespace detail

//==============================================================================
//= Construct<Interval> multon
//==============================================================================
template<class Type>
typename enable_if<has_static_bounds<Type>, Type>::type
construct(const typename interval_traits<Type>::domain_type& low,
          const typename interval_traits<Type>::domain_type& up  )
{
    return interval_traits<Type>::construct(low, up);
}

template<class Type>
typename enable_if<has_dynamic_bounds<Type>, Type>::type
construct(const typename interval_traits<Type>::domain_type& low,
          const typename interval_traits<Type>::domain_type& up,
          interval_bounds bounds = interval_bounds::right_open())
{
    return dynamic_interval_traits<Type>::construct(low, up, bounds);
}

//- construct form bounded values ----------------------------------------------
template<class Type>
typename enable_if<has_dynamic_bounds<Type>, Type>::type
construct(const typename Type::bounded_domain_type& low,
          const typename Type::bounded_domain_type& up)
{
    return dynamic_interval_traits<Type>::construct_bounded(low, up);
}

template<class Type>
typename enable_if<is_interval<Type>, Type>::type
span(const typename interval_traits<Type>::domain_type& left,
     const typename interval_traits<Type>::domain_type& right)
{
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    if(domain_compare()(left,right))
        return construct<Type>(left, right);
    else
        return construct<Type>(right, left);
}

//==============================================================================
template<class Type>
typename enable_if<is_static_right_open<Type>, Type>::type
hull(const typename interval_traits<Type>::domain_type& left,
     const typename interval_traits<Type>::domain_type& right)
{
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    if(domain_compare()(left,right))
        return construct<Type>(left, domain_next<Type>(right));
    else
        return construct<Type>(right, domain_next<Type>(left));
}

template<class Type>
typename enable_if<is_static_left_open<Type>, Type>::type
hull(const typename interval_traits<Type>::domain_type& left,
     const typename interval_traits<Type>::domain_type& right)
{
    typedef typename interval_traits<Type>::domain_type    domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    if(domain_compare()(left,right))
    {
        BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                     ::is_less_than(left) ));
        return construct<Type>(domain_prior<Type>(left), right);
    }
    else
    {
        BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                     ::is_less_than(right) ));
        return construct<Type>(domain_prior<Type>(right), left);
    }
}

template<class Type>
typename enable_if<is_static_closed<Type>, Type>::type
hull(const typename interval_traits<Type>::domain_type& left,
     const typename interval_traits<Type>::domain_type& right)
{
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    if(domain_compare()(left,right))
        return construct<Type>(left, right);
    else
        return construct<Type>(right, left);
}

template<class Type>
typename enable_if<is_static_open<Type>, Type>::type
hull(const typename interval_traits<Type>::domain_type& left,
     const typename interval_traits<Type>::domain_type& right)
{
    typedef typename interval_traits<Type>::domain_type    domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    if(domain_compare()(left,right))
    {
        BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                     ::is_less_than(left) ));
        return construct<Type>( domain_prior<Type>(left)
                              ,  domain_next<Type>(right));
    }
    else
    {
        BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                     ::is_less_than(right) ));
        return construct<Type>( domain_prior<Type>(right)
                              ,  domain_next<Type>(left));
    }
}

template<class Type>
typename enable_if<has_dynamic_bounds<Type>, Type>::type
hull(const typename interval_traits<Type>::domain_type& left,
     const typename interval_traits<Type>::domain_type& right)
{
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    if(domain_compare()(left,right))
        return construct<Type>(left, right, interval_bounds::closed());
    else
        return construct<Type>(right, left, interval_bounds::closed());
}

//==============================================================================
//= Selection
//==============================================================================

template<class Type>
inline typename enable_if<is_interval<Type>,
                          typename interval_traits<Type>::domain_type>::type
lower(const Type& object)
{
    return interval_traits<Type>::lower(object);
}

template<class Type>
inline typename enable_if<is_interval<Type>,
                          typename interval_traits<Type>::domain_type>::type
upper(const Type& object)
{
    return interval_traits<Type>::upper(object);
}

//- first ----------------------------------------------------------------------
template<class Type>
inline typename
enable_if< mpl::or_<is_static_right_open<Type>, is_static_closed<Type> >
         , typename interval_traits<Type>::domain_type>::type
first(const Type& object)
{
    return lower(object);
}

template<class Type>
inline typename
enable_if< mpl::and_< mpl::or_<is_static_left_open<Type>, is_static_open<Type> >
                    , is_discrete<typename interval_traits<Type>::domain_type> >
         , typename interval_traits<Type>::domain_type>::type
first(const Type& object)
{
    return domain_next<Type>(lower(object));
}

template<class Type>
inline typename enable_if<is_discrete_interval<Type>,
                          typename interval_traits<Type>::domain_type>::type
first(const Type& object)
{
    return is_left_closed(object.bounds()) ?
                                 lower(object) :
               domain_next<Type>(lower(object));
}

//- last -----------------------------------------------------------------------
template<class Type>
inline typename
enable_if< mpl::or_<is_static_left_open<Type>, is_static_closed<Type> >
         , typename interval_traits<Type>::domain_type>::type
last(const Type& object)
{
    return upper(object);
}

template<class Type>
inline typename
enable_if< mpl::and_< mpl::or_<is_static_right_open<Type>, is_static_open<Type> >
                    , is_discrete<typename interval_traits<Type>::domain_type>  >
         , typename interval_traits<Type>::domain_type>::type
last(const Type& object)
{
    typedef typename interval_traits<Type>::domain_type    domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                 ::is_less_than(upper(object)) ));
    return domain_prior<Type>(upper(object));
}

template<class Type>
inline typename enable_if<is_discrete_interval<Type>,
                          typename interval_traits<Type>::domain_type>::type
last(const Type& object)
{
    typedef typename interval_traits<Type>::domain_type    domain_type;
    typedef typename interval_traits<Type>::domain_compare domain_compare;
    BOOST_ASSERT((numeric_minimum<domain_type, domain_compare, is_numeric<domain_type>::value>
                                 ::is_less_than_or(upper(object), is_right_closed(object.bounds())) ));
    return is_right_closed(object.bounds()) ?
                                  upper(object) :
               domain_prior<Type>(upper(object));
}

//- last_next ------------------------------------------------------------------
template<class Type>
inline typename
enable_if< mpl::and_< mpl::or_<is_static_left_open<Type>, is_static_closed<Type> >
                    , is_discrete<typename interval_traits<Type>::domain_type>  >
         , typename interval_traits<Type>::domain_type>::type
last_next(const Type& object)
{
    return domain_next<Type>(upper(object));
}

template<class Type>
inline typename
enable_if< mpl::and_< mpl::or_<is_static_right_open<Type>, is_static_open<Type> >
                    , is_discrete<typename interval_traits<Type>::domain_type>  >
         , typename interval_traits<Type>::domain_type>::type
last_next(const Type& object)
{
    //CL typedef typename interval_traits<Type>::domain_type domain_type;
    return upper(object); // NOTE: last_next is implemented to avoid calling pred(object)
}                         // For unsigned integral types this may cause underflow.

template<class Type>
inline typename enable_if<is_discrete_interval<Type>,
                          typename interval_traits<Type>::domain_type>::type
last_next(const Type& object)
{
    return is_right_closed(object.bounds()) ?
               domain_next<Type>(upper(object)):
                                 upper(object) ;
}

//------------------------------------------------------------------------------
template<class Type>
typename enable_if<has_dynamic_bounds<Type>,
                   typename Type::bounded_domain_type>::type
bounded_lower(const Type& object)
{
    return typename
        Type::bounded_domain_type(lower(object), object.bounds().left());
}

template<class Type>
typename enable_if<has_dynamic_bounds<Type>,
                   typename Type::bounded_domain_type>::type
reverse_bounded_lower(const Type& object)
{
    return typename
        Type::bounded_domain_type(lower(object),
                                  object.bounds().reverse_left());
}

template<class Type>
typename enable_if<has_dynamic_bounds<Type>,
                   typename Type::bounded_domain_type>::type
bounded_upper(const Type& object)
{
    return typename
        Type::bounded_domain_type(upper(object),
                                  object.bounds().right());
}

template<class Type>
typename enable_if<has_dynamic_bounds<Type>,
                   typename Type::bounded_domain_type>::type
reverse_bounded_upper(const Type& object)
{
    return typename
        Type::bounded_domain_type(upper(object),
                                  object.bounds().reverse_right());
}

//- bounds ---------------------------------------------------------------------
template<class Type>
inline typename enable_if<has_dynamic_bounds<Type>, interval_bounds>::type
bounds(const Type& object)
{
    return object.bounds();
}

template<class Type>
inline typename enable_if<has_static_bounds<Type>, interval_bounds>::type
bounds(const Type&)
{
    return interval_bounds(interval_bound_type<Type>::value);
}

//==============================================================================
//= Emptieness
//==============================================================================
/** Is the interval empty? */
template<class Type>
typename boost::enable_if<is_asymmetric_interval<Type>, bool>::type
is_empty(const Type& object)
{
    return domain_less_equal<Type>(upper(object), lower(object));
}

template<class Type>
typename boost::enable_if<is_static_closed<Type>, bool>::type
is_empty(const Type& object)
{
    return domain_less<Type>(upper(object), lower(object));
}

template<class Type>
typename boost::enable_if<is_static_open<Type>, bool>::type
is_empty(const Type& object)
{
    return domain_less_equal<Type>(upper(object),                   lower(object) )
        || domain_less_equal<Type>(upper(object), domain_next<Type>(lower(object)));
}

template<class Type>
typename boost::enable_if<is_discrete_interval<Type>, bool>::type
is_empty(const Type& object)
{
    if(object.bounds() == interval_bounds::closed())
        return domain_less<Type>(upper(object), lower(object));
    else if(object.bounds() == interval_bounds::open())
        return domain_less_equal<Type>(upper(object),                   lower(object) )
            || domain_less_equal<Type>(upper(object), domain_next<Type>(lower(object)));
    else
        return domain_less_equal<Type>(upper(object), lower(object));
}

template<class Type>
typename boost::enable_if<is_continuous_interval<Type>, bool>::type
is_empty(const Type& object)
{
    return     domain_less<Type>(upper(object), lower(object))
        || (   domain_equal<Type>(upper(object), lower(object))
            && object.bounds() != interval_bounds::closed()    );
}

//==============================================================================
//= Equivalences and Orderings
//==============================================================================
//- exclusive_less -------------------------------------------------------------
/** Maximal element of <tt>left</tt> is less than the minimal element of
    <tt>right</tt> */
template<class Type>
inline typename boost::enable_if<is_asymmetric_interval<Type>, bool>::type
exclusive_less(const Type& left, const Type& right)
{
    return icl::is_empty(left) || icl::is_empty(right)
        || domain_less_equal<Type>(upper(left), lower(right));
}

template<class Type>
inline typename boost::enable_if<is_discrete_interval<Type>, bool>::type
exclusive_less(const Type& left, const Type& right)
{
    return icl::is_empty(left) || icl::is_empty(right)
        || domain_less<Type>(last(left), first(right));
}

template<class Type>
inline typename boost::
enable_if<has_symmetric_bounds<Type>, bool>::type
exclusive_less(const Type& left, const Type& right)
{
    return icl::is_empty(left) || icl::is_empty(right)
        || domain_less<Type>(last(left), first(right));
}

template<class Type>
inline typename boost::enable_if<is_continuous_interval<Type>, bool>::type
exclusive_less(const Type& left, const Type& right)
{
    return     icl::is_empty(left) || icl::is_empty(right)
        ||     domain_less<Type>(upper(left), lower(right))
        || (   domain_equal<Type>(upper(left), lower(right))
            && inner_bounds(left,right) != interval_bounds::open() );
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<has_static_bounds<Type>, bool>::type
lower_less(const Type& left, const Type& right)
{
    return domain_less<Type>(lower(left), lower(right));
}

template<class Type>
typename boost::enable_if<is_discrete_interval<Type>, bool>::type
lower_less(const Type& left, const Type& right)
{
    return domain_less<Type>(first(left), first(right));
}

template<class Type>
typename boost::enable_if<is_continuous_interval<Type>, bool>::type
lower_less(const Type& left, const Type& right)
{
    if(left_bounds(left,right) == interval_bounds::right_open())  //'[(' == 10
        return domain_less_equal<Type>(lower(left), lower(right));
    else
        return domain_less<Type>(lower(left), lower(right));
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<has_static_bounds<Type>, bool>::type
upper_less(const Type& left, const Type& right)
{
    return domain_less<Type>(upper(left), upper(right));
}

template<class Type>
typename boost::enable_if<is_discrete_interval<Type>, bool>::type
upper_less(const Type& left, const Type& right)
{
    return domain_less<Type>(last(left), last(right));
}

template<class Type>
typename boost::enable_if<is_continuous_interval<Type>, bool>::type
upper_less(const Type& left, const Type& right)
{
    if(right_bounds(left,right) == interval_bounds::left_open())
        return domain_less_equal<Type>(upper(left), upper(right));
    else
        return domain_less<Type>(upper(left), upper(right));
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>,
                          typename Type::bounded_domain_type   >::type
lower_min(const Type& left, const Type& right)
{
    return lower_less(left, right) ? bounded_lower(left) : bounded_lower(right);
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>,
                          typename Type::bounded_domain_type   >::type
lower_max(const Type& left, const Type& right)
{
    return lower_less(left, right) ? bounded_lower(right) : bounded_lower(left);
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>,
                          typename Type::bounded_domain_type   >::type
upper_max(const Type& left, const Type& right)
{
    return upper_less(left, right) ? bounded_upper(right) : bounded_upper(left);
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>,
                          typename Type::bounded_domain_type   >::type
upper_min(const Type& left, const Type& right)
{
    return upper_less(left, right) ? bounded_upper(left) : bounded_upper(right);
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<is_asymmetric_interval<Type>, bool>::type
lower_equal(const Type& left, const Type& right)
{
    return domain_equal<Type>(lower(left), lower(right));
}

template<class Type>
typename boost::enable_if<has_symmetric_bounds<Type>, bool>::type
lower_equal(const Type& left, const Type& right)
{
    return domain_equal<Type>(first(left), first(right));
}

template<class Type>
typename boost::enable_if<is_discrete_interval<Type>, bool>::type
lower_equal(const Type& left, const Type& right)
{
    return domain_equal<Type>(first(left), first(right));
}

template<class Type>
typename boost::enable_if<is_continuous_interval<Type>, bool>::type
lower_equal(const Type& left, const Type& right)
{
    return (left.bounds().left()==right.bounds().left())
        && domain_equal<Type>(lower(left), lower(right));
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<is_asymmetric_interval<Type>, bool>::type
upper_equal(const Type& left, const Type& right)
{
    return domain_equal<Type>(upper(left), upper(right));
}

template<class Type>
typename boost::enable_if<has_symmetric_bounds<Type>, bool>::type
upper_equal(const Type& left, const Type& right)
{
    return domain_equal<Type>(last(left), last(right));
}

template<class Type>
typename boost::enable_if<is_discrete_interval<Type>, bool>::type
upper_equal(const Type& left, const Type& right)
{
    return domain_equal<Type>(last(left), last(right));
}

template<class Type>
typename boost::enable_if<is_continuous_interval<Type>, bool>::type
upper_equal(const Type& left, const Type& right)
{
    return (left.bounds().right()==right.bounds().right())
        && domain_equal<Type>(upper(left), upper(right));
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<is_interval<Type>, bool>::type
lower_less_equal(const Type& left, const Type& right)
{
    return lower_less(left,right) || lower_equal(left,right);
}

template<class Type>
typename boost::enable_if<is_interval<Type>, bool>::type
upper_less_equal(const Type& left, const Type& right)
{
    return upper_less(left,right) || upper_equal(left,right);
}

//==============================================================================
//= Orderings, containedness (non empty)
//==============================================================================
namespace non_empty
{

    template<class Type>
    inline typename boost::enable_if<is_asymmetric_interval<Type>, bool>::type
    exclusive_less(const Type& left, const Type& right)
    {
        BOOST_ASSERT(!(icl::is_empty(left) || icl::is_empty(right)));
        return domain_less_equal<Type>(upper(left), lower(right));
    }

    template<class Type>
    inline typename boost::enable_if<is_discrete_interval<Type>, bool>::type
    exclusive_less(const Type& left, const Type& right)
    {
        BOOST_ASSERT(!(icl::is_empty(left) || icl::is_empty(right)));
        return domain_less<Type>(last(left), first(right));
    }

    template<class Type>
    inline typename boost::
    enable_if<has_symmetric_bounds<Type>, bool>::type
    exclusive_less(const Type& left, const Type& right)
    {
        BOOST_ASSERT(!(icl::is_empty(left) || icl::is_empty(right)));
        return domain_less<Type>(last(left), first(right));
    }

    template<class Type>
    inline typename boost::enable_if<is_continuous_interval<Type>, bool>::type
    exclusive_less(const Type& left, const Type& right)
    {
        BOOST_ASSERT(!(icl::is_empty(left) || icl::is_empty(right)));
        return     domain_less <Type>(upper(left), lower(right))
            || (   domain_equal<Type>(upper(left), lower(right))
                && inner_bounds(left,right) != interval_bounds::open() );
    }

    template<class Type>
    inline typename boost::enable_if<is_interval<Type>, bool>::type
    contains(const Type& super, const Type& sub)
    {
        return lower_less_equal(super,sub) && upper_less_equal(sub,super);
    }

} //namespace non_empty

//- contains -------------------------------------------------------------------
template<class Type>
inline typename boost::enable_if<is_interval<Type>, bool>::type
contains(const Type& super, const Type& sub)
{
    return icl::is_empty(sub) || non_empty::contains(super, sub);
}

template<class Type>
typename boost::enable_if<is_discrete_static<Type>, bool>::type
contains(const Type& super, const typename interval_traits<Type>::domain_type& element)
{
    return domain_less_equal<Type>(icl::first(super), element                  )
        && domain_less_equal<Type>(                   element, icl::last(super));
}

template<class Type>
typename boost::enable_if<is_continuous_left_open<Type>, bool>::type
contains(const Type& super, const typename interval_traits<Type>::domain_type& element)
{
    return domain_less      <Type>(icl::lower(super), element                   )
        && domain_less_equal<Type>(                   element, icl::upper(super));
}

template<class Type>
typename boost::enable_if<is_continuous_right_open<Type>, bool>::type
contains(const Type& super, const typename interval_traits<Type>::domain_type& element)
{
    return domain_less_equal<Type>(icl::lower(super), element                   )
        && domain_less      <Type>(                   element, icl::upper(super));
}

template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>, bool>::type
contains(const Type& super, const typename interval_traits<Type>::domain_type& element)
{
    return
        (is_left_closed(super.bounds())
            ? domain_less_equal<Type>(lower(super), element)
            :       domain_less<Type>(lower(super), element))
    &&
        (is_right_closed(super.bounds())
            ? domain_less_equal<Type>(element, upper(super))
            :       domain_less<Type>(element, upper(super)));
}

//- within ---------------------------------------------------------------------
template<class Type>
inline typename boost::enable_if<is_interval<Type>, bool>::type
within(const Type& sub, const Type& super)
{
    return contains(super,sub);
}

//- operator == ----------------------------------------------------------------
template<class Type>
typename boost::enable_if<is_interval<Type>, bool>::type
operator == (const Type& left, const Type& right)
{
    return (icl::is_empty(left) && icl::is_empty(right))
        || (lower_equal(left,right) && upper_equal(left,right));
}

template<class Type>
typename boost::enable_if<is_interval<Type>, bool>::type
operator != (const Type& left, const Type& right)
{
    return !(left == right);
}

//- operator < -----------------------------------------------------------------
template<class Type>
typename boost::enable_if<is_interval<Type>, bool>::type
operator < (const Type& left, const Type& right)
{
    if(icl::is_empty(left))
        return !icl::is_empty(right);
    else
        return lower_less(left,right)
            || (lower_equal(left,right) && upper_less(left,right));
}

template<class Type>
inline typename boost::enable_if<is_interval<Type>, bool>::type
operator > (const Type& left, const Type& right)
{
    return right < left;
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::enable_if<is_asymmetric_interval<Type>, bool>::type
touches(const Type& left, const Type& right)
{
    return domain_equal<Type>(upper(left), lower(right));
}

template<class Type>
typename boost::enable_if<has_symmetric_bounds<Type>, bool>::type
touches(const Type& left, const Type& right)
{
    return domain_equal<Type>(last_next(left), first(right));
}

template<class Type>
typename boost::enable_if<is_discrete_interval<Type>, bool>::type
touches(const Type& left, const Type& right)
{
    return domain_equal<Type>(domain_next<Type>(last(left)), first(right));
}

template<class Type>
typename boost::enable_if<is_continuous_interval<Type>, bool>::type
touches(const Type& left, const Type& right)
{
    return is_complementary(inner_bounds(left,right))
        && domain_equal<Type>(upper(left), lower(right));
}

//==============================================================================
//= Size
//==============================================================================
//- cardinality ----------------------------------------------------------------

template<class Type>
typename boost::enable_if<is_continuous_interval<Type>,
    typename size_type_of<interval_traits<Type> >::type>::type
cardinality(const Type& object)
{
    typedef typename size_type_of<interval_traits<Type> >::type SizeT;
    if(icl::is_empty(object))
        return icl::identity_element<SizeT>::value();
    else if(   object.bounds() == interval_bounds::closed()
            && domain_equal<Type>(lower(object), upper(object)))
        return icl::unit_element<SizeT>::value();
    else
        return icl::infinity<SizeT>::value();
}

template<class Type>
typename boost::enable_if<is_discrete_interval<Type>,
    typename size_type_of<interval_traits<Type> >::type>::type
cardinality(const Type& object)
{
    typedef typename size_type_of<interval_traits<Type> >::type SizeT;
    return icl::is_empty(object) ? identity_element<SizeT>::value()
                                 : static_cast<SizeT>(last_next(object) - first(object));
}

template<class Type>
typename boost::enable_if<is_continuous_asymmetric<Type>,
    typename size_type_of<interval_traits<Type> >::type>::type
cardinality(const Type& object)
{
    typedef typename size_type_of<interval_traits<Type> >::type SizeT;
    if(icl::is_empty(object))
        return icl::identity_element<SizeT>::value();
    else
        return icl::infinity<SizeT>::value();
}

template<class Type>
typename boost::enable_if<is_discrete_asymmetric<Type>,
    typename size_type_of<interval_traits<Type> >::type>::type
cardinality(const Type& object)
{
    typedef typename size_type_of<interval_traits<Type> >::type SizeT;
    return icl::is_empty(object) ? identity_element<SizeT>::value()
                                 : static_cast<SizeT>(last_next(object) - first(object));
}

template<class Type>
typename boost::enable_if<has_symmetric_bounds<Type>,
    typename size_type_of<interval_traits<Type> >::type>::type
cardinality(const Type& object)
{
    typedef typename size_type_of<interval_traits<Type> >::type SizeT;
    return icl::is_empty(object) ? identity_element<SizeT>::value()
                                 : static_cast<SizeT>(last_next(object) - first(object));
}

//- size -----------------------------------------------------------------------
template<class Type>
inline typename enable_if<is_interval<Type>,
    typename size_type_of<interval_traits<Type> >::type>::type
size(const Type& object)
{
    return cardinality(object);
}

//- length ---------------------------------------------------------------------
template<class Type>
inline typename boost::enable_if<is_continuous_interval<Type>,
    typename difference_type_of<interval_traits<Type> >::type>::type
length(const Type& object)
{
    typedef typename difference_type_of<interval_traits<Type> >::type DiffT;
    return icl::is_empty(object) ? identity_element<DiffT>::value()
                                 : upper(object) - lower(object);
}

template<class Type>
inline typename boost::enable_if<is_discrete_interval<Type>,
    typename difference_type_of<interval_traits<Type> >::type>::type
length(const Type& object)
{
    typedef typename difference_type_of<interval_traits<Type> >::type DiffT;
    return icl::is_empty(object) ? identity_element<DiffT>::value()
                                 : last_next(object) - first(object);
}

template<class Type>
typename boost::enable_if<is_continuous_asymmetric<Type>,
    typename difference_type_of<interval_traits<Type> >::type>::type
length(const Type& object)
{
    typedef typename difference_type_of<interval_traits<Type> >::type DiffT;
    return icl::is_empty(object) ? identity_element<DiffT>::value()
                                 : upper(object) - lower(object);
}

template<class Type>
inline typename boost::enable_if<is_discrete_static<Type>,
    typename difference_type_of<interval_traits<Type> >::type>::type
length(const Type& object)
{
    typedef typename difference_type_of<interval_traits<Type> >::type DiffT;
    return icl::is_empty(object) ? identity_element<DiffT>::value()
                                 : last_next(object) - first(object);
}

//- iterative_size -------------------------------------------------------------
template<class Type>
inline typename enable_if<is_interval<Type>,
    typename size_type_of<interval_traits<Type> >::type>::type
iterative_size(const Type&)
{
    return 2;
}

//==============================================================================
//= Addition
//==============================================================================
//- hull -----------------------------------------------------------------------
/** \c hull returns the smallest interval containing \c left and \c right. */
template<class Type>
typename boost::enable_if<has_static_bounds<Type>, Type>::type
hull(Type left, const Type& right)
{
    typedef typename interval_traits<Type>::domain_compare domain_compare;

    if(icl::is_empty(right))
        return left;
    else if(icl::is_empty(left))
        return right;

    return
        construct<Type>
        (
            (std::min)(lower(left), lower(right), domain_compare()),
            (std::max)(upper(left), upper(right), domain_compare())
        );
}

template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>, Type>::type
hull(Type left, const Type& right)
{
    if(icl::is_empty(right))
        return left;
    else if(icl::is_empty(left))
        return right;

    return  dynamic_interval_traits<Type>::construct_bounded
            (
                lower_min(left, right),
                upper_max(left, right)
            );
}

//==============================================================================
//= Subtraction
//==============================================================================
//- left_subtract --------------------------------------------------------------
/** subtract \c left_minuend from the \c right interval on it's left side.
    Return the difference: The part of \c right right of \c left_minuend.
\code
right_over = right - left_minuend; //on the left.
...      d) : right
... c)      : left_minuend
     [c  d) : right_over
\endcode
*/
template<class Type>
typename boost::enable_if<is_asymmetric_interval<Type>, Type>::type
left_subtract(Type right, const Type& left_minuend)
{
    if(exclusive_less(left_minuend, right))
        return right;

    return construct<Type>(upper(left_minuend), upper(right));
}

template<class Type>
typename boost::enable_if<is_static_closed<Type>, Type>::type
left_subtract(Type right, const Type& left_minuend)
{
    if(exclusive_less(left_minuend, right))
        return right;
    else if(upper_less_equal(right, left_minuend))
        return identity_element<Type>::value();

    return construct<Type>(domain_next<Type>(upper(left_minuend)), upper(right));
}

template<class Type>
typename boost::enable_if<is_static_open<Type>, Type>::type
left_subtract(Type right, const Type& left_minuend)
{
    if(exclusive_less(left_minuend, right))
        return right;

    return construct<Type>(domain_prior<Type>(upper(left_minuend)), upper(right));
}

template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>, Type>::type
left_subtract(Type right, const Type& left_minuend)
{
    if(exclusive_less(left_minuend, right))
        return right;
    return  dynamic_interval_traits<Type>::construct_bounded
            ( reverse_bounded_upper(left_minuend), bounded_upper(right) );
}

//- right_subtract -------------------------------------------------------------
/** subtract \c right_minuend from the \c left interval on it's right side.
    Return the difference: The part of \c left left of \c right_minuend.
\code
left_over = left - right_minuend; //on the right side.
[a      ...  : left
     [b ...  : right_minuend
[a  b)       : left_over
\endcode
*/
template<class Type>
typename boost::enable_if<is_asymmetric_interval<Type>, Type>::type
right_subtract(Type left, const Type& right_minuend)
{
    if(exclusive_less(left, right_minuend))
        return left;
    return construct<Type>(lower(left), lower(right_minuend));
}

template<class Type>
typename boost::enable_if<is_static_closed<Type>, Type>::type
right_subtract(Type left, const Type& right_minuend)
{
    if(exclusive_less(left, right_minuend))
        return left;
    else if(lower_less_equal(right_minuend, left))
        return identity_element<Type>::value();

    return construct<Type>(lower(left), domain_prior<Type>(lower(right_minuend)));
}

template<class Type>
typename boost::enable_if<is_static_open<Type>, Type>::type
right_subtract(Type left, const Type& right_minuend)
{
    if(exclusive_less(left, right_minuend))
        return left;

    return construct<Type>(lower(left), domain_next<Type>(lower(right_minuend)));
}

template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>, Type>::type
right_subtract(Type left, const Type& right_minuend)
{
    if(exclusive_less(left, right_minuend))
        return left;

    return  dynamic_interval_traits<Type>::construct_bounded
            ( bounded_lower(left), reverse_bounded_lower(right_minuend) );
}

//==============================================================================
//= Intersection
//==============================================================================
//- operator & -----------------------------------------------------------------
/** Returns the intersection of \c left and \c right interval. */
template<class Type>
typename boost::enable_if<is_asymmetric_interval<Type>, Type>::type
operator & (Type left, const Type& right)
{
    typedef typename interval_traits<Type>::domain_compare domain_compare;

    if(icl::is_empty(left) || icl::is_empty(right))
        return identity_element<Type>::value();
    else
        return
        construct<Type>
        (
            (std::max)(icl::lower(left), icl::lower(right), domain_compare()),
            (std::min)(icl::upper(left), icl::upper(right), domain_compare())
        );
}

template<class Type>
typename boost::enable_if<has_symmetric_bounds<Type>, Type>::type
operator & (Type left, const Type& right)
{
    typedef typename interval_traits<Type>::domain_compare domain_compare;

    if(icl::is_empty(left) || icl::is_empty(right))
        return identity_element<Type>::value();
    else
        return
        construct<Type>
        (
            (std::max)(icl::lower(left), icl::lower(right), domain_compare()),
            (std::min)(icl::upper(left), icl::upper(right), domain_compare())
        );
}

template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>, Type>::type
operator & (Type left, const Type& right)
{
    if(icl::is_empty(left) || icl::is_empty(right))
        return identity_element<Type>::value();
    else
        return  dynamic_interval_traits<Type>::construct_bounded
                (
                    lower_max(left, right),
                    upper_min(left, right)
                );
}

//- intersects -----------------------------------------------------------------
template<class Type>
typename boost::enable_if<is_interval<Type>, bool>::type
intersects(const Type& left, const Type& right)
{
    return !(   icl::is_empty(left) || icl::is_empty(right)
             || exclusive_less(left,right) || exclusive_less(right,left));
}

//- disjoint -------------------------------------------------------------------
template<class Type>
typename boost::enable_if<is_interval<Type>, bool>::type
disjoint(const Type& left, const Type& right)
{
    return icl::is_empty(left) || icl::is_empty(right)
        || exclusive_less(left,right) || exclusive_less(right,left);
}

//==============================================================================
//= Complement
//==============================================================================

template<class Type>
typename boost::enable_if<is_asymmetric_interval<Type>, Type>::type
inner_complement(const Type& left, const Type& right)
{
    if(icl::is_empty(left) || icl::is_empty(right))
        return  identity_element<Type>::value();
    else if(exclusive_less(left, right))
        return construct<Type>(upper(left), lower(right));
    else if(exclusive_less(right, left))
        return construct<Type>(upper(right), lower(left));
    else
        return identity_element<Type>::value();
}

template<class Type>
typename boost::enable_if<is_discrete_static_closed<Type>, Type>::type
inner_complement(const Type& left, const Type& right)
{
    if(icl::is_empty(left) || icl::is_empty(right))
        return  identity_element<Type>::value();
    else if(exclusive_less(left, right))
        return construct<Type>(domain_next<Type>(upper(left)), domain_prior<Type>(lower(right)));
    else if(exclusive_less(right, left))
        return construct<Type>(domain_next<Type>(upper(right)), domain_prior<Type>(lower(left)));
    else
        return identity_element<Type>::value();
}

template<class Type>
typename boost::enable_if<is_discrete_static_open<Type>, Type>::type
inner_complement(const Type& left, const Type& right)
{
    if(icl::is_empty(left) || icl::is_empty(right))
        return  identity_element<Type>::value();
    else if(exclusive_less(left, right))
        return construct<Type>(last(left), first(right));
    else if(exclusive_less(right, left))
        return construct<Type>(last(right), first(left));
    else
        return identity_element<Type>::value();
}

template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>, Type>::type
inner_complement(const Type& left, const Type& right)
{
    if(icl::is_empty(left) || icl::is_empty(right))
        return  identity_element<Type>::value();
    else if(exclusive_less(left, right))
        return right_subtract(left_subtract(hull(left, right), left), right);
    else if(exclusive_less(right, left))
        return right_subtract(left_subtract(hull(right, left), right), left);
    else
        return identity_element<Type>::value();
}

template<class Type>
inline typename boost::enable_if<is_interval<Type>, Type>::type
between(const Type& left, const Type& right)
{
    return inner_complement(left, right);
}

//==============================================================================
//= Distance
//==============================================================================
template<class Type>
typename boost::
enable_if< mpl::and_< is_interval<Type>
                    , has_difference<typename interval_traits<Type>::domain_type>
                    , is_discrete<typename interval_traits<Type>::domain_type>
                    >
         , typename difference_type_of<interval_traits<Type> >::type>::type
distance(const Type& x1, const Type& x2)
{
    typedef typename difference_type_of<interval_traits<Type> >::type difference_type;

    if(icl::is_empty(x1) || icl::is_empty(x2))
        return icl::identity_element<difference_type>::value();
    else if(domain_less<Type>(last(x1), first(x2)))
        return static_cast<difference_type>(icl::pred(first(x2) - last(x1)));
    else if(domain_less<Type>(last(x2), first(x1)))
        return static_cast<difference_type>(icl::pred(first(x1) - last(x2)));
    else
        return icl::identity_element<difference_type>::value();
}

template<class Type>
typename boost::
enable_if< mpl::and_< is_interval<Type>
                    , has_difference<typename interval_traits<Type>::domain_type>
                    , is_continuous<typename interval_traits<Type>::domain_type>
                    >
         , typename difference_type_of<interval_traits<Type> >::type>::type
distance(const Type& x1, const Type& x2)
{
    typedef typename difference_type_of<interval_traits<Type> >::type DiffT;

    if(icl::is_empty(x1) || icl::is_empty(x2))
        return icl::identity_element<DiffT>::value();
    else if(domain_less<Type>(upper(x1), lower(x2)))
        return lower(x2) - upper(x1);
    else if(domain_less<Type>(upper(x2), lower(x1)))
        return lower(x1) - upper(x2);
    else
        return icl::identity_element<DiffT>::value();
}

//==============================================================================
//= Streaming, representation
//==============================================================================
template<class Type>
typename boost::
    enable_if< mpl::or_< is_static_left_open<Type>
                       , is_static_open<Type>    >, std::string>::type
left_bracket(const Type&) { return "("; }

template<class Type>
typename boost::
    enable_if< mpl::or_< is_static_right_open<Type>
                       , is_static_closed<Type>   >, std::string>::type
left_bracket(const Type&) { return "["; }

template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>, std::string>::type
left_bracket(const Type& object)
{
    return left_bracket(object.bounds());
}

//------------------------------------------------------------------------------
template<class Type>
typename boost::
    enable_if< mpl::or_< is_static_right_open<Type>
                       , is_static_open<Type>     >, std::string>::type
right_bracket(const Type&) { return ")"; }

template<class Type>
typename boost::
    enable_if< mpl::or_< is_static_left_open<Type>
                       , is_static_closed<Type>    >, std::string>::type
right_bracket(const Type&) { return "]"; }

template<class Type>
typename boost::enable_if<has_dynamic_bounds<Type>, std::string>::type
right_bracket(const Type& object)
{
    return right_bracket(object.bounds());
}

//------------------------------------------------------------------------------
template<class CharType, class CharTraits, class Type>
typename boost::enable_if<is_interval<Type>,
                          std::basic_ostream<CharType, CharTraits> >::type&
operator << (std::basic_ostream<CharType, CharTraits> &stream, Type const& object)
{
    if(boost::icl::is_empty(object))
        return stream << left_bracket<Type>(object) << right_bracket<Type>(object);
    else
        return stream << left_bracket<Type>(object)
                      << interval_traits<Type>::lower(object)
                      << ","
                      << interval_traits<Type>::upper(object)
                      << right_bracket<Type>(object) ;
}

}} // namespace icl boost

#endif