/*
Copyright 2014, 2015 Rogier van Dalen.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef MATH_SEQUENCE_HPP_INCLUDED
#define MATH_SEQUENCE_HPP_INCLUDED

#include <vector>
#include <list>
#include <stdexcept>
#include <algorithm> // for std::reverse.

#include <iosfwd>

#include <boost/utility/enable_if.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/optional.hpp>
#include <boost/functional/hash_fwd.hpp>

#include "utility/overload_order.hpp"
#include "utility/disable_if_same.hpp"

#include "range/core.hpp"
#include "range/for_each_macro.hpp"
#include "range/empty_view.hpp"
#include "range/tuple.hpp"
#include "range/std/view_optional.hpp"
#include "range/std/container.hpp"
#include "range/equal.hpp"
#include "range/less_lexicographical.hpp"
#include "range/hash_range.hpp"

#include "rime/if.hpp"
#include "rime/call_if.hpp"
#include "rime/assert.hpp"

#include "magma.hpp"

namespace math {

/**
Semiring that contains a sequence of zero or more symbols, or it can be
the multiplicative annihilator.
All specialised classes can be converted to this one implicitly; the other
way around the conversion is explicit, because they may not be possible.

\ref times concatenates two sequences.
\ref plus returns the longest common prefix (if \a Direction is \ref left) or
suffix (if \a Direction is \ref right).
\ref divide is only defined from \a Direction, and then only if the divisor is
a prefix (or suffix) of the dividend.

\ref compare implements a strict weak ordering by sorting elements
lexicographically from \a Direction.
\ref choose picks the shortest sequence first, and uses lexicographical order
from \a Direction as a tie-breaker.
This makes the sequence a semiring with \ref times and \ref choose in both
directions, whatever the value of \a Direction

Sequences support Boost.Hash, if \c boost/functional/hash.hpp is included.
Sequences of different types that compare equal have the same hash value.
The details of how the hashes are computed (in what direction, for example) are
not defined, and likely to change in future versions.

\sa math::empty_sequence, math::single_sequence, math::optional_sequence,
math::sequence_annihilator

\todo Should a bi-directional version be implemented, which would not provide
the plus operation, but allow division in either direction?
*/
template <class Symbol, class Direction = left> class sequence;
template <class Symbol, class Direction = left> class empty_sequence;
template <class Symbol, class Direction = left> class single_sequence;
template <class Symbol, class Direction = left> class optional_sequence;
template <class Symbol, class Direction = left> class sequence_annihilator;

template <class Symbol, class Direction> struct sequence_tag;

template <class Symbol, class Direction>
    struct decayed_magma_tag <sequence <Symbol, Direction>>
{ typedef sequence_tag <Symbol, Direction> type; };

template <class Symbol, class Direction>
    struct decayed_magma_tag <empty_sequence <Symbol, Direction>>
{ typedef sequence_tag <Symbol, Direction> type; };

template <class Symbol, class Direction>
    struct decayed_magma_tag <single_sequence <Symbol, Direction>>
{ typedef sequence_tag <Symbol, Direction> type; };

template <class Symbol, class Direction>
    struct decayed_magma_tag <optional_sequence <Symbol, Direction>>
{ typedef sequence_tag <Symbol, Direction> type; };

template <class Symbol, class Direction>
    struct decayed_magma_tag <sequence_annihilator <Symbol, Direction>>
{ typedef sequence_tag <Symbol, Direction> type; };

template <class Symbol, class Direction> class sequence {
private:
    bool is_annihilator_;
    std::vector <Symbol> symbols_;
public:
    /**
    Initialise with no symbols.
    (Multiplicative identity.)
    */
    sequence() : is_annihilator_ (false), symbols_() {}

    /**
    Initialise with the empty sequence.
    */
    sequence (empty_sequence <Symbol, Direction> const &)
    : is_annihilator_ (false), symbols_() {}

    /**
    Initialise with a sequence with one element.
    */
    sequence (single_sequence <Symbol, Direction> const & s)
    : is_annihilator_ (false), symbols_ (1, s.symbol()) {}

    /**
    Initialise with a sequence with zero or one element.
    */
    sequence (optional_sequence <Symbol, Direction> const & s)
    : is_annihilator_ (false)
    {
        if (!s.empty())
            symbols_.push_back (s.symbol().get());
        assert (s.empty() == this->empty());
    }

    /**
    Initialise as the multiplicative annihilator.
    (Additive identity.)
    */
    sequence (sequence_annihilator <Symbol, Direction> const &)
    : is_annihilator_ (true) {}

    /**
    Initialise with a range of symbols.
    */
    template <class Range, class Enable = typename
            boost::enable_if <range::is_range <Range>>::type>
        explicit sequence (Range && range)
    : is_annihilator_ (false)
    {
        RANGE_FOR_EACH (element, range)
            symbols_.push_back (element);
    }

    /**
    Initialise with a range of symbols.
    (Optimised version using the move constructor of std::vector.)
    */
    template <class Range> explicit sequence (std::vector <Symbol> && range)
    : is_annihilator_ (false), symbols_ (std::move (range)) {}

    /**
    Return \c true iff this is an annihilator.
    */
    bool is_annihilator() const { return is_annihilator_; }

    /**
    Return \c true iff this contains a symbol sequence of zero elements.
    \pre This is not an annihilator.
    */
    bool empty() const {
        assert (!is_annihilator());
        return range::empty (symbols_);
    }

    /**
    Return a range containing the symbols.
    \pre This is not an annihilator.
    */
    std::vector <Symbol> const & symbols() const {
        assert (!is_annihilator());
        return symbols_;
    }
};

/**
Sequence that is known at compile time to be of zero length.

\sa math::sequence, math::single_sequence, math::optional_sequence
*/
template <class Symbol, class Direction> class empty_sequence {
public:
    empty_sequence() {}

    /**
    Initialise with a sequence, which must be empty.
    \throw magma_not_convertible If the sequence is non-empty.
    */
    explicit empty_sequence (sequence <Symbol, Direction> const & s) {
        if (s.is_annihilator() || !s.empty())
            throw magma_not_convertible();
    }

    /**
    Initialise with single_sequence, which is never possible and always throws.
    \throw magma_not_convertible
    */
    explicit empty_sequence (single_sequence <Symbol, Direction> const &)
    { throw magma_not_convertible(); }

    /**
    Initialise with optional_sequence, which must be empty.
    \throw magma_not_convertible If the sequence is non-empty.
    */
    explicit empty_sequence (optional_sequence <Symbol, Direction> const & s) {
        if (!s.empty())
            throw magma_not_convertible();
    }

    /**
    Initialise with a range of symbols.
    This range must be empty.
    */
    template <class Range, class Enable = typename
        boost::enable_if <range::is_range <Range>>::type>
    explicit empty_sequence (Range && range)
    { rime::assert_ (range::empty (range)); }

    rime::false_type is_annihilator() const { return rime::false_; }
    rime::true_type empty() const { return rime::true_; }

    range::empty_view symbols() const { return range::empty_view(); }
};

/**
Sequence that is known at compile time to be of length one.

\sa math::sequence, math::empty_sequence, math::optional_sequence
*/
template <class Symbol, class Direction> class single_sequence {
private:
    Symbol symbol_;

    /**
    Extract the first symbol from a symbol_sequence.
    If the range has another length than 1, throw \c magma_not_convertible.
    */
    static Symbol get_only_symbol_from (sequence <Symbol, Direction> const & s)
    {
        if (s.is_annihilator())
            throw magma_not_convertible();
        auto && symbols = s.symbols();
        if (range::empty (symbols) || !range::empty (range::drop (symbols)))
            throw magma_not_convertible();
        else
            return range::first (symbols);
    }

public:
    /**
    Initialise with an explicit symbol.
    \todo Add overloads for Symbol && and Symbol &?
    */
    explicit single_sequence (Symbol const & symbol)
    : symbol_ (symbol) {}

    /**
    Initialise with a range of symbols, which must have one element.
    */
    template <class Range,
        class Enable1 = typename
            boost::enable_if <range::is_range <Range>>::type,
        class Enable2 = typename
            utility::disable_if_same_or_derived <Symbol, Range>::type>
    explicit single_sequence (Range && range)
    : symbol_ (range::first (range))
    { rime::assert_ (range::empty (range::drop (range))); }

    /**
    Convert from sequence: explicit.
    \pre The sequence must contain exactly one element.
    \throw magma_not_convertible If the sequence has another length than 1.
    */
    explicit single_sequence (sequence <Symbol, Direction> const & that)
    : symbol_ (get_only_symbol_from (that)) {}

    rime::false_type empty() const { return rime::false_; }
    rime::false_type is_annihilator() const { return rime::false_; }

    Symbol const & symbol() const { return symbol_; }

    range::tuple <Symbol const &> symbols() const
    { return range::tie (symbol_); }
};

/**
Sequence that is known at compile time to be of length zero or one.

\sa math::sequence
*/
template <class Symbol, class Direction> class optional_sequence {
private:
    boost::optional <Symbol> symbol_;

    /**
    Extract an optional element from a range of length zero or one.
    */
    template <class Range>
        static boost::optional <Symbol> extract_from_range (Range && range)
    {
        if (range::empty (range))
            return boost::optional <Symbol>();
        else {
            auto chopped = range::chop (range);
            assert (range::empty (chopped.rest()));
            return boost::optional <Symbol> (chopped.move_first());
        }
    }
public:
    /**
    Initialise empty.
    */
    optional_sequence() {}

    /**
    Initialise with an explicit symbol.
    */
    explicit optional_sequence (Symbol const & symbol)
    : symbol_ (symbol) {}

    /**
    Convert empty, from empty_sequence.
    */
    optional_sequence (empty_sequence <Symbol, Direction> const & that)
    : symbol_ () {}

    /**
    Convert with one symbol, from single_sequence.
    */
    optional_sequence (
        single_sequence <Symbol, Direction> const & that)
    : symbol_ (that.symbol()) {}

    /**
    Convert from sequence: explicit.
    \pre The sequence must contain either zero or one element.
    \throw magma_not_convertible
        If the sequence is an annihilator or has a greater length.
    */
    explicit optional_sequence (sequence <Symbol, Direction> const & that) {
        if (that.is_annihilator())
            throw magma_not_convertible();
        auto && symbols = that.symbols();
        if (!range::empty (symbols)) {
            if (!range::empty (range::drop (symbols)))
                throw magma_not_convertible();
            else
                symbol_ = range::first (symbols);
        }
        assert (that.empty() == this->empty());
    }

    /**
    Initialise with a range of symbols, which must have one element.
    */
    template <class Range,
        class Enable1 = typename
            boost::enable_if <range::is_range <Range>>::type,
        class Enable2 = typename
            utility::disable_if_same_or_derived <Symbol, Range>::type>
    explicit optional_sequence (Range && range)
    : symbol_ (extract_from_range (std::forward <Range> (range))) {}

    rime::false_type is_annihilator() const { return rime::false_; }
    bool empty() const { return !symbol_; }

    boost::optional <Symbol> const & symbol() const { return symbol_; }

    typename range::result_of <range::callable::view_optional (
        boost::optional <Symbol> const &)>::type
    symbols() const
    { return range::view_optional (symbol_); }
};

/**
Sequence that is known at compile time to be the multiplicative annihilator:
Multiplying this with any sequence yields a sequence_annihilator.

\sa math::sequence
*/
template <class Symbol, class Direction> class sequence_annihilator {
public:
    sequence_annihilator() {}

    /**
    Construct from a sequence, which must be an annihilator.
    \throw magma_not_convertible If the argument is not an annihilator.
    */
    explicit sequence_annihilator (sequence <Symbol, Direction> const & s) {
        if (!s.is_annihilator())
            throw magma_not_convertible();
    }

    rime::true_type is_annihilator() const { return rime::true_; }
};

namespace detail {

    template <class Type> struct is_sequence_tag : boost::mpl::false_ {};
    template <class Symbol, class Direction>
        struct is_sequence_tag <sequence_tag <Symbol, Direction>>
    : boost::mpl::true_ {};

} // namespace detail

MATH_MAGMA_GENERATE_OPERATORS (detail::is_sequence_tag)

namespace operation {

    template <bool value> struct return_bool {
        template <class ... Arguments>
            rime::bool_ <value> operator() (Arguments const & ...) const
        { return rime::bool_ <value>(); }
    };

    namespace sequence_detail {

        /**
        Return the range direction associated with the direction.
        <c>range_direction<left>::type</c> is <c>::direction::front</c>.
        <c>range_direction<right>::type</c> is <c>::direction::back</c>.
        */
        template <class Direction> struct range_direction;

        template <> struct range_direction <left>
        { typedef ::direction::front type; };
        template <> struct range_direction <right>
        { typedef ::direction::back type; };

    } // namespace sequence_detail

    /* Queries. */
    template <class Symbol, class Direction>
        struct equal <sequence_tag <Symbol, Direction>>
    {
    private:
        struct when_first_annihilator {
            template <class Sequence1, class Sequence2> auto operator() (
                Sequence1 const & sequence1, Sequence2 const & sequence2) const
            RETURNS (sequence2.is_annihilator());
        };

        struct when_neither_annihilator {
            template <class Sequence1, class Sequence2> auto operator() (
                Sequence1 const & sequence1, Sequence2 const & sequence2) const
            RETURNS (range::equal (sequence1.symbols(), sequence2.symbols()));
        };

        struct when_first_not_annihilator {
            template <class Sequence1, class Sequence2> auto operator() (
                Sequence1 const & sequence1, Sequence2 const & sequence2) const
            RETURNS (rime::call_if (sequence2.is_annihilator(),
                return_bool <false>(), when_neither_annihilator(),
                sequence1, sequence2));
        };

    public:
        template <class Sequence1, class Sequence2> auto operator () (
            Sequence1 const & sequence1, Sequence2 const & sequence2) const
        RETURNS (rime::call_if (sequence1.is_annihilator(),
            when_first_annihilator(), when_first_not_annihilator(),
            sequence1, sequence2));
    };

    template <class Symbol, class Direction>
        struct compare <sequence_tag <Symbol, Direction>>
    {
    private:
        // It is recommended that the "best" element comes first in the strict
        // weak order, so the annihilator is last in the sorting order.
        struct when_first_annihilator {
            template <class Sequence1, class Sequence2> auto operator() (
                Sequence1 const & sequence1, Sequence2 const & sequence2) const
            RETURNS (rime::false_);
        };

        struct when_neither_annihilator {
            template <class Sequence1, class Sequence2> auto operator() (
                Sequence1 const & sequence1, Sequence2 const & sequence2) const
            RETURNS (range::less_lexicographical (
                sequence1.symbols(), sequence2.symbols(),
                typename sequence_detail::range_direction <Direction>::type()));
        };

        struct when_first_not_annihilator {
            template <class Sequence1, class Sequence2> auto operator() (
                Sequence1 const & sequence1, Sequence2 const & sequence2) const
            RETURNS (rime::call_if (sequence2.is_annihilator(),
                return_bool <true>(), when_neither_annihilator(),
                sequence1, sequence2));
        };

    public:
        template <class Sequence1, class Sequence2> auto operator () (
            Sequence1 const & sequence1, Sequence2 const & sequence2) const
        RETURNS (rime::call_if (sequence1.is_annihilator(),
            when_first_annihilator(), when_first_not_annihilator(),
            sequence1, sequence2));
    };

    /* Produce. */

    template <class Symbol, class Direction, class Sequence>
        struct identity <sequence_tag <Symbol, Direction>, callable::times,
            Sequence>
    {
        empty_sequence <Symbol, Direction> operator() () const
        { return empty_sequence <Symbol, Direction>(); }
    };

    template <class Symbol, class Direction, class Sequence>
        struct identity <sequence_tag <Symbol, Direction>, callable::plus,
            Sequence>
    {
        sequence_annihilator <Symbol, Direction> operator() () const
        { return sequence_annihilator <Symbol, Direction>(); }
    };

    // identity for choose: forward to identity for plus.
    template <class Symbol, class Direction, class Sequence>
        struct identity <sequence_tag <Symbol, Direction>, callable::choose,
            Sequence>
    : identity <sequence_tag <Symbol, Direction>, callable::plus, Sequence> {};

    template <class Symbol, class Direction, class Sequence>
        struct annihilator <sequence_tag <Symbol, Direction>, callable::times,
            Sequence>
    {
        sequence_annihilator <Symbol, Direction> operator() () const
        { return sequence_annihilator <Symbol, Direction>(); }
    };

    /* Operations. */

    /**
    To make this a semiring with "choose" and "times", the order for "choose"
    prefers shorter sequences and uses a lexicographical comparison as a
    tie-breaker.
    */
    template <class Symbol, class Direction>
        struct order <sequence_tag <Symbol, Direction>, callable::choose>
    {
        typedef typename sequence_detail::range_direction <Direction>::type
            range_direction;

        typedef empty_sequence <Symbol, Direction> empty;
        typedef single_sequence <Symbol, Direction> single;
        typedef optional_sequence <Symbol, Direction> optional;
        typedef sequence <Symbol, Direction> sequence_type;
        typedef sequence_annihilator <Symbol, Direction> annihilator;

        // Left is empty_sequence.
        rime::false_type operator() (
            empty const & left, empty const & right) const
        { return rime::false_; }

        rime::true_type operator() (
            empty const & left, single const & right) const
        { return rime::true_; }

        bool operator() (empty const & left, optional const & right) const
        { return !right.empty(); }

        rime::true_type operator() (
            empty const & left, annihilator const & right) const
        { return rime::true_; }

        bool operator() (empty const & left, sequence_type const & right) const
        { return right.is_annihilator() || !right.empty(); }

        // Left is single_sequence.
        rime::false_type operator() (
            single const & left, empty const & right) const
        { return rime::false_; }

        bool operator() (single const & left, single const & right) const
        { return left.symbol() < right.symbol(); }

        bool operator() (single const & left, optional const & right) const {
            if (right.empty())
                return false;
            else
                return left.symbol() < right.symbol();
        }

        rime::true_type operator() (
            single const & left, annihilator const & right) const
        { return rime::true_; }

        bool operator() (single const & left, sequence_type const & right) const
        {
            if (right.is_annihilator())
                return true;
            if (right.empty())
                return false;
            if (range::size (right.symbols()) == 1)
                return left.symbol()
                    < range::first (right.symbols(), range_direction());
            return true;
        }

        // Left is optional_sequence.

        rime::false_type operator() (
            optional const & left, empty const & right) const
        { return rime::false_; }

        bool operator() (optional const & left, single const & right) const {
            if (left.empty())
                return true;
            else
                return left.symbol() < right.symbol();
        }

        bool operator() (optional const & left, optional const & right) const {
            if (left.empty())
                return !right.empty();
            if (right.empty())
                return false;
            return left.symbol() < right.symbol();
        }

        rime::true_type operator() (
            optional const & left, annihilator const & right) const
        { return rime::true_; }

        bool operator() (optional const & left, sequence_type const & right)
            const
        {
            if (right.is_annihilator())
                return true;
            if (left.empty())
                return !right.empty();
            if (right.empty())
                return false;
            if (range::size (right.symbols()) == 1)
                return left.symbol()
                    < range::first (right.symbols(), range_direction());
            return true;
        }

        // Left is an annihilator.
        template <class Sequence>
        rime::false_type operator() (
            annihilator const & left, Sequence const & right) const
        { return rime::false_; }

        // Left is a sequence.
        bool operator() (sequence_type const & left, empty const & right) const
        { return !left.is_annihilator() && left.empty(); }

        bool operator() (sequence_type const & left, single const & right) const
        {
            if (left.is_annihilator())
                return false;
            if (left.empty())
                return true;
            if (range::size (left.symbols()) == 1)
                return range::first (left.symbols(), range_direction())
                    < right.symbol();
            return false;
        }

        bool operator() (sequence_type const & left, optional const & right)
            const
        {
            if (left.is_annihilator())
                return false;
            if (left.empty())
                return !right.empty();
            if (right.empty())
                return false;
            if (range::size (left.symbols()) == 1)
                return range::first (left.symbols(), range_direction())
                    < right.symbol();
            return false;
        }

        rime::false_type operator() (
            sequence_type const & left, annihilator const & right) const
        { return rime::false_; }

        bool operator() (
            sequence_type const & left, sequence_type const & right) const
        {
            if (left.is_annihilator())
                return false;
            if (right.is_annihilator())
                return true;
            auto && left_symbols = left.symbols();
            auto && right_symbols = right.symbols();
            if (range::size (left_symbols) < range::size (right_symbols))
                return true;
            if (range::size (right_symbols) < range::size (left_symbols))
                return false;
            return range::less_lexicographical (left_symbols, right_symbols);
        }
    };

    // Even though "order" is defined, this overrides the default implementation
    // to get the types right.
    template <class Symbol, class Direction>
        struct choose <sequence_tag <Symbol, Direction>>
    : associative, commutative, path_operation
    {
        typedef typename sequence_detail::range_direction <Direction>::type
            range_direction;

        typedef empty_sequence <Symbol, Direction> empty;
        typedef single_sequence <Symbol, Direction> single;
        typedef optional_sequence <Symbol, Direction> optional;
        typedef sequence <Symbol, Direction> sequence_type;
        typedef sequence_annihilator <Symbol, Direction> annihilator;

        struct implementation {
            // One argument is known to be an annihilator.
            template <class Sequence> Sequence operator() (
                annihilator const &, Sequence const & right,
                utility::overload_order <1> *) const
            { return right; }

            template <class Sequence> Sequence operator() (
                Sequence const & left, annihilator const &,
                utility::overload_order <2> *) const
            { return left; }

            // One argument is known-empty.
            template <class Right> empty operator() (
                empty const &, Right const &,
                utility::overload_order <3> *) const
            { return empty(); }

            template <class Left> empty operator() (
                Left const &, empty const &,
                utility::overload_order <4> *) const
            { return empty(); }

            // One argument is known-single.
            single operator() (single const & left, single const & right,
                utility::overload_order <5> *) const
            {
                if (left.symbol() < right.symbol())
                    return left;
                else
                    return right;
            }

            // Case where one argument is at most one symbol long.
            template <class Short, class Long> optional when_short (
                Short const & s, Long const & l) const
            {
                if (l.is_annihilator())
                    return s;
                if (s.empty() || l.empty())
                    return optional();
                if (!range::empty (range::drop (
                        l.symbols(), range_direction())))
                    // l is more than one symbol long.
                    return s;
                if (s.symbol() < range::first (l.symbols(), range_direction()))
                    return s;
                else
                    return optional (l);
            }

            // One argument is an optional_sequence.
            template <class Sequence> optional operator() (
                optional const & left, Sequence const & right,
                utility::overload_order <6> *) const
            { return when_short (left, right); }

            template <class Sequence> optional operator() (
                Sequence const & left, optional const & right,
                utility::overload_order <7> *) const
            { return when_short (right, left); }

            // One argument is an single_sequence.
            template <class Sequence> optional operator() (
                single const & left, Sequence const & right,
                utility::overload_order <6> *) const
            { return when_short (left, right); }

            template <class Sequence> optional operator() (
                Sequence const & left, single const & right,
                utility::overload_order <7> *) const
            { return when_short (right, left); }

            // Both are sequences.
            sequence_type operator() (
                sequence_type const & left, sequence_type const & right,
                utility::overload_order <8> *) const
            {
                if (left.is_annihilator())
                    return right;
                if (right.is_annihilator())
                    return left;
                auto left_size = range::size (left.symbols());
                auto right_size = range::size (right.symbols());
                if (left_size < right_size)
                    return left;
                if (right_size < left_size)
                    return right;
                if (range::less_lexicographical (
                        left.symbols(), right.symbols()))
                    return left;
                else
                    return right;
            }
        };

        template <class Left, class Right> auto operator() (
            Left const & left, Right const & right) const
        RETURNS (implementation() (left, right, utility::pick_overload()));
    };

    // Since "choose" selects the shortest sequence first, and compares
    // lexicographically as a tie-breaker, sequences in one direction are
    // semirings in both directions.
    template <class Symbol, class Direction> struct is_semiring <
        sequence_tag <Symbol, Direction>, either,
        callable::times, callable::choose>
    : rime::true_type {};

    // Concatenate
    template <class Symbol, class Direction>
        struct times <sequence_tag <Symbol, Direction>>
    : associative
    {
    private:
        typedef sequence_annihilator <Symbol, Direction> annihilator_type;
        typedef empty_sequence <Symbol, Direction> empty_type;
        typedef single_sequence <Symbol, Direction> single_type;
        typedef optional_sequence <Symbol, Direction> optional_type;
        typedef sequence <Symbol, Direction> sequence_type;

        struct implementation {
            // Annihilator: annihilates.
            auto operator() (
                annihilator_type const & left, annihilator_type const &,
                utility::overload_order <1> *) const
            RETURNS (left);

            template <class Sequence> auto operator() (
                annihilator_type const & annihilator, Sequence const &,
                utility::overload_order <1> *) const
            RETURNS (annihilator);

            template <class Sequence> auto operator() (
                Sequence const &, annihilator_type const & annihilator,
                utility::overload_order <1> *) const
            RETURNS (annihilator);

            // Empty symbol: return the other one.
            auto operator() (
                empty_type const & left, empty_type const &,
                utility::overload_order <2> *) const
            RETURNS (left);

            template <class Sequence> auto operator() (
                empty_type const &, Sequence const & right,
                utility::overload_order <2> *) const
            RETURNS (right);

            template <class Sequence> auto operator() (
                Sequence const & left, empty_type const &,
                utility::overload_order <2> *) const
            RETURNS (left);

            // Non-empty: both operands could be three types.

            void append_symbols (
                std::vector <Symbol> & symbols, single_type const & s) const
            { symbols.push_back (s.symbol()); }

            void append_symbols (
                std::vector <Symbol> & symbols, optional_type const & s) const
            {
                if (!s.empty())
                    symbols.push_back (s.symbol().get());
            }

            void append_symbols (
                std::vector <Symbol> & symbols, sequence_type const & s) const
            {
                auto && s_symbols = s.symbols();
                symbols.insert (symbols.end(),
                    s_symbols.begin(), s_symbols.end());
            }

            template <class Sequence1, class Sequence2>
                sequence <Symbol, Direction> operator() (
                    Sequence1 const & sequence1, Sequence2 const & sequence2,
                    utility::overload_order <3> *) const
            {
                if (sequence1.is_annihilator())
                    return sequence1;
                if (sequence2.is_annihilator())
                    return sequence2;

                std::vector <Symbol> concatenation;
                append_symbols (concatenation, sequence1);
                append_symbols (concatenation, sequence2);
                return sequence <Symbol, Direction> (std::move (concatenation));
            }
        };

    public:
        template <class Left, class Right> auto operator() (
            Left const & left, Right const & right) const
        RETURNS (implementation() (left, right, utility::pick_overload()));

        // This returns the wrong type but is useful for debugging:
        /*
        template <class Left, class Right> sequence <Symbol, Direction>
            operator() (Left const & left, Right const & right) const
        { return implementation() (left, right, utility::pick_overload()); }
        */
    };

    /**
    Addition for left sequences: find the longest common prefix.
    */
    template <class Symbol, class Direction>
        struct plus <sequence_tag <Symbol, Direction>>
    : associative, commutative, idempotent
    {
    private:
        typedef sequence_annihilator <Symbol, Direction> annihilator_type;
        typedef empty_sequence <Symbol, Direction> empty_type;
        typedef single_sequence <Symbol, Direction> single_type;
        typedef optional_sequence <Symbol, Direction> optional_type;
        typedef sequence <Symbol, Direction> sequence_type;

        struct implementation {
            // Annihalator is the additive identity.
            template <class Sequence> auto operator() (
                annihilator_type const &, Sequence const & right,
                utility::overload_order <1> *) const
            RETURNS (right);

            template <class Sequence> auto operator() (
                Sequence const & left, annihilator_type const &,
                utility::overload_order <2> *) const
            RETURNS (left);

            // Empty sequence: return the empty sequence.
            template <class Sequence> auto operator() (
                empty_type const & empty,
                Sequence const &,
                utility::overload_order <3> *) const
            RETURNS (empty);

            template <class Sequence> auto operator() (
                Sequence const &, empty_type const & empty,
                utility::overload_order <4> *) const
            RETURNS (empty);

            // Two sequences: different implementation for left and right.
            /*
            \todo This should really be one implementation for left and right
            sequences.
            This is easy (take the implementation for the right sequence,and
            s/range::back/direction/), except for dealing with "push_back".
            This requires range::subrange.
            */
            sequence <Symbol, left> operator() (
                sequence <Symbol, left> const & sequence1,
                sequence <Symbol, left> const & sequence2,
                utility::overload_order <5> * pick) const
            {
                // Annihalator is the additive identity.
                if (sequence1.is_annihilator())
                    return sequence2;
                if (sequence2.is_annihilator())
                    return sequence1;

                // This should be replaced by something with range::subrange.
                std::vector <Symbol> longest_common_prefix;
                auto symbols1 = range::view (sequence1.symbols());
                auto symbols2 = range::view (sequence2.symbols());
                while (!range::empty (symbols1) && !range::empty (symbols2)
                    && range::first (symbols1) == range::first (symbols2))
                {
                    longest_common_prefix.push_back (range::first (symbols1));
                    symbols1 = range::drop (symbols1);
                    symbols2 = range::drop (symbols2);
                }
                return sequence <Symbol, left> (longest_common_prefix);
            }

            sequence <Symbol, right> operator() (
                sequence <Symbol, right> const & sequence1,
                sequence <Symbol, right> const & sequence2,
                utility::overload_order <5> * pick) const
            {
                // Annihalator is the additive identity.
                if (sequence1.is_annihilator())
                    return sequence2;
                if (sequence2.is_annihilator())
                    return sequence1;

                std::list <Symbol> longest_common_prefix;
                auto symbols1 = range::view (sequence1.symbols(), range::back);
                auto symbols2 = range::view (sequence2.symbols(), range::back);
                while (!range::empty (symbols1, range::back)
                    && !range::empty (symbols2, range::back)
                    && range::first (symbols1, range::back)
                        == range::first (symbols2, range::back))
                {
                    longest_common_prefix.push_front (
                        range::first (symbols1, range::back));
                    symbols1 = range::drop (symbols1, range::back);
                    symbols2 = range::drop (symbols2, range::back);
                }
                return sequence <Symbol, right> (longest_common_prefix);
            }

            // At least one of the arguments has zero or one, or one elements.
            // The common prefix therefore is zero or one elements long.

            static Symbol const & first_symbol (single_type const & s)
            { return s.symbol(); }
            static Symbol const & first_symbol (optional_type const & s)
            { return s.symbol().get(); }

            static Symbol const & first_symbol (
                sequence <Symbol, left> const & s)
            { return range::first (s.symbols(), range::front); }
            static Symbol const & first_symbol (
                sequence <Symbol, right> const & s)
            { return range::first (s.symbols(), range::back); }

            template <class Sequence1, class Sequence2>
                optional_type operator() (
                    Sequence1 const & sequence1, Sequence2 const & sequence2,
                    utility::overload_order <5> * pick) const
            {
                typedef optional_type result_type;
                // Annihilator is the additive identity.
                if (sequence1.is_annihilator())
                    return result_type (sequence2);
                if (sequence2.is_annihilator())
                    return result_type (sequence1);

                if (sequence1.empty() || sequence2.empty())
                    return result_type();

                Symbol const & symbol = first_symbol (sequence1);
                if (symbol == first_symbol (sequence2))
                    return result_type (symbol);
                else
                    return result_type();
            }
        };

    public:
        template <class Left, class Right> auto operator() (
            Left const & left, Right const & right) const
        RETURNS (implementation() (left, right, utility::pick_overload()));

        // This returns the wrong type but is useful for debugging:
        /*
        template <class Left, class Right> sequence <Symbol, Direction>
            operator() (Left const & left, Right const & right) const
        { return implementation() (left, right, utility::pick_overload()); }
        */
    };

    // The direction matches the direction of the sequence:
    // left and right sequences are left and right semirings over times and
    // plus.
    template <class Symbol, class Direction> struct is_semiring <
        sequence_tag <Symbol, Direction>, Direction,
        callable::times, callable::plus>
    : rime::true_type {};

    /**
    The prefix of the dividend must be equal to the divisor.
    \return The dividend without the prefix.
    */
    template <class Symbol, class Direction>
        struct divide <sequence_tag <Symbol, Direction>, Direction>
    : throw_if_undefined
    {
        typedef sequence <Symbol, Direction> sequence_type;
        typedef empty_sequence <Symbol, Direction> empty_sequence_type;
        typedef single_sequence <Symbol, Direction> single_sequence_type;
        typedef optional_sequence <Symbol, Direction> optional_sequence_type;
        typedef sequence_annihilator <Symbol, Direction>
            sequence_annihilator_type;

        struct implementation {
            /**
            Implementation for the run-time type.
            This works for all sequences, but it returns the general type, which
            is not efficient.
            */
            sequence_type operator() (sequence_type const & dividend,
                sequence_type const & divisor, utility::overload_order <10> *)
            const
            {
                if (dividend.is_annihilator()) {
                    if (divisor.is_annihilator())
                        throw operation_undefined();
                    else
                        return dividend;
                } else if (divisor.is_annihilator())
                    throw divide_by_zero();

                // Start at the front of the sequence for a left division, and
                // at the back for a right division.
                auto start = typename
                    sequence_detail::range_direction <Direction>::type();
                auto divisor_symbols = range::view (divisor.symbols(), start);
                auto dividend_symbols = range::view (dividend.symbols(), start);
                while (!range::empty (divisor_symbols, start)) {
                    if (range::empty (dividend_symbols, start))
                        throw operation_undefined();
                    if (range::first (dividend_symbols, start)
                            != range::first (divisor_symbols, start))
                        throw operation_undefined();
                    divisor_symbols = range::drop (divisor_symbols, start);
                    dividend_symbols = range::drop (dividend_symbols, start);
                }
                return sequence_type (dividend_symbols);
            }

            /* Specialisations. */

            /* 1. annihilators. */

            sequence_type operator() (
                sequence_annihilator_type const & dividend,
                sequence_annihilator_type const & divisor,
                utility::overload_order <1> *) const
            { throw operation_undefined(); }

            // Divide by annihilator.
            template <class Dividend> sequence_type operator() (
                Dividend const & dividend,
                sequence_annihilator_type const & divisor,
                utility::overload_order <1> *) const
            { throw divide_by_zero(); }

            // Divide annihilator by something else.
            template <class Divisor> sequence_annihilator_type operator() (
                sequence_annihilator_type const & dividend,
                Divisor const & divisor, utility::overload_order <1> *) const
            {
                if (divisor.is_annihilator())
                    throw operation_undefined();
                else
                    return dividend;
            }

            /* 2. Empty divisor. */
            template <class Dividend> auto operator() (
                Dividend const & dividend, empty_sequence_type const & divisor,
                utility::overload_order <2> *) const
            RETURNS (dividend);

            /* 3. Empty dividend. */
            template <class Divisor> empty_sequence_type operator() (
                empty_sequence_type const & dividend, Divisor const & divisor,
                utility::overload_order <3> *) const
            {
                if (divisor.is_annihilator())
                    throw divide_by_zero();
                else if (!divisor.empty())
                    throw operation_undefined();
                else
                    return dividend;
            }

            /* 4. Combinations of single, optional, and general sequence. */

            Symbol const & first_symbol (sequence <Symbol, left> const & s)
                const
            { return range::first (s.symbols()); }

            Symbol const & first_symbol (sequence <Symbol, right> const & s)
                const
            { return range::first (s.symbols(), range::back); }

            empty_sequence_type operator() (
                single_sequence_type const & dividend,
                single_sequence_type const & divisor,
                utility::overload_order <4> *) const
            {
                if (dividend.symbol() == divisor.symbol())
                    return empty_sequence_type();
                else
                    throw operation_undefined();
            }

            optional_sequence_type operator() (
                single_sequence_type const & dividend,
                optional_sequence_type const & divisor,
                utility::overload_order <4> *) const
            {
                // Correct case.
                if (divisor.empty())
                    return dividend;
                if (dividend.symbol() == divisor.symbol().get())
                    return empty_sequence_type();
                throw operation_undefined();
            }

            optional_sequence_type operator() (
                single_sequence_type const & dividend,
                sequence_type const & divisor,
                utility::overload_order <4> *) const
            {
                if (divisor.is_annihilator())
                    throw divide_by_zero();
                // Correct case.
                if (divisor.empty())
                    return dividend;
                if (range::size (divisor.symbols()) == 1
                        && dividend.symbol() == first_symbol (divisor))
                    return empty_sequence_type();
                // Otherwise, the division is not defined.
                throw operation_undefined();
            }

            // Optional-symbol dividend.
            empty_sequence_type operator() (
                optional_sequence_type const & dividend,
                single_sequence_type const & divisor,
                utility::overload_order <4> *) const
            {
                if (!dividend.empty()
                        && dividend.symbol().get() == divisor.symbol())
                    return empty_sequence_type();
                throw operation_undefined();
            }

            optional_sequence_type operator() (
                optional_sequence_type const & dividend,
                optional_sequence_type const & divisor,
                utility::overload_order <4> *) const
            {
                if (divisor.empty())
                    return dividend;
                if (!dividend.empty() && !divisor.empty()
                        && dividend.symbol() == divisor.symbol())
                    return empty_sequence_type();
                throw operation_undefined();
            }

            optional_sequence_type operator() (
                optional_sequence_type const & dividend,
                sequence_type const & divisor,
                utility::overload_order <4> *) const
            {
                if (divisor.is_annihilator())
                    throw divide_by_zero();
                // Correct case.
                if (divisor.empty())
                    return dividend;
                if (!dividend.empty()
                        && range::size (divisor.symbols()) == 1
                        && dividend.symbol() == first_symbol (divisor))
                    return empty_sequence_type();
                // Otherwise, the division is not defined.
                throw operation_undefined();
            }

            // Negotiate between std::vector and the Range library.
            std::vector <Symbol> drop_copy (
                std::vector <Symbol> const & symbols) const
            {
                auto result_symbols_source = range::drop (symbols, typename
                    sequence_detail::range_direction <Direction>::type());
                return std::vector <Symbol> (
                    result_symbols_source.begin(), result_symbols_source.end());
            }

            // Divisor is a single_sequence or an optional_sequence.
            sequence_type operator() (
                sequence_type const & dividend,
                single_sequence_type const & divisor,
                utility::overload_order <4> *) const
            {
                if (dividend.is_annihilator())
                    return dividend;

                if (dividend.empty())
                    throw operation_undefined();
                if (first_symbol (dividend) != divisor.symbol())
                    throw operation_undefined();

                return sequence_type (drop_copy (dividend.symbols()));
            }

            sequence_type operator() (
                sequence_type const & dividend,
                optional_sequence_type const & divisor,
                utility::overload_order <4> *) const
            {
                if (divisor.empty() || dividend.is_annihilator())
                    return dividend;

                if (dividend.empty())
                    throw operation_undefined();
                if (first_symbol (dividend) != divisor.symbol().get())
                    throw operation_undefined();

                return sequence_type (drop_copy (dividend.symbols()));
            }
        };

        template <class Sequence1, class Sequence2> auto operator() (
            Sequence1 const & sequence1, Sequence2 const & sequence2) const
        RETURNS (
            implementation() (sequence1, sequence2, utility::pick_overload()));
    };

    template <class Symbol, class Direction>
        struct reverse <sequence_tag <Symbol, Direction>, callable::times>
    {
        typedef typename opposite_direction <Direction>::type other_direction;

        sequence <Symbol, other_direction> operator() (
            sequence <Symbol, Direction> const & s) const
        {
            if (s.is_annihilator())
                return sequence_annihilator <Symbol, other_direction>();
            else {
                std::vector <Symbol> symbols = s.symbols();
                std::reverse (symbols.begin(), symbols.end());
                return sequence <Symbol, other_direction> (std::move (symbols));
            }
        }

        empty_sequence <Symbol, other_direction> operator() (
            empty_sequence <Symbol, Direction> const & s) const
        { return empty_sequence <Symbol, other_direction>(); }

        single_sequence <Symbol, other_direction> operator() (
            single_sequence <Symbol, Direction> const & s) const
        { return single_sequence <Symbol, other_direction> (s.symbol()); }

        sequence_annihilator <Symbol, other_direction> operator() (
            sequence_annihilator <Symbol, Direction> const & s) const
        { return sequence_annihilator <Symbol, other_direction>(); }
    };

    template <class Symbol, class Direction>
        struct print <sequence_tag <Symbol, Direction>>
    {
        template <class Stream>
            void operator() (Stream & stream,
                sequence <Symbol, Direction> const & s) const
        {
            if (s.is_annihilator())
                stream << "<annihilator>";
            else {
                stream << "[ ";
                RANGE_FOR_EACH (symbol, s.symbols())
                    stream << symbol << " ";
                stream << "]";
            }
        }

        template <class Stream>
            void operator() (Stream & stream,
                empty_sequence <Symbol, Direction> const &) const
        { stream << "[]"; }

        template <class Stream>
            void operator() (Stream & stream,
                single_sequence <Symbol, Direction> const & s) const
        { stream << "[ " << s.symbol() << "]"; }

        template <class Stream>
            void operator() (Stream & stream,
                sequence_annihilator <Symbol, Direction> const &) const
        { stream << "<annihilator>"; }
    };

    /**
    If the sequence types are the same, return that type.
    If the sequence types are not the same, return sequence <...> or
    optional_sequence <...>.
    */
    // Same type.
    template <class Symbol, class Direction, class Sequence>
        struct unify_type <sequence_tag <Symbol, Direction>, Sequence, Sequence>
    { typedef Sequence type; };

    template <class Symbol, class Direction, class Sequence1, class Sequence2>
        struct unify_type <sequence_tag <Symbol, Direction>,
            Sequence1, Sequence2>
    { typedef sequence <Symbol, Direction> type; };

    // Mixing empty/single sequences.
    template <class Symbol, class Direction>
        struct unify_type <sequence_tag <Symbol, Direction>,
            empty_sequence <Symbol, Direction>,
            single_sequence <Symbol, Direction>>
    { typedef optional_sequence <Symbol, Direction> type; };

    template <class Symbol, class Direction>
        struct unify_type <sequence_tag <Symbol, Direction>,
            empty_sequence <Symbol, Direction>,
            optional_sequence <Symbol, Direction>>
    { typedef optional_sequence <Symbol, Direction> type; };

    template <class Symbol, class Direction>
        struct unify_type <sequence_tag <Symbol, Direction>,
            single_sequence <Symbol, Direction>,
            empty_sequence <Symbol, Direction>>
    { typedef optional_sequence <Symbol, Direction> type; };

    template <class Symbol, class Direction>
        struct unify_type <sequence_tag <Symbol, Direction>,
            single_sequence <Symbol, Direction>,
            optional_sequence <Symbol, Direction>>
    { typedef optional_sequence <Symbol, Direction> type; };

    template <class Symbol, class Direction>
        struct unify_type <sequence_tag <Symbol, Direction>,
            optional_sequence <Symbol, Direction>,
            empty_sequence <Symbol, Direction>>
    { typedef optional_sequence <Symbol, Direction> type; };

    template <class Symbol, class Direction>
        struct unify_type <sequence_tag <Symbol, Direction>,
            optional_sequence <Symbol, Direction>,
            single_sequence <Symbol, Direction>>
    { typedef optional_sequence <Symbol, Direction> type; };

} // namespace operation

/*
Hash support for Boost.Hash.
This makes all values (even of different types) that compare equal have equal
hashes.
This means that an annihilator will always have the same (random) hash.
Empty sequences have a hash value defined by range::hash_range.
Single-symbol sequences have the hash value of the one symbol.
Longer sequences use all symbols for the hash value.
*/

namespace sequence_detail {

    // A random number that will hopefully not come up much.
    static std::size_t constexpr annihilator_hash =
        std::size_t (0x84c8fa43d5283350 & std::size_t (-1));

} // sequence_detail

template <class Symbol, class Direction> inline
    std::size_t hash_value (
        sequence_annihilator <Symbol, Direction> const & s)
{ return sequence_detail::annihilator_hash; }

template <class Symbol, class Direction> inline
    std::size_t hash_value (sequence <Symbol, Direction> const & s)
{
    if (s.is_annihilator())
        return sequence_detail::annihilator_hash;
    else
        return range::hash_range (s.symbols());
}

template <class Symbol, class Direction> inline
    std::size_t hash_value (empty_sequence <Symbol, Direction> const & s)
// Return whatever hash_range returns for empty sequences.
{ return range::hash_range (s.symbols()); }

template <class Symbol, class Direction> inline
    std::size_t hash_value (single_sequence <Symbol, Direction> const & s)
{ return boost::hash <Symbol>() (s.symbol()); }

template <class Symbol, class Direction> inline
    std::size_t hash_value (optional_sequence <Symbol, Direction> const & s)
{ return range::hash_range (s.symbols()); }

} // namespace math

#endif // MATH_SEQUENCE_HPP_INCLUDED


/* ----------------------------------------------------------------------------

 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
 * Atlanta, Georgia 30332-0415
 * All Rights Reserved
 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)

 * See LICENSE for the license information

 * -------------------------------------------------------------------------- */

/**
 *  @file testTableFactor.cpp
 *  @date May 12, 2022
 *  @author Yoonwoo Kim
 */

#include <CppUnitLite/TestHarness.h>
#include <gtsam/base/Testable.h>
#include <gtsam/discrete/DiscreteDistribution.h>
#include <gtsam/discrete/Signature.h>
#include <gtsam/discrete/TableFactor.h>
#include <gtsam/discrete/DecisionTreeFactor.h>

#include <Eigen/Sparse>
#include <boost/assign/std/map.hpp>

using namespace boost::assign;
using namespace std;
using namespace gtsam;

/* ************************************************************************* */
TEST(TableFactor, constructors) {
  // Declare a bunch of keys
  DiscreteKey X(0, 2), Y(1, 3), Z(2, 2), A(3, 5);

  // Create factors
  TableFactor f_zeros(A, {0, 0, 0, 0, 1});
  TableFactor f1(X, {2, 8});
  TableFactor f2(X & Y, "2 5 3 6 4 7");
  TableFactor f3(X & Y & Z, "2 5 3 6 4 7 25 55 35 65 45 75");
  EXPECT_LONGS_EQUAL(1, f1.size());
  EXPECT_LONGS_EQUAL(2, f2.size());
  EXPECT_LONGS_EQUAL(3, f3.size());

  DiscreteValues values;
  values[0] = 1;  // x
  values[1] = 2;  // y
  values[2] = 1;  // z
  values[3] = 4;
  EXPECT_DOUBLES_EQUAL(1, f_zeros(values), 1e-9);
  EXPECT_DOUBLES_EQUAL(8, f1(values), 1e-9);
  EXPECT_DOUBLES_EQUAL(7, f2(values), 1e-9);
  EXPECT_DOUBLES_EQUAL(75, f3(values), 1e-9);
}

/* ************************************************************************* */
TEST(TableFactor, lazy_cartesian_product) {
  DiscreteKey v0(0, 2), v1(1, 2), v2(2, 2), A(3, 5);

  TableFactor f1(v0 & v1, "1 2 3 4");
  double actual_v0 = f1.lazy_cp(v0.first, 2);
  EXPECT_DOUBLES_EQUAL(1, actual_v0, 1e-9);

  double actual_v1 = f1.lazy_cp(v1.first, 2);
  EXPECT_DOUBLES_EQUAL(0, actual_v1, 1e-9);

  TableFactor f_zeros(A, {0, 0, 0, 0, 1});
  double actual_zero = f_zeros.lazy_cp(A.first, 3);
  EXPECT_DOUBLES_EQUAL(3, actual_zero, 1e-9);
}

/* ************************************************************************* */
TEST(TableFactor, project) {
  DiscreteKey v0(0, 2), v1(1, 2), v2(2, 2), A(3, 5);

  TableFactor f1(v0 & v1, "1 2 3 4");
  // (v0 = 0)
  DiscreteValues assignment_1;
  assignment_1[v0.first] = 0;;
  std::vector<DiscreteValues> actual_vec1 = f1.project(assignment_1);
  DiscreteValues expected_1;
  expected_1[v0.first] = 0;
  expected_1[v1.first] = 0;
  DiscreteValues expected_2;
  expected_2[v0.first] = 0;
  expected_2[v1.first] = 1;
  std::vector<DiscreteValues> expected_vec1 = {expected_1, expected_2};
  EXPECT(expected_vec1 == actual_vec1);

  // (v0 = 1)
  DiscreteValues assignment_2;
  assignment_2[v0.first] = 1;
  std::vector<DiscreteValues> actual_vec2 = f1.project(assignment_2);
  DiscreteValues expected_3;
  expected_3[v0.first] = 1;
  expected_3[v1.first] = 0;
  DiscreteValues expected_4;
  expected_4[v0.first] = 1;
  expected_4[v1.first] = 1;
  std::vector<DiscreteValues> expected_vec2 = {expected_3, expected_4};
  EXPECT(expected_vec2 == actual_vec2);

  // (v1 = 0)
  DiscreteValues assignment_3;
  assignment_3[v1.first] = 0;
  std::vector<DiscreteValues> actual_vec3 = f1.project(assignment_3);
  DiscreteValues expected_5;
  expected_5[v0.first] = 0;
  expected_5[v1.first] = 0;
  DiscreteValues expected_6;
  expected_6[v0.first] = 1;
  expected_6[v1.first] = 0;
  std::vector<DiscreteValues> expected_vec3 = {expected_5, expected_6};
  EXPECT(expected_vec3 == actual_vec3);

  //  (v1 = 1)
  DiscreteValues assignment_4;
  assignment_4[v1.first] = 1;
  std::vector<DiscreteValues> actual_vec4 = f1.project(assignment_4);
  DiscreteValues expected_7;
  expected_7[v0.first] = 0;
  expected_7[v1.first] = 1;
  DiscreteValues expected_8;
  expected_8[v0.first] = 1;
  expected_8[v1.first] = 1;
  std::vector<DiscreteValues> expected_vec4 = {expected_7, expected_8};
  EXPECT(expected_vec4 == actual_vec4);
}

/* ************************************************************************* */
TEST(TableFactor, find_idx) {
  DiscreteKey v0(0, 2), v1(1, 3);
  TableFactor f1(v0 & v1, "1 2 3 4 5 6");

  DiscreteValues assignment;
  assignment[v0.first] = 1;
  assignment[v1.first] = 1;

  double actual_1 = f1.findIndex(assignment);
  EXPECT_LONGS_EQUAL(1 * 3 + 1, actual_1);

  assignment[v0.first] = 1;
  assignment[v1.first] = 0;

  double actual_2 = f1.findIndex(assignment);
  EXPECT_LONGS_EQUAL(1 * 3 + 0, actual_2);
}

/* ************************************************************************* */
TEST(TableFactor, multiplication) {
  DiscreteKey v0(0, 2), v1(1, 2), v2(2, 2);

  // Multiply with a DiscreteDistribution, i.e., Bayes Law!
  DiscreteDistribution prior(v1 % "1/3");
  TableFactor f1(v0 & v1, "1 2 3 4");
  DecisionTreeFactor expected(v0 & v1, "0.25 1.5 0.75 3");
  CHECK(assert_equal(expected, static_cast<DecisionTreeFactor>(prior) *
                                   f1.toDecisionTreeFactor()));
  CHECK(assert_equal(expected, f1 * prior));

  // Multiply two factors
  TableFactor f2(v1 & v2, "5 6 7 8");
  TableFactor actual = f1 * f2;
  TableFactor expected2(v0 & v1 & v2, "5 6 14 16 15 18 28 32");
  CHECK(assert_equal(expected2, actual));

  DiscreteKey A(0, 3), B(1, 2), C(2, 2);
  TableFactor f_zeros1(A & C, "0 0 0 2 0 3");
  TableFactor f_zeros2(B & C, "4 0 0 5");
  TableFactor actual_zeros = f_zeros1 * f_zeros2;
  TableFactor expected3(A & B & C, "0 0 0 0 0 0 0 10 0 0 0 15");
  CHECK(assert_equal(expected3, actual_zeros));
}

/* ************************************************************************* */
TEST(TableFactor, sum_max) {
  DiscreteKey v0(0, 3), v1(1, 2);
  TableFactor f1(v0 & v1, "1 2  3 4  5 6");

  TableFactor expected(v1, "9 12");
  TableFactor::shared_ptr actual = f1.sum(1);
  CHECK(assert_equal(expected, *actual, 1e-5));

  TableFactor expected2(v1, "5 6");
  TableFactor actual2 = f1.max(1);
  CHECK(assert_equal(expected2, actual2));

  TableFactor f2(v1 & v0, "1 2  3 4  5 6");
  TableFactor::shared_ptr actual22 = f2.sum(1);
}

/* ************************************************************************* */
TEST( DecisionTreeFactor, max_ordering)
{
  DiscreteKey v0(0,3), v1(1,2), v2(2, 2);
  TableFactor f1(v0 & v1 & v2, "1 2 3 10 5 6 7 11 9 4 8 12");
  Ordering ordering;
  ordering += Key(1);

  TableFactor actual = f1.max(ordering);
  TableFactor expected(v0 & v2, "3 10 7 11 9 12");
  CHECK(assert_equal(expected, actual));

  TableFactor f2(v0 & v1 & v2, "0 0 0 0 0 0 0 15 0 0 0 10");
  TableFactor actual_zeros = f2.max(ordering);
  TableFactor expected_zeros(v0 & v2, "0 0 0 15 0 10");
  CHECK(assert_equal(expected_zeros, actual_zeros));
}

/* ************************************************************************* */
TEST( DecisionTreeFactor, max_assignment)
{
  DiscreteKey v0(0,3), v1(1,2), v2(2, 2);
  TableFactor f1(v0 & v1 & v2, "1 2 3 10 5 6 7 11 9 4 8 12");
  DiscreteValues assignment = f1.maxAssignment();
  DiscreteValues expected;
  expected[v0.first] = 2;
  expected[v1.first] = 1;
  expected[v2.first] = 1;
  CHECK(assert_equal(expected, assignment));

  TableFactor f2(v0 & v1 & v2, "0 0 0 0 0 0 0 15 0 0 0 10");
  DiscreteValues assignment_zeros = f2.maxAssignment();
  DiscreteValues expected_zeros;
  expected_zeros[v0.first] = 1;
  expected_zeros[v1.first] = 1;
  expected_zeros[v2.first] = 1;
  CHECK(assert_equal(expected_zeros, assignment_zeros));
}

/* ************************************************************************* */
// Check enumerate yields the correct list of assignment/value pairs.
TEST(TableFactor, enumerate) {
  DiscreteKey A(12, 3), B(5, 2);
  TableFactor f(A & B, "1 2  3 4  5 6");
  auto actual = f.enumerate();
  std::vector<std::pair<DiscreteValues, double>> expected;
  DiscreteValues values;
  for (size_t a : {0, 1, 2}) {
    for (size_t b : {0, 1}) {
      values[12] = a;
      values[5] = b;
      expected.emplace_back(values, f(values));
    }
  }
  EXPECT(actual == expected);
}

/* ************************************************************************* */
// Check markdown representation looks as expected.
TEST(TableFactor, markdown) {
  DiscreteKey A(12, 3), B(5, 2);
  TableFactor f(A & B, "1 2  3 4  5 6");
  string expected =
      "|A|B|value|\n"
      "|:-:|:-:|:-:|\n"
      "|0|0|1|\n"
      "|0|1|2|\n"
      "|1|0|3|\n"
      "|1|1|4|\n"
      "|2|0|5|\n"
      "|2|1|6|\n";
  auto formatter = [](Key key) { return key == 12 ? "A" : "B"; };
  string actual = f.markdown(formatter);
  EXPECT(actual == expected);
}

/* ************************************************************************* */
// Check markdown representation with a value formatter.
TEST(TableFactor, markdownWithValueFormatter) {
  DiscreteKey A(12, 3), B(5, 2);
  TableFactor f(A & B, "1 2  3 4  5 6");
  string expected =
      "|A|B|value|\n"
      "|:-:|:-:|:-:|\n"
      "|Zero|-|1|\n"
      "|Zero|+|2|\n"
      "|One|-|3|\n"
      "|One|+|4|\n"
      "|Two|-|5|\n"
      "|Two|+|6|\n";
  auto keyFormatter = [](Key key) { return key == 12 ? "A" : "B"; };
  TableFactor::Names names{{12, {"Zero", "One", "Two"}},
                                  {5, {"-", "+"}}};
  string actual = f.markdown(keyFormatter, names);
  EXPECT(actual == expected);
}

/* ************************************************************************* */
// Check html representation with a value formatter.
TEST(TableFactor, htmlWithValueFormatter) {
  DiscreteKey A(12, 3), B(5, 2);
  TableFactor f(A & B, "1 2  3 4  5 6");
  string expected =
      "
\n"
      "
\n"
      "
\n"
      "
A	B	value
\n"
      "
\n"
      "
\n"
      "
Zero	-	1
\n"
      "
Zero	+	2
\n"
      "
One	-	3
\n"
      "
One	+	4
\n"
      "
Two	-	5
\n"
      "
Two	+	6
\n"
      "
\n"
      "
\n"
      "
";
  auto keyFormatter = [](Key key) { return key == 12 ? "A" : "B"; };
  TableFactor::Names names{{12, {"Zero", "One", "Two"}},
                                  {5, {"-", "+"}}};
  string actual = f.html(keyFormatter, names);
  EXPECT(actual == expected);
}

/* ************************************************************************* */
int main() {
  TestResult tr;
  return TestRegistry::runAllTests(tr);
}
/* ************************************************************************* */


#ifndef STAN_MATH_PRIM_SCAL_PROB_NORMAL_RNG_HPP
#define STAN_MATH_PRIM_SCAL_PROB_NORMAL_RNG_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/err/check_positive_finite.hpp>
#include <boost/random/normal_distribution.hpp>
#include <boost/random/variate_generator.hpp>

namespace stan {
namespace math {

/**
 * Return a Normal random variate for the given location and scale
 * using the specified random number generator.
 *
 * mu and sigma can each be a scalar or a vector. Any non-scalar
 * inputs must be the same length.
 *
 * @tparam T_loc Type of location parameter
 * @tparam T_scale Type of scale parameter
 * @tparam RNG type of random number generator
 * @param mu (Sequence of) location parameter(s)
 * @param sigma (Sequence of) positive scale parameter(s)
 * @param rng random number generator
 * @return (Sequence of) normal random variate(s)
 * @throw std::domain_error if mu is infinite or sigma is nonpositive
 * @throw std::invalid_argument if non-scalar arguments are of different
 * sizes
 */
template <typename T_loc, typename T_scale, class RNG>
inline typename VectorBuilder<true, double, T_loc, T_scale>::type normal_rng(
    const T_loc& mu, const T_scale& sigma, RNG& rng) {
  using boost::normal_distribution;
  using boost::variate_generator;
  static const char* function = "normal_rng";

  check_finite(function, "Location parameter", mu);
  check_positive_finite(function, "Scale parameter", sigma);
  check_consistent_sizes(function, "Location parameter", mu, "Scale Parameter",
                         sigma);

  scalar_seq_view<T_loc> mu_vec(mu);
  scalar_seq_view<T_scale> sigma_vec(sigma);
  size_t N = max_size(mu, sigma);
  VectorBuilder<true, double, T_loc, T_scale> output(N);

  for (size_t n = 0; n < N; ++n) {
    variate_generator<RNG&, normal_distribution<> > norm_rng(
        rng, normal_distribution<>(mu_vec[n], sigma_vec[n]));
    output[n] = norm_rng();
  }

  return output.data();
}

}  // namespace math
}  // namespace stan
#endif


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_IEEE_FUNCTIONS_SIGN_HPP_INCLUDED
#define BOOST_SIMD_IEEE_FUNCTIONS_SIGN_HPP_INCLUDED
#include <boost/simd/include/functor.hpp>
#include <boost/dispatch/include/functor.hpp>

namespace boost { namespace simd { namespace tag
  {
   /*!
     @brief sign generic tag

     Represents the sign function in generic contexts.

     @par Models:
        Hierarchy
   **/
    struct sign_ : ext::elementwise_<sign_>
    {
      /// @brief Parent hierarchy
      typedef ext::elementwise_<sign_> parent;
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_sign_( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };
  }
  namespace ext
  {
   template<class Site>
   BOOST_FORCEINLINE generic_dispatcher<tag::sign_, Site> dispatching_sign_(adl_helper, boost::dispatch::meta::unknown_<Site>, ...)
   {
     return generic_dispatcher<tag::sign_, Site>();
   }
   template<class... Args>
   struct impl_sign_;
  }
  /*!
    Returns the sign of a0. I.e. -1, 0 or 1, according
    a0 is less than zero, zero or greater than zero.
    For floating sign of nan is nan

    @par Semantic:

    @code
    T r = sign(x);
    @endcode

    is similar to:

    @code
    T r = (x > 0) ? T(1) : ((x < 0) ? T(-1) : ((x == 0) ? 0 : Nan<T>()));
    @endcode

    @param a0

    @return a value of same type as the input
  **/
  BOOST_DISPATCH_FUNCTION_IMPLEMENTATION(tag::sign_, sign, 1)
} }

#endif


///* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
//
///*
// Copyright (C) 2014 Jose Aparicio
//
// This file is part of QuantLib, a free-software/open-source library
// for financial quantitative analysts and developers - http://quantlib.org/
//
// QuantLib is free software: you can redistribute it and/or modify it
// under the terms of the QuantLib license.  You should have received a
// copy of the license along with this program; if not, please email
// <quantlib-dev@lists.sf.net>. The license is also available online at
// <http://quantlib.org/license.shtml>.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE.  See the license for more details.
//*/
//
//#include <boost/make_shared.hpp>
//#include <qlo/objects/credit/latentmodels.hpp>
//
//#include <ql/experimental/credit/defaultprobabilitylatentmodel.hpp>
//
//namespace QuantLibAddin {
//
//    GaussianDefProbLM::GaussianDefProbLM(
//        const boost::shared_ptr<reposit::ValueObject>& properties,
//        const boost::shared_ptr<QuantLib::Basket>& basket,
//        const std::vector<std::vector<QuantLib::Real> >& factorWeights,
//        bool permanent)
//    : reposit::LibraryObject<QuantLib::GaussianDefProbLM>(properties,
//      permanent) {
//        libraryObject_ = boost::make_shared<QuantLib::GaussianDefProbLM>(
//            //basket,
//            factorWeights,
//            QuantLib::LatentModelIntegrationType::GaussianQuadrature);
//        libraryObject_->resetBasket(basket);
//    }
//
//    TDefProbLM::TDefProbLM(
//        const boost::shared_ptr<reposit::ValueObject>& properties,
//        const std::vector<QuantLib::Integer>& tOrders,
//        const boost::shared_ptr<QuantLib::Basket>& basket,
//        const std::vector<std::vector<QuantLib::Real> >& factorWeights,
//        bool permanent)
//    : reposit::LibraryObject<QuantLib::TDefProbLM>(properties,
//      permanent) {
//        QuantLib::TCopulaPolicy::initTraits initsT;
//        initsT.tOrders = tOrders;
//        libraryObject_ = boost::make_shared<QuantLib::TDefProbLM>(
//            //basket,
//            factorWeights,
//            QuantLib::LatentModelIntegrationType::GaussianQuadrature,
//            initsT);
//        libraryObject_->resetBasket(basket);
//    }
//
//}


/*
    Copyright 2005-2007 Adobe Systems Incorporated
    Distributed under the MIT License (see accompanying file LICENSE_1_0_0.txt
    or a copy at http://stlab.adobe.com/licenses.html)
*/

/**************************************************************************************************/

// config
#include <adobe/config.hpp>

// stdc++
#include <iostream>
#include <sstream>

#ifndef USING_OPENSSL
#define USING_OPENSSL 0
#endif

#ifndef USING_BOOSTCRYPTO
#define USING_BOOSTCRYPTO 0
#endif

#if USING_OPENSSL
// openssl
#include <openssl/sha.h>
#endif

// asl
#include <adobe/sha.hpp>
#include <adobe/timer.hpp>

#if ADOBE_PLATFORM_MAC
// Mac OS X Common Crypto
#include <CommonCrypto/CommonDigest.h>
#endif

/*
    Boost.crypto can be obtained from here:

    https://github.com/boost-vault/Miscellaneous/blob/master/crypto_v01.zip

    Unzip the folder and leave it unmodified as a sibling to this source file.

    Then build using:

        bjam define=USING_BOOSTCRYPTO=1 release
*/
#if USING_BOOSTCRYPTO
#include <boost/crypto/sha1.hpp>
#include <boost/crypto/sha2.hpp>
#endif

/**************************************************************************************************/

namespace {

/**************************************************************************************************/

template <typename DigestType>
std::string digest_binary(const DigestType& digest) {
    enum { num_bytes = sizeof(typename DigestType::value_type) };

    std::string digest_str;

    digest_str.reserve(digest.size() * num_bytes + 1);

    for (auto& curdigest : digest) {
        for (int i(num_bytes - 1); i >= 0; --i) {
            std::size_t shift_amt(8 * i);

            digest_str += char((curdigest >> shift_amt) & 0xff);
        }
    }

    return digest_str;
}

/**************************************************************************************************/

void print_binary_message(const std::string& str, std::size_t unit_size = 0) {
    std::cout << std::hex;

    std::size_t count(0);

    for (std::string::const_iterator first(str.begin()), last(str.end()); first != last; ++first) {
        int value(int(*first) & 0xff);

        std::cout.width(2);
        std::cout.fill('0');

        std::cout << value;

        if (++count == unit_size) {
            std::cout << ' ';
            count = 0;
        }
    }

    std::cout << std::dec;
}

/**************************************************************************************************/

template <typename Hash>
std::vector<std::string> bench(const std::vector<std::string>& corpus, const char* timer_header,
                               Hash hash) {
    std::vector<std::string> result;

    result.reserve(corpus.size());

    adobe::timer_t timer;

    std::for_each(corpus.begin(), corpus.end(),
                  [&](const std::string& element) { result.emplace_back(hash(element)); });

    timer.report(timer_header);

    return result;
}

/**************************************************************************************************/
#if 0
#pragma mark -
#endif
/**************************************************************************************************/

template <typename Hash>
std::vector<std::string> asl_bench(const std::vector<std::string>& corpus) {
    return bench(corpus, "    adobe", [&](const std::string& element) {
        return digest_binary(Hash::digest(element.begin(), element.end()));
    });
}

/**************************************************************************************************/

inline std::vector<std::string> asl_bench_1(const std::vector<std::string>& corpus) {
    return asl_bench<adobe::sha1_t>(corpus);
}

/**************************************************************************************************/

inline std::vector<std::string> asl_bench_224(const std::vector<std::string>& corpus) {
    return asl_bench<adobe::sha224_t>(corpus);
}

/**************************************************************************************************/

inline std::vector<std::string> asl_bench_256(const std::vector<std::string>& corpus) {
    return asl_bench<adobe::sha256_t>(corpus);
}

/**************************************************************************************************/

inline std::vector<std::string> asl_bench_384(const std::vector<std::string>& corpus) {
    return asl_bench<adobe::sha384_t>(corpus);
}

/**************************************************************************************************/

inline std::vector<std::string> asl_bench_512(const std::vector<std::string>& corpus) {
    return asl_bench<adobe::sha512_t>(corpus);
}

/**************************************************************************************************/
#if 0
#pragma mark -
#endif
/**************************************************************************************************/

#if USING_OPENSSL

/**************************************************************************************************/

std::vector<std::string> openssl_bench_1(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(SHA_DIGEST_LENGTH, 0);

    return bench(corpus, "  openssl", [&](const std::string& element) {
        SHA1(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

std::vector<std::string> openssl_bench_224(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(SHA224_DIGEST_LENGTH, 0);

    return bench(corpus, "  openssl", [&](const std::string& element) {
        SHA224(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

std::vector<std::string> openssl_bench_256(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(SHA256_DIGEST_LENGTH, 0);

    return bench(corpus, "  openssl", [&](const std::string& element) {
        SHA256(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

std::vector<std::string> openssl_bench_384(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(SHA384_DIGEST_LENGTH, 0);

    return bench(corpus, "  openssl", [&](const std::string& element) {
        SHA384(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

std::vector<std::string> openssl_bench_512(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(SHA512_DIGEST_LENGTH, 0);

    return bench(corpus, "  openssl", [&](const std::string& element) {
        SHA512(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

#endif // USING_OPENSSL

/**************************************************************************************************/
#if 0
#pragma mark -
#endif
/**************************************************************************************************/

#if USING_BOOSTCRYPTO

/**************************************************************************************************/

template <typename Hash>
std::vector<std::string> boostcrypto_bench(const std::vector<std::string>& corpus) {
    return bench(corpus, "    boost", [&](const std::string& element) {
        Hash sha(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size());

        const std::uint8_t* digest(reinterpret_cast<const std::uint8_t*>(sha.digest()));

        return std::string(digest, digest + Hash::digest_length);
    });
}

/**************************************************************************************************/

inline std::vector<std::string> boostcrypto_bench_1(const std::vector<std::string>& corpus) {
    return boostcrypto_bench<boost::crypto::sha1>(corpus);
}

/**************************************************************************************************/

inline std::vector<std::string> boostcrypto_bench_224(const std::vector<std::string>& corpus) {
    return boostcrypto_bench<boost::crypto::sha224>(corpus);
}

/**************************************************************************************************/

inline std::vector<std::string> boostcrypto_bench_256(const std::vector<std::string>& corpus) {
    return boostcrypto_bench<boost::crypto::sha256>(corpus);
}

/**************************************************************************************************/

inline std::vector<std::string> boostcrypto_bench_384(const std::vector<std::string>& corpus) {
    return boostcrypto_bench<boost::crypto::sha384>(corpus);
}

/**************************************************************************************************/

inline std::vector<std::string> boostcrypto_bench_512(const std::vector<std::string>& corpus) {
    return boostcrypto_bench<boost::crypto::sha512>(corpus);
}

/**************************************************************************************************/

#endif // USING_BOOSTCRYPTO

/**************************************************************************************************/
#if 0
#pragma mark -
#endif
/**************************************************************************************************/

#if ADOBE_PLATFORM_MAC

/**************************************************************************************************/

std::vector<std::string> commoncrypto_bench_1(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(CC_SHA1_DIGEST_LENGTH, 0);

    return bench(corpus, "    apple", [&](const std::string& element) {
        CC_SHA1(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

std::vector<std::string> commoncrypto_bench_224(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(CC_SHA224_DIGEST_LENGTH, 0);

    return bench(corpus, "    apple", [&](const std::string& element) {
        CC_SHA224(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

std::vector<std::string> commoncrypto_bench_256(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(CC_SHA256_DIGEST_LENGTH, 0);

    return bench(corpus, "    apple", [&](const std::string& element) {
        CC_SHA256(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

std::vector<std::string> commoncrypto_bench_384(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(CC_SHA384_DIGEST_LENGTH, 0);

    return bench(corpus, "    apple", [&](const std::string& element) {
        CC_SHA384(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

std::vector<std::string> commoncrypto_bench_512(const std::vector<std::string>& corpus) {
    std::vector<std::uint8_t> digest(CC_SHA512_DIGEST_LENGTH, 0);

    return bench(corpus, "    apple", [&](const std::string& element) {
        CC_SHA512(reinterpret_cast<const std::uint8_t*>(&element[0]), element.size(), &digest[0]);

        return digest_binary(digest);
    });
}

/**************************************************************************************************/

#endif // ADOBE_PLATFORM_MAC

/**************************************************************************************************/

void validate(const std::vector<std::string>& x, const std::vector<std::string>& y) {
    if (x.size() != y.size())
        throw std::runtime_error("Hash vector size mismatch");

    for (std::size_t i(0); i < x.size(); ++i) {
        if (x[i] == y[i])
            continue;

        std::cout << "    x: ";
        print_binary_message(x[i]);
        std::cout << '\n';

        std::cout << "    y: ";
        print_binary_message(y[i]);
        std::cout << '\n';

        throw std::runtime_error("Digest mismatch");
    }
}

/**************************************************************************************************/

} // namespace

/**************************************************************************************************/

int main() try {
#if 0
    std::vector<std::string> corpus;

    for (std::size_t i(0); i < 4; ++i)
    {
        std::size_t sz((i + 1) * 1000000);

        for (char c('a'); c < 'z'; ++c)
            corpus.emplace_back(sz, c);

        for (char c('0'); c < '9'; ++c)
            corpus.emplace_back(sz, c);
    }
#else
    const std::size_t factor(1000000);
    std::size_t n1(1);
    std::size_t n2(1);
    std::vector<std::string> corpus;

    while (n2 <= 5702887) {
        std::size_t count(std::min<std::size_t>(1000000, n2 / factor));

        for (std::size_t i(0); i < count; ++i)
            corpus.emplace_back(n2, static_cast<char>(i));

        // advance the counters
        std::size_t n(n1 + n2);

        n1 = n2;
        n2 = n;
    }
#endif

    std::cout << "SHA-1:\n";
    std::vector<std::string> asl_result_1(asl_bench_1(corpus));
#if USING_OPENSSL
    validate(asl_result_1, openssl_bench_1(corpus));
#endif
#if ADOBE_PLATFORM_MAC
    validate(asl_result_1, commoncrypto_bench_1(corpus));
#endif // ADOBE_PLATFORM_MAC
#if USING_BOOSTCRYPTO
    validate(asl_result_1, boostcrypto_bench_1(corpus));
#endif // USING_BOOSTCRYPTO

    // It would be very surprising to find a difference in timing between
    // SHA-224 and SHA-256. They are, after all, the same fundamental algorithm.
    // But that's why we test these things, isn't it?

    std::cout << "SHA-224:\n";
    std::vector<std::string> asl_result_224(asl_bench_224(corpus));
#if USING_OPENSSL
    validate(asl_result_224, openssl_bench_224(corpus));
#endif
#if ADOBE_PLATFORM_MAC
    validate(asl_result_224, commoncrypto_bench_224(corpus));
#endif // ADOBE_PLATFORM_MAC
#if USING_BOOSTCRYPTO
    validate(asl_result_224, boostcrypto_bench_224(corpus));
#endif // USING_BOOSTCRYPTO

    std::cout << "SHA-256:\n";
    std::vector<std::string> asl_result_256(asl_bench_256(corpus));
#if USING_OPENSSL
    validate(asl_result_256, openssl_bench_256(corpus));
#endif
#if ADOBE_PLATFORM_MAC
    validate(asl_result_256, commoncrypto_bench_256(corpus));
#endif // ADOBE_PLATFORM_MAC
#if USING_BOOSTCRYPTO
    validate(asl_result_256, boostcrypto_bench_256(corpus));
#endif // USING_BOOSTCRYPTO

    // It would be very surprising to find a difference in timing between
    // SHA-384 and SHA-512. They are, after all, the same fundamental algorithm.
    // But that's why we test these things, isn't it?

    std::cout << "SHA-384:\n";
    std::vector<std::string> asl_result_384(asl_bench_384(corpus));
#if USING_OPENSSL
    validate(asl_result_384, openssl_bench_384(corpus));
#endif
#if ADOBE_PLATFORM_MAC
    validate(asl_result_384, commoncrypto_bench_384(corpus));
#endif // ADOBE_PLATFORM_MAC
#if USING_BOOSTCRYPTO
    validate(asl_result_384, boostcrypto_bench_384(corpus));
#endif // USING_BOOSTCRYPTO

    std::cout << "SHA-512:\n";
    std::vector<std::string> asl_result_512(asl_bench_512(corpus));
#if USING_OPENSSL
    validate(asl_result_512, openssl_bench_512(corpus));
#endif
#if ADOBE_PLATFORM_MAC
    validate(asl_result_512, commoncrypto_bench_512(corpus));
#endif // ADOBE_PLATFORM_MAC
#if USING_BOOSTCRYPTO
    validate(asl_result_512, boostcrypto_bench_512(corpus));
#endif // USING_BOOSTCRYPTO

    return 0;
} catch (const std::exception& error) {
    std::cerr << "Error: " << error.what() << '\n';

    return 1;
} catch (...) {
    std::cerr << "Error: unknown\n";

    return 1;
}

/**************************************************************************************************/


#include <iostream>
#include <algorithm>

#include "ggl/Graph.hh"

#include "dataTargetGraph_1.icc"

#include <boost/graph/breadth_first_search.hpp>

#include <boost/graph/visitors.hpp>
#include <boost/graph/graph_utility.hpp>

template <class GRAPH>
struct cycle_reporter
  : public boost::base_visitor<cycle_reporter<GRAPH> >
{
  typedef boost::on_gray_target event_filter;
//  typedef boost::on_black_target event_filter;
//  typedef boost::on_examine_edge event_filter;

  cycle_reporter(const std::vector<typename GRAPH::vertex_descriptor>& pred_)
   : pred(pred_)
  { }

  template <class Edge, class Graph>
  void operator()(Edge e, Graph& g) {
    std::cout <<" found cycle closing : ("<<boost::source(e, g)<<", " << boost::target(e, g) <<std::endl;
    typename GRAPH::vertex_descriptor v = boost::target(e,g);
	std::cout <<" " <<(size_t)v;
    while( pred[v] != v ){
    	v = pred[v];
    	std::cout <<" " <<(size_t)v;
    }
    std::cout <<" +";
    v = boost::source(e,g);
	std::cout <<" " <<(size_t)v;
    while( pred[v] != v ){
    	v = pred[v];
    	std::cout <<" " <<(size_t)v;
    }
    std::cout <<std::endl;
  }
protected:
	const std::vector<typename GRAPH::vertex_descriptor>& pred;
};

template <class GRAPH>
struct edge_reporter
  : public boost::base_visitor<edge_reporter<GRAPH> >
{
//  typedef boost::on_black_target event_filter;
//  typedef boost::on_examine_edge event_filter;
  typedef boost::on_tree_edge event_filter;

  edge_reporter(const std::vector<typename GRAPH::vertex_descriptor>& pred_)
   : pred(pred_)
  { }

  template <class Edge, class Graph>
  void operator()(Edge e, Graph& g) {
    std::cout <<" next edge : ("<<boost::source(e, g)<<", " << boost::target(e, g) <<std::endl;
  }
protected:
	const std::vector<typename GRAPH::vertex_descriptor>& pred;
};

template <class GRAPH>
struct shortest_path_reporter
  : public boost::base_visitor<shortest_path_reporter<GRAPH> >
{
  typedef boost::on_tree_edge event_filter;

  shortest_path_reporter(
		  	const std::vector<typename GRAPH::vertex_descriptor>& pred_
		  	, const typename GRAPH::vertex_descriptor & target_
		  	, std::vector<typename GRAPH::vertex_descriptor>& path_)
   : pred(pred_), target(target_), path(path_)
  {
  }

  template <class Edge, class Graph>
  void operator()(Edge e, Graph& g) {
	  if (boost::target(e,g) == target) {
		    typename GRAPH::vertex_descriptor v = boost::source(e,g);
		    while( pred[v] != v ){
		    	  // add to path
		    	path.push_back(v);
		    	v = pred[v];
	    	}
	  }
  }
protected:
	const std::vector<typename GRAPH::vertex_descriptor>& pred;
	const typename GRAPH::vertex_descriptor & target;
	std::vector<typename GRAPH::vertex_descriptor>& path;
};

//inline cycle_reporter
//report_cycle() {
//  return cycle_reporter();
//}

int main(int argc, char** argv) {

	std::cout	<<"\n"
				<<"==============  BEGIN TEST  ==================\n"
				<<"==============================================\n"
				<<"              ggl::CE_Horton  \n"
				<<"      detection of all cycles in graphs \n"
				<<"==============================================\n"
				<<std::endl;

	std::string graphString = "";

	std::cout <<"-->create ggl::Graph = getTarget_1()" <<std::endl;

	ggl::Graph t = getTarget_1(graphString);

	std::cout <<"\n" <<"filled graph is :\n\n" <<graphString <<std::endl;

	ggl::Graph::vertex_iterator vi,v_end;
	ggl::Graph::edge_iterator ei,e_end;

	for (boost::tie(ei,e_end)=boost::edges(t); ei!=e_end; ei++)  {
		ggl::Graph::vertex_descriptor i = boost::source(*ei,t);
		ggl::Graph::vertex_descriptor j = boost::target(*ei,t);
		for (boost::tie(vi,v_end)=boost::vertices(t); vi!=v_end; vi++)  {
			if ( *vi != i && *vi != j ) {
				std::vector<ggl::Graph::vertex_descriptor> path_v_i;
				path_v_i.push_back(*vi);
				{
					  // the predecessor mapping
					std::vector<ggl::Graph::vertex_descriptor> p(boost::num_vertices(t));
					  // The source vertex maps to itself
					p[*vi] = *vi;
					  // find path from *vi --> i
					boost::breadth_first_search
					(t, *vi,
					boost::visitor(boost::make_bfs_visitor(
							std::make_pair(
									boost::record_predecessors(&p[0],boost::on_tree_edge())
									, shortest_path_reporter<ggl::Graph>(p,i,path_v_i)))) );
				}
				path_v_i.push_back(i);
				std::vector<ggl::Graph::vertex_descriptor> path_v_j;
				{
					  // the predecessor mapping
					std::vector<ggl::Graph::vertex_descriptor> p(boost::num_vertices(t));
					  // The source vertex maps to itself
					p[*vi] = *vi;
					  // find path from *vi --> j
					boost::breadth_first_search
					(t, *vi,
					boost::visitor(boost::make_bfs_visitor(
							std::make_pair(
									boost::record_predecessors(&p[0],boost::on_tree_edge())
									, shortest_path_reporter<ggl::Graph>(p,j,path_v_j)))) );
				}
				path_v_j.push_back(j);

				bool isUnique = true;
				for (size_t ix=0; isUnique && ix<path_v_i.size(); ix++) {
					isUnique = std::find(path_v_j.begin(), path_v_j.end(), path_v_i[ix]) == path_v_j.end();
				}
				if (isUnique) {
					  // reverse last part
					std::cout <<"  = next cycle = ";
					copy(path_v_i.begin(), path_v_i.end(), std::ostream_iterator<ggl::Graph::vertex_descriptor>(std::cout, " "));
					copy(path_v_j.rbegin(), path_v_j.rend(), std::ostream_iterator<ggl::Graph::vertex_descriptor>(std::cout, " "));
					std::cout <<std::endl;
				}
			}
		}
	}

	std::cout	<<"\n"
				<<"===============  TEST END  ===================\n"
				<<"==============================================\n"
				<<std::endl;

	return 0;
}