// Pattern-defeating quicksort

//              Copyright Orson Peters 2017.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

// See http://www.boost.org/libs/sort/ for library home page.

#ifndef BOOST_SORT_PDQSORT_HPP
#define BOOST_SORT_PDQSORT_HPP

#include <algorithm>
#include <cstddef>
#include <functional>
#include <iterator>
#include <utility>
#include <boost/type_traits.hpp>

#if __cplusplus >= 201103L
    #include <cstdint>
    #define BOOST_PDQSORT_PREFER_MOVE(x) std::move(x)
#else
    #define BOOST_PDQSORT_PREFER_MOVE(x) (x)
#endif

namespace boost {
namespace sort {

namespace pdqsort_detail {
    enum {
        // Partitions below this size are sorted using insertion sort.
        insertion_sort_threshold = 24,

        // Partitions above this size use Tukey's ninther to select the pivot.
        ninther_threshold = 128,

        // When we detect an already sorted partition, attempt an insertion sort that allows this
        // amount of element moves before giving up.
        partial_insertion_sort_limit = 8,

        // Must be multiple of 8 due to loop unrolling, and < 256 to fit in unsigned char.
        block_size = 64,

        // Cacheline size, assumes power of two.
        cacheline_size = 64
    };

    template<class T> struct is_default_compare : boost::false_type { };
    template<class T> struct is_default_compare<std::less<T> > : boost::true_type { };
    template<class T> struct is_default_compare<std::greater<T> > : boost::true_type { };

    // Returns floor(log2(n)), assumes n > 0.
    template<class T>
    inline int log2(T n) {
        int log = 0;
        while (n >>= 1) ++log;
        return log;
    }

    // Sorts [begin, end) using insertion sort with the given comparison function.
    template<class Iter, class Compare>
    inline void insertion_sort(Iter begin, Iter end, Compare comp) {
        typedef typename std::iterator_traits<Iter>::value_type T;
        if (begin == end) return;

        for (Iter cur = begin + 1; cur != end; ++cur) {
            Iter sift = cur;
            Iter sift_1 = cur - 1;

            // Compare first so we can avoid 2 moves for an element already positioned correctly.
            if (comp(*sift, *sift_1)) {
                T tmp = BOOST_PDQSORT_PREFER_MOVE(*sift);

                do { *sift-- = BOOST_PDQSORT_PREFER_MOVE(*sift_1); }
                while (sift != begin && comp(tmp, *--sift_1));

                *sift = BOOST_PDQSORT_PREFER_MOVE(tmp);
            }
        }
    }

    // Sorts [begin, end) using insertion sort with the given comparison function. Assumes
    // *(begin - 1) is an element smaller than or equal to any element in [begin, end).
    template<class Iter, class Compare>
    inline void unguarded_insertion_sort(Iter begin, Iter end, Compare comp) {
        typedef typename std::iterator_traits<Iter>::value_type T;
        if (begin == end) return;

        for (Iter cur = begin + 1; cur != end; ++cur) {
            Iter sift = cur;
            Iter sift_1 = cur - 1;

            // Compare first so we can avoid 2 moves for an element already positioned correctly.
            if (comp(*sift, *sift_1)) {
                T tmp = BOOST_PDQSORT_PREFER_MOVE(*sift);

                do { *sift-- = BOOST_PDQSORT_PREFER_MOVE(*sift_1); }
                while (comp(tmp, *--sift_1));

                *sift = BOOST_PDQSORT_PREFER_MOVE(tmp);
            }
        }
    }

    // Attempts to use insertion sort on [begin, end). Will return false if more than
    // partial_insertion_sort_limit elements were moved, and abort sorting. Otherwise it will
    // successfully sort and return true.
    template<class Iter, class Compare>
    inline bool partial_insertion_sort(Iter begin, Iter end, Compare comp) {
        typedef typename std::iterator_traits<Iter>::value_type T;
        if (begin == end) return true;

        int limit = 0;
        for (Iter cur = begin + 1; cur != end; ++cur) {
            if (limit > partial_insertion_sort_limit) return false;

            Iter sift = cur;
            Iter sift_1 = cur - 1;

            // Compare first so we can avoid 2 moves for an element already positioned correctly.
            if (comp(*sift, *sift_1)) {
                T tmp = BOOST_PDQSORT_PREFER_MOVE(*sift);

                do { *sift-- = BOOST_PDQSORT_PREFER_MOVE(*sift_1); }
                while (sift != begin && comp(tmp, *--sift_1));

                *sift = BOOST_PDQSORT_PREFER_MOVE(tmp);
                limit += cur - sift;
            }
        }

        return true;
    }

    template<class Iter, class Compare>
    inline void sort2(Iter a, Iter b, Compare comp) {
        if (comp(*b, *a)) std::iter_swap(a, b);
    }

    // Sorts the elements *a, *b and *c using comparison function comp.
    template<class Iter, class Compare>
    inline void sort3(Iter a, Iter b, Iter c, Compare comp) {
        sort2(a, b, comp);
        sort2(b, c, comp);
        sort2(a, b, comp);
    }

    template<class T>
    inline T* align_cacheline(T* p) {
#if defined(UINTPTR_MAX) && __cplusplus >= 201103L
        std::uintptr_t ip = reinterpret_cast<std::uintptr_t>(p);
#else
        std::size_t ip = reinterpret_cast<std::size_t>(p);
#endif
        ip = (ip + cacheline_size - 1) & -cacheline_size;
        return reinterpret_cast<T*>(ip);
    }

    template<class Iter>
    inline void swap_offsets(Iter first, Iter last,
                             unsigned char* offsets_l, unsigned char* offsets_r,
                             int num, bool use_swaps) {
        typedef typename std::iterator_traits<Iter>::value_type T;
        if (use_swaps) {
            // This case is needed for the descending distribution, where we need
            // to have proper swapping for pdqsort to remain O(n).
            for (int i = 0; i < num; ++i) {
                std::iter_swap(first + offsets_l[i], last - offsets_r[i]);
            }
        } else if (num > 0) {
            Iter l = first + offsets_l[0]; Iter r = last - offsets_r[0];
            T tmp(BOOST_PDQSORT_PREFER_MOVE(*l)); *l = BOOST_PDQSORT_PREFER_MOVE(*r);
            for (int i = 1; i < num; ++i) {
                l = first + offsets_l[i]; *r = BOOST_PDQSORT_PREFER_MOVE(*l);
                r = last - offsets_r[i]; *l = BOOST_PDQSORT_PREFER_MOVE(*r);
            }
            *r = BOOST_PDQSORT_PREFER_MOVE(tmp);
        }
    }

    // Partitions [begin, end) around pivot *begin using comparison function comp. Elements equal
    // to the pivot are put in the right-hand partition. Returns the position of the pivot after
    // partitioning and whether the passed sequence already was correctly partitioned. Assumes the
    // pivot is a median of at least 3 elements and that [begin, end) is at least
    // insertion_sort_threshold long. Uses branchless partitioning.
    template<class Iter, class Compare>
    inline std::pair<Iter, bool> partition_right_branchless(Iter begin, Iter end, Compare comp) {
        typedef typename std::iterator_traits<Iter>::value_type T;

        // Move pivot into local for speed.
        T pivot(BOOST_PDQSORT_PREFER_MOVE(*begin));
        Iter first = begin;
        Iter last = end;

        // Find the first element greater than or equal than the pivot (the median of 3 guarantees
        // this exists).
        while (comp(*++first, pivot));

        // Find the first element strictly smaller than the pivot. We have to guard this search if
        // there was no element before *first.
        if (first - 1 == begin) while (first < last && !comp(*--last, pivot));
        else                    while (                !comp(*--last, pivot));

        // If the first pair of elements that should be swapped to partition are the same element,
        // the passed in sequence already was correctly partitioned.
        bool already_partitioned = first >= last;
        if (!already_partitioned) {
            std::iter_swap(first, last);
            ++first;
        }

        // The following branchless partitioning is derived from "BlockQuicksort: How Branch
        // Mispredictions don't affect Quicksort" by Stefan Edelkamp and Armin Weiss.
        unsigned char offsets_l_storage[block_size + cacheline_size];
        unsigned char offsets_r_storage[block_size + cacheline_size];
        unsigned char* offsets_l = align_cacheline(offsets_l_storage);
        unsigned char* offsets_r = align_cacheline(offsets_r_storage);
        int num_l, num_r, start_l, start_r;
        num_l = num_r = start_l = start_r = 0;

        while (last - first > 2 * block_size) {
            // Fill up offset blocks with elements that are on the wrong side.
            if (num_l == 0) {
                start_l = 0;
                Iter it = first;
                for (unsigned char i = 0; i < block_size;) {
                    offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it;
                    offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it;
                    offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it;
                    offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it;
                    offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it;
                    offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it;
                    offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it;
                    offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it;
                }
            }
            if (num_r == 0) {
                start_r = 0;
                Iter it = last;
                for (unsigned char i = 0; i < block_size;) {
                    offsets_r[num_r] = ++i; num_r += comp(*--it, pivot);
                    offsets_r[num_r] = ++i; num_r += comp(*--it, pivot);
                    offsets_r[num_r] = ++i; num_r += comp(*--it, pivot);
                    offsets_r[num_r] = ++i; num_r += comp(*--it, pivot);
                    offsets_r[num_r] = ++i; num_r += comp(*--it, pivot);
                    offsets_r[num_r] = ++i; num_r += comp(*--it, pivot);
                    offsets_r[num_r] = ++i; num_r += comp(*--it, pivot);
                    offsets_r[num_r] = ++i; num_r += comp(*--it, pivot);
                }
            }

            // Swap elements and update block sizes and first/last boundaries.
            int num = (std::min)(num_l, num_r);
            swap_offsets(first, last, offsets_l + start_l, offsets_r + start_r,
                         num, num_l == num_r);
            num_l -= num; num_r -= num;
            start_l += num; start_r += num;
            if (num_l == 0) first += block_size;
            if (num_r == 0) last -= block_size;
        }

        int l_size = 0, r_size = 0;
        int unknown_left = (last - first) - ((num_r || num_l) ? block_size : 0);
        if (num_r) {
            // Handle leftover block by assigning the unknown elements to the other block.
            l_size = unknown_left;
            r_size = block_size;
        } else if (num_l) {
            l_size = block_size;
            r_size = unknown_left;
        } else {
            // No leftover block, split the unknown elements in two blocks.
            l_size = unknown_left/2;
            r_size = unknown_left - l_size;
        }

        // Fill offset buffers if needed.
        if (unknown_left && !num_l) {
            start_l = 0;
            Iter it = first;
            for (unsigned char i = 0; i < l_size;) {
                offsets_l[num_l] = i++; num_l += !comp(*it, pivot); ++it;
            }
        }
        if (unknown_left && !num_r) {
            start_r = 0;
            Iter it = last;
            for (unsigned char i = 0; i < r_size;) {
                offsets_r[num_r] = ++i; num_r += comp(*--it, pivot);
            }
        }

        int num = (std::min)(num_l, num_r);
        swap_offsets(first, last, offsets_l + start_l, offsets_r + start_r, num, num_l == num_r);
        num_l -= num; num_r -= num;
        start_l += num; start_r += num;
        if (num_l == 0) first += l_size;
        if (num_r == 0) last -= r_size;

        // We have now fully identified [first, last)'s proper position. Swap the last elements.
        if (num_l) {
            offsets_l += start_l;
            while (num_l--) std::iter_swap(first + offsets_l[num_l], --last);
            first = last;
        }
        if (num_r) {
            offsets_r += start_r;
            while (num_r--) std::iter_swap(last - offsets_r[num_r], first), ++first;
            last = first;
        }

        // Put the pivot in the right place.
        Iter pivot_pos = first - 1;
        *begin = BOOST_PDQSORT_PREFER_MOVE(*pivot_pos);
        *pivot_pos = BOOST_PDQSORT_PREFER_MOVE(pivot);

        return std::make_pair(pivot_pos, already_partitioned);
    }

    // Partitions [begin, end) around pivot *begin using comparison function comp. Elements equal
    // to the pivot are put in the right-hand partition. Returns the position of the pivot after
    // partitioning and whether the passed sequence already was correctly partitioned. Assumes the
    // pivot is a median of at least 3 elements and that [begin, end) is at least
    // insertion_sort_threshold long.
    template<class Iter, class Compare>
    inline std::pair<Iter, bool> partition_right(Iter begin, Iter end, Compare comp) {
        typedef typename std::iterator_traits<Iter>::value_type T;

        // Move pivot into local for speed.
        T pivot(BOOST_PDQSORT_PREFER_MOVE(*begin));

        Iter first = begin;
        Iter last = end;

        // Find the first element greater than or equal than the pivot (the median of 3 guarantees
        // this exists).
        while (comp(*++first, pivot));

        // Find the first element strictly smaller than the pivot. We have to guard this search if
        // there was no element before *first.
        if (first - 1 == begin) while (first < last && !comp(*--last, pivot));
        else                    while (                !comp(*--last, pivot));

        // If the first pair of elements that should be swapped to partition are the same element,
        // the passed in sequence already was correctly partitioned.
        bool already_partitioned = first >= last;

        // Keep swapping pairs of elements that are on the wrong side of the pivot. Previously
        // swapped pairs guard the searches, which is why the first iteration is special-cased
        // above.
        while (first < last) {
            std::iter_swap(first, last);
            while (comp(*++first, pivot));
            while (!comp(*--last, pivot));
        }

        // Put the pivot in the right place.
        Iter pivot_pos = first - 1;
        *begin = BOOST_PDQSORT_PREFER_MOVE(*pivot_pos);
        *pivot_pos = BOOST_PDQSORT_PREFER_MOVE(pivot);

        return std::make_pair(pivot_pos, already_partitioned);
    }

    // Similar function to the one above, except elements equal to the pivot are put to the left of
    // the pivot and it doesn't check or return if the passed sequence already was partitioned.
    // Since this is rarely used (the many equal case), and in that case pdqsort already has O(n)
    // performance, no block quicksort is applied here for simplicity.
    template<class Iter, class Compare>
    inline Iter partition_left(Iter begin, Iter end, Compare comp) {
        typedef typename std::iterator_traits<Iter>::value_type T;

        T pivot(BOOST_PDQSORT_PREFER_MOVE(*begin));
        Iter first = begin;
        Iter last = end;

        while (comp(pivot, *--last));

        if (last + 1 == end) while (first < last && !comp(pivot, *++first));
        else                 while (                !comp(pivot, *++first));

        while (first < last) {
            std::iter_swap(first, last);
            while (comp(pivot, *--last));
            while (!comp(pivot, *++first));
        }

        Iter pivot_pos = last;
        *begin = BOOST_PDQSORT_PREFER_MOVE(*pivot_pos);
        *pivot_pos = BOOST_PDQSORT_PREFER_MOVE(pivot);

        return pivot_pos;
    }

    template<class Iter, class Compare, bool Branchless>
    inline void pdqsort_loop(Iter begin, Iter end, Compare comp, int bad_allowed, bool leftmost = true) {
        typedef typename std::iterator_traits<Iter>::difference_type diff_t;

        // Use a while loop for tail recursion elimination.
        while (true) {
            diff_t size = end - begin;

            // Insertion sort is faster for small arrays.
            if (size < insertion_sort_threshold) {
                if (leftmost) insertion_sort(begin, end, comp);
                else unguarded_insertion_sort(begin, end, comp);
                return;
            }

            // Choose pivot as median of 3 or pseudomedian of 9.
            diff_t s2 = size / 2;
            if (size > ninther_threshold) {
                sort3(begin, begin + s2, end - 1, comp);
                sort3(begin + 1, begin + (s2 - 1), end - 2, comp);
                sort3(begin + 2, begin + (s2 + 1), end - 3, comp);
                sort3(begin + (s2 - 1), begin + s2, begin + (s2 + 1), comp);
                std::iter_swap(begin, begin + s2);
            } else sort3(begin + s2, begin, end - 1, comp);

            // If *(begin - 1) is the end of the right partition of a previous partition operation
            // there is no element in [begin, end) that is smaller than *(begin - 1). Then if our
            // pivot compares equal to *(begin - 1) we change strategy, putting equal elements in
            // the left partition, greater elements in the right partition. We do not have to
            // recurse on the left partition, since it's sorted (all equal).
            if (!leftmost && !comp(*(begin - 1), *begin)) {
                begin = partition_left(begin, end, comp) + 1;
                continue;
            }

            // Partition and get results.
            std::pair<Iter, bool> part_result =
                Branchless ? partition_right_branchless(begin, end, comp)
                           : partition_right(begin, end, comp);
            Iter pivot_pos = part_result.first;
            bool already_partitioned = part_result.second;

            // Check for a highly unbalanced partition.
            diff_t l_size = pivot_pos - begin;
            diff_t r_size = end - (pivot_pos + 1);
            bool highly_unbalanced = l_size < size / 8 || r_size < size / 8;

            // If we got a highly unbalanced partition we shuffle elements to break many patterns.
            if (highly_unbalanced) {
                // If we had too many bad partitions, switch to heapsort to guarantee O(n log n).
                if (--bad_allowed == 0) {
                    std::make_heap(begin, end, comp);
                    std::sort_heap(begin, end, comp);
                    return;
                }

                if (l_size >= insertion_sort_threshold) {
                    std::iter_swap(begin,             begin + l_size / 4);
                    std::iter_swap(pivot_pos - 1, pivot_pos - l_size / 4);

                    if (l_size > ninther_threshold) {
                        std::iter_swap(begin + 1,         begin + (l_size / 4 + 1));
                        std::iter_swap(begin + 2,         begin + (l_size / 4 + 2));
                        std::iter_swap(pivot_pos - 2, pivot_pos - (l_size / 4 + 1));
                        std::iter_swap(pivot_pos - 3, pivot_pos - (l_size / 4 + 2));
                    }
                }

                if (r_size >= insertion_sort_threshold) {
                    std::iter_swap(pivot_pos + 1, pivot_pos + (1 + r_size / 4));
                    std::iter_swap(end - 1,                   end - r_size / 4);

                    if (r_size > ninther_threshold) {
                        std::iter_swap(pivot_pos + 2, pivot_pos + (2 + r_size / 4));
                        std::iter_swap(pivot_pos + 3, pivot_pos + (3 + r_size / 4));
                        std::iter_swap(end - 2,             end - (1 + r_size / 4));
                        std::iter_swap(end - 3,             end - (2 + r_size / 4));
                    }
                }
            } else {
                // If we were decently balanced and we tried to sort an already partitioned
                // sequence try to use insertion sort.
                if (already_partitioned && partial_insertion_sort(begin, pivot_pos, comp)
                                        && partial_insertion_sort(pivot_pos + 1, end, comp)) return;
            }

            // Sort the left partition first using recursion and do tail recursion elimination for
            // the right-hand partition.
            pdqsort_loop<Iter, Compare, Branchless>(begin, pivot_pos, comp, bad_allowed, leftmost);
            begin = pivot_pos + 1;
            leftmost = false;
        }
    }
}

/*! \brief Generic sort algorithm using random access iterators and a user-defined comparison operator.

    \details @c pdqsort is a fast generic sorting algorithm that is similar in concept to introsort
but runs faster on certain patterns. @c pdqsort is in-place, unstable, deterministic, has a worst
case runtime of <em>O(N * lg(N))</em> and a best case of <em>O(N)</em>. Even without patterns, the
quicksort has been very efficiently implemented, and @c pdqsort runs 1-5% faster than GCC 6.2's
@c std::sort. If the type being sorted is @c std::is_arithmetic and Compare is @c std::less or
@c std::greater this function will automatically use @c pdqsort_branchless for far greater speedups.

   \param[in] first Iterator pointer to first element.
   \param[in] last Iterator pointing to one beyond the end of data.
   \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.
   \pre [@c first, @c last) is a valid range.
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveAssignable">MoveAssignable</a>
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveConstructible">MoveConstructible</a>
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \return @c void.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps
   (or moves), functors, or any operations on iterators throw.
   \warning Invalid arguments cause undefined behaviour.
   \warning Throwing an exception may cause data loss.
*/
template<class Iter, class Compare>
inline void pdqsort(Iter first, Iter last, Compare comp) {
    if (first == last) return;
    pdqsort_detail::pdqsort_loop<Iter, Compare,
        pdqsort_detail::is_default_compare<typename boost::decay<Compare>::type>::value &&
        boost::is_arithmetic<typename std::iterator_traits<Iter>::value_type>::value>(
        first, last, comp, pdqsort_detail::log2(last - first));
}

/*! \brief Generic sort algorithm using random access iterators and a user-defined comparison operator.

    \details @c pdqsort_branchless is a fast generic sorting algorithm that is similar in concept to
introsort but runs faster on certain patterns. @c pdqsort_branchless is in-place, unstable,
deterministic, has a worst case runtime of <em>O(N * lg(N))</em> and a best case of <em>O(N)</em>.
Even without patterns, the quicksort has been very efficiently implemented with block based
partitioning, and @c pdqsort_branchless runs 80-90% faster than GCC 6.2's @c std::sort when sorting
small data such as integers. However, this speedup is gained by totally bypassing the branch
predictor, if your comparison operator or iterator contains branches you will most likely see little
gain or a small loss in performance.

   \param[in] first Iterator pointer to first element.
   \param[in] last Iterator pointing to one beyond the end of data.
   \param[in] comp A binary functor that returns whether the first element passed to it should go before the second in order.
   \pre [@c first, @c last) is a valid range.
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveAssignable">MoveAssignable</a>
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveConstructible">MoveConstructible</a>
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \return @c void.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps
   (or moves), functors, or any operations on iterators throw.
   \warning Invalid arguments cause undefined behaviour.
   \warning Throwing an exception may cause data loss.
*/
template<class Iter, class Compare>
inline void pdqsort_branchless(Iter first, Iter last, Compare comp) {
    if (first == last) return;
    pdqsort_detail::pdqsort_loop<Iter, Compare, true>(
        first, last, comp, pdqsort_detail::log2(last - first));
}

/*! \brief Generic sort algorithm using random access iterators.

    \details @c pdqsort is a fast generic sorting algorithm that is similar in concept to introsort
but runs faster on certain patterns. @c pdqsort is in-place, unstable, deterministic, has a worst
case runtime of <em>O(N * lg(N))</em> and a best case of <em>O(N)</em>. Even without patterns, the
quicksort partitioning has been very efficiently implemented, and @c pdqsort runs 80-90% faster than
GCC 6.2's @c std::sort. If the type being sorted is @c std::is_arithmetic this function will
automatically use @c pdqsort_branchless.

   \param[in] first Iterator pointer to first element.
   \param[in] last Iterator pointing to one beyond the end of data.
   \pre [@c first, @c last) is a valid range.
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveAssignable">MoveAssignable</a>
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveConstructible">MoveConstructible</a>
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \return @c void.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps
   (or moves), functors, or any operations on iterators throw.
   \warning Invalid arguments cause undefined behaviour.
   \warning Throwing an exception may cause data loss.
*/
template<class Iter>
inline void pdqsort(Iter first, Iter last) {
    typedef typename std::iterator_traits<Iter>::value_type T;
    pdqsort(first, last, std::less<T>());
}

/*! \brief Generic sort algorithm using random access iterators.

    \details @c pdqsort_branchless is a fast generic sorting algorithm that is similar in concept to
introsort but runs faster on certain patterns. @c pdqsort_branchless is in-place, unstable,
deterministic, has a worst case runtime of <em>O(N * lg(N))</em> and a best case of <em>O(N)</em>.
Even without patterns, the quicksort has been very efficiently implemented with block based
partitioning, and @c pdqsort_branchless runs 80-90% faster than GCC 6.2's @c std::sort when sorting
small data such as integers. However, this speedup is gained by totally bypassing the branch
predictor, if your comparison operator or iterator contains branches you will most likely see little
gain or a small loss in performance.

   \param[in] first Iterator pointer to first element.
   \param[in] last Iterator pointing to one beyond the end of data.
   \pre [@c first, @c last) is a valid range.
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveAssignable">MoveAssignable</a>
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/MoveConstructible">MoveConstructible</a>
   \pre @c RandomAccessIter @c value_type is <a href="http://en.cppreference.com/w/cpp/concept/LessThanComparable">LessThanComparable</a>
   \post The elements in the range [@c first, @c last) are sorted in ascending order.

   \return @c void.

   \throws std::exception Propagates exceptions if any of the element comparisons, the element swaps
   (or moves), functors, or any operations on iterators throw.
   \warning Invalid arguments cause undefined behaviour.
   \warning Throwing an exception may cause data loss.
*/
template<class Iter>
inline void pdqsort_branchless(Iter first, Iter last) {
    typedef typename std::iterator_traits<Iter>::value_type T;
    pdqsort_branchless(first, last, std::less<T>());
}

}
}

#undef BOOST_PDQSORT_PREFER_MOVE

#endif


#define BOOST_TEST_MODULE pcraster geo gridded_points
#include <boost/test/unit_test.hpp>
#include "geo_griddedpoints.h"
#include "geo_rasterspace.h"
#include "geo_point.h"

BOOST_AUTO_TEST_CASE(constructor)
{
  using namespace geo;

  RasterSpace space(5, 6, 10.0, 0.0, 1.0, YIncrB2T);
  GriddedPoints<Point<double, 2> > points(space);
  // BOOST_CHECK(false);
}

BOOST_AUTO_TEST_CASE(nr_points)
{
  using namespace geo;

  // Raster with one row and two columns.
  RasterSpace space(1, 2, 1.0, 0.0, 0.0, YIncrB2T);
  GriddedPoints<Point<double, 2> > points(space);

  BOOST_CHECK(points.nrPoints(0, 0) == 0);
  BOOST_CHECK(points.nrPoints(0, 1) == 0);

  // First cell is MV. Second not.
  points.setMV(0, 0);

  Point<double, 2> p;
  p[0] = 1.0;
  p[1] = 0.0;

  points.insert(p);
  points.insert(p);
  points.insert(p);

  BOOST_CHECK(points.nrPoints(0, 0) == 0);
  BOOST_CHECK(points.nrPoints(0, 1) == 3);
}

BOOST_AUTO_TEST_CASE(points)
{
  using namespace geo;

  std::vector<Point<double, 2> > points;

  {
    RasterSpace space(1, 1, 1.0, 0.0, 0.0, YIncrB2T);
    GriddedPoints<Point<double, 2> > griddedPoints(space);

    points.clear();
    griddedPoints.points(CellLoc(0, 0), 3, points);
    BOOST_CHECK(points.empty());

    griddedPoints.insert(Point<double, 2>(0.0, 0.0));
    points.clear();
    griddedPoints.points(CellLoc(0, 0), 1, points);
    BOOST_CHECK(points.size() == 1);

    points.clear();
    griddedPoints.points(CellLoc(0, 0), 3, points);
    BOOST_CHECK(points.size() == 1);

    griddedPoints.insert(Point<double, 2>(0.0, 0.0));
    points.clear();
    griddedPoints.points(CellLoc(0, 0), 1, points);
    BOOST_CHECK(points.size() == 2);

    points.clear();
    griddedPoints.points(CellLoc(0, 0), 3, points);
    BOOST_CHECK(points.size() == 2);
  }

  {
    RasterSpace space(3, 3, 1.0, 0.0, 0.0, YIncrT2B);
    GriddedPoints<Point<double, 2> > griddedPoints(space);

    points.clear();
    griddedPoints.points(CellLoc(0, 0), 1, points);
    BOOST_CHECK(points.empty());

    // Put a point in each cell.
    for(size_t row = 0; row < griddedPoints.nrRows(); ++row) {
      for(size_t col = 0; col < griddedPoints.nrCols(); ++col) {
        griddedPoints.insert(
              Point<double, 2>(row * 1.0, col * 1.0));
      }
    }

    // 5 points in a circular neighbourhood with radius 1.
    points.clear();
    griddedPoints.points(CellLoc(1, 1), 1, points);
    BOOST_CHECK(points.size() == 5);
  }
}

BOOST_AUTO_TEST_CASE(copy)
{
  using namespace geo;

  // Create and fill an object.
    RasterSpace space(3, 3, 1.0, 0.0, 0.0, YIncrT2B);
    GriddedPoints<Point<double, 2> > source(space);
    for(size_t row = 0; row < source.nrRows(); ++row) {
      for(size_t col = 0; col < source.nrCols(); ++col) {
        source.insert(Point<double, 2>(row * 1.0, col * 1.0));
      }
    }

  // Create a copy.
    GriddedPoints<Point<double, 2> > target(source);

  // Compare source and target objects.
    BOOST_CHECK(source.size() == target.size());
}


//          Copyright Gavin Band 2008 - 2012.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <string>
#include <vector>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/math/distributions/chi_squared.hpp>
#include "genfile/Error.hpp"
#include "genfile/ToGP.hpp"
#include "genfile/VariantDataReader.hpp"
#include "metro/likelihood/Multinomial.hpp"
#include "components/SNPSummaryComponent/SNPHWE.hpp"
#include "components/SNPSummaryComponent/SNPSummaryComputation.hpp"
#include "components/SNPSummaryComponent/HWEComputation.hpp"
#include "components/SNPSummaryComponent/IntensitySummaryComputation.hpp"
#include "components/SNPSummaryComponent/ClusterFitComputation.hpp"
#include "components/SNPSummaryComponent/InfoComputation.hpp"

// #define DEBUG_SNP_SUMMARY_COMPUTATION 1

namespace stats {
	namespace {
		struct AlleleCountClient: public genfile::VariantDataReader::PerSampleSetter {
			AlleleCountClient( std::vector< double >* counts ):
				m_counts( counts ),
				m_number_of_alleles( 0 )
			{
				assert( counts != 0 ) ;
			}

			~AlleleCountClient() throw() {}

			void set_counts( std::vector< double >* counts ) {
				assert( counts != 0 ) ;
				m_counts = counts ;
			}

			void initialise( std::size_t number_of_samples, std::size_t number_of_alleles ) {
				m_number_of_alleles = number_of_alleles ;
				assert( m_counts->size() == number_of_alleles ) ;
			}

			bool set_sample( std::size_t i ) {
				m_ploidy = 0 ;
				m_table = 0 ;
				return true ;
			}

			void set_number_of_entries( uint32_t ploidy, std::size_t n, OrderType const order_type, ValueType const value_type ) {
				assert( order_type == genfile::ePerUnorderedGenotype ) ;
				assert( value_type == genfile::eProbability ) ;

				std::map< uint32_t, genfile::impl::Enumeration >::iterator where = m_tables.find( ploidy ) ;
				if( where == m_tables.end() ) {
					std::pair< std::map< uint32_t, genfile::impl::Enumeration >::iterator, bool >
						result = m_tables.insert( std::make_pair( ploidy, genfile::impl::enumerate_unphased_genotypes( ploidy ) )) ;
					assert( result.second ) ;
					where = result.first ;
				}
				m_ploidy = ploidy ;
				m_table = &(where->second) ;
			}

			void set_value( std::size_t value_i, genfile::MissingValue const value ) {
				set_value( value_i, 0.0 ) ;
			}

			void set_value( std::size_t value_i, double const value ) {
				assert( value_i <= std::size_t( std::numeric_limits< uint16_t >::max() )) ;
				genfile::impl::Enumeration const& enumeration = *m_table ;
				std::size_t const& maxAlleles = enumeration.first.second ;
				assert( m_number_of_alleles <= maxAlleles ) ;
				uint16_t const encodedGenotype = enumeration.second.second[ uint16_t( value_i ) ] ;
				uint32_t const bitsPerAllele = enumeration.first.first ;
				uint16_t mask = uint16_t( 0xFFFF ) >> ( 16 - bitsPerAllele ) ;
				// Dosage of the 1st allele in the genotype is not encoded directly.
				// We compute it as the ploidy minus the dosage of other alleles.
				uint16_t dosage_of_nonref_alleles = 0 ;
				for( std::size_t allele = 1; allele < m_number_of_alleles; ++allele ) {
					uint16_t const dosage = ( encodedGenotype >> ( (allele-1) * bitsPerAllele )) & mask ;
					(*m_counts)[allele] += value * dosage ;
					dosage_of_nonref_alleles += dosage ;
				}
				(*m_counts)[0] += value * (m_ploidy - dosage_of_nonref_alleles) ;
			}

			void finalise() {}
		private:
			std::vector< double >* m_counts ;
			std::size_t m_number_of_alleles ;
			uint32_t m_ploidy ;
			std::map< uint32_t, genfile::impl::Enumeration > m_tables ;
			genfile::impl::Enumeration* m_table ;
		} ;
	}

	struct AlleleCountComputation: public SNPSummaryComputation {
	public:

		AlleleCountComputation():
			m_counter( &m_counts )
		{}

		void list_variables( NameCallback callback ) const {
			using genfile::string_utils::to_string ;
			// By default we list 10 alleles
			callback( "number_of_alleles" ) ;
			for( std::size_t i = 0; i < 10; ++i ) {
				callback( "allele" + to_string(i+1) + "_count" ) ;
			}
		}

		void operator()(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			genfile::VariantDataReader& data_reader,
			ResultCallback callback
		) {
			using genfile::string_utils::to_string ;
			m_counts = std::vector< double >( snp.number_of_alleles(), 0.0 ) ;
			m_counter.set_counts( &m_counts ) ;
			callback( "number_of_alleles", int64_t( snp.number_of_alleles() )) ;
			data_reader.get( ":genotypes:", genfile::to_GP_unphased( m_counter ) ) ;
			std::size_t i = 0 ;
			for( ; i < snp.number_of_alleles(); ++i ) {
				callback( "allele" + to_string(i+1) + "_count", m_counts[i] ) ;
			}
			for( ; i < 10; ++i ) {
				callback( "allele" + to_string(i+1) + "_count", genfile::MissingValue() ) ;
			}
		}

		std::string get_summary( std::string const& prefix, std::size_t column_width ) const {
			return prefix + "AlleleCountComputation" ;
		}
	private:
		std::vector< double > m_counts ;
		AlleleCountClient m_counter ;
	} ;

	struct AlleleFrequencyComputation: public SNPSummaryComputation
	{
		AlleleFrequencyComputation( std::string const& what ):
			m_compute_counts( what == "everything" || what == "counts" ),
			m_compute_frequencies( what == "everything" )
		{
			assert( what == "counts" || what == "everything" ) ;
		}
		void operator()(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			genfile::VariantDataReader&,
			ResultCallback callback
		) {
			if( snp.number_of_alleles() == 2 ) {
				bool const allDiploid = ( ploidy.array() == 2 ).cast< int >().sum() == ploidy.size() ;
				if( allDiploid ) {
					compute_autosomal_frequency( snp, genotypes, callback ) ;
				} else {
					compute_sex_chromosome_frequency( snp, genotypes, ploidy, callback ) ;
				}
			}
		}

		void compute_sex_chromosome_frequency(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			ResultCallback callback
		) {
			Genotypes haploid_genotypes = genotypes ;
			Genotypes diploid_genotypes = genotypes ;
			assert( std::size_t( genotypes.rows() ) == ploidy.size() ) ;

			for( int i = 0; i < genotypes.rows(); ++i ) {
				if( ploidy(i) != 1 ) {
					haploid_genotypes.row(i).setZero() ;
				}
				if( ploidy(i) != 2 ) {
					diploid_genotypes.row(i).setZero() ;
				}
			}

			double const a_allele_count = haploid_genotypes.col(0).sum()
				+ ( ( 2.0 * diploid_genotypes.col(0).sum() ) + diploid_genotypes.col(1).sum() ) ;
			double const b_allele_count = haploid_genotypes.col(1).sum()
				+ ( ( 2.0 * diploid_genotypes.col(2).sum() ) + diploid_genotypes.col(1).sum() ) ;

			if( m_compute_counts ) {
				callback( "alleleA_count", a_allele_count ) ;
				callback( "alleleB_count", b_allele_count ) ;
			}

			if( m_compute_frequencies ) {
				double const total_allele_count = ( haploid_genotypes.sum() + 2.0 * diploid_genotypes.sum() ) ;
				double const a_allele_freq = a_allele_count / total_allele_count ;
				double const b_allele_freq = b_allele_count / total_allele_count ;

				callback( "alleleA_frequency", a_allele_freq ) ;
				callback( "alleleB_frequency", b_allele_freq ) ;

				if( a_allele_freq < b_allele_freq ) {
					callback( "minor_allele_frequency", a_allele_freq ) ;
					callback( "minor_allele", snp.get_allele(0) ) ;
					callback( "major_allele", snp.get_allele(1) ) ;
				}
				else if( a_allele_freq > b_allele_freq ) {
					callback( "minor_allele_frequency", b_allele_freq ) ;
					callback( "minor_allele", snp.get_allele(1) ) ;
					callback( "major_allele", snp.get_allele(0) ) ;
				} else {
					callback( "minor_allele_frequency", a_allele_freq ) ;
				}
			}
		}

		void compute_autosomal_frequency( VariantIdentifyingData const& snp, Genotypes const& genotypes, ResultCallback callback ) {
			double const a_allele_count = ( 2.0 * genotypes.col(0).sum() ) + genotypes.col(1).sum() ;
			double const b_allele_count = ( 2.0 * genotypes.col(2).sum() ) + genotypes.col(1).sum() ;

			if( m_compute_counts ) {
				callback( "alleleA_count", a_allele_count ) ;
				callback( "alleleB_count", b_allele_count ) ;
			}

			if( m_compute_frequencies ) {
				double const total_allele_count = ( 2.0 * genotypes.sum() ) ;
				double const a_allele_freq = a_allele_count / total_allele_count ;
				double const b_allele_freq = b_allele_count / total_allele_count ;

				callback( "alleleA_frequency", a_allele_freq ) ;
				callback( "alleleB_frequency", b_allele_freq ) ;

				if( a_allele_freq < b_allele_freq ) {
					callback( "minor_allele_frequency", a_allele_freq ) ;
					callback( "minor_allele", snp.get_allele(0) ) ;
					callback( "major_allele", snp.get_allele(1) ) ;
				}
				else if( a_allele_freq > b_allele_freq ) {
					callback( "minor_allele_frequency", b_allele_freq ) ;
					callback( "minor_allele", snp.get_allele(1) ) ;
					callback( "major_allele", snp.get_allele(0) ) ;
				} else {
					callback( "minor_allele_frequency", a_allele_freq ) ;
				}
			}
		}

		std::string get_summary( std::string const& prefix, std::size_t column_width ) const {
			return prefix + "AlleleFrequencyComputation" ;
		}
	private:
		bool const m_compute_counts ;
		bool const m_compute_frequencies ;
	} ;

	// What proportion of the mass on a genotype is due to high-confidence calls?
	struct CallMassComputation: public SNPSummaryComputation
	{

		CallMassComputation( double const threshhold = 0.9 ):
			m_threshhold(threshhold)
		{}

		void operator()( VariantIdentifyingData const& snp, Genotypes const& genotypes, Ploidy const& ploidy, genfile::VariantDataReader&, ResultCallback callback ) {
			bool const allDiploid = ( ploidy.array() == 2 ).cast< int >().sum() == ploidy.size() ;
			if( allDiploid ) {
				compute_autosomal_call_mass( snp, genotypes, callback ) ;
			} else {
				return ;
			}
		}

		void compute_autosomal_call_mass( VariantIdentifyingData const& snp, Genotypes const& genotypes, ResultCallback callback ) {
			Eigen::VectorXd const masses = genotypes.colwise().sum() ;
			m_hcGenotypes = ( genotypes.array() > m_threshhold ).cast< double >()  * genotypes.array() ;
			Eigen::VectorXd const hcMasses = m_hcGenotypes.colwise().sum() ;

			callback( "AA_mass_propn", hcMasses(0)/masses(0) ) ;
			callback( "AB_mass_propn", hcMasses(1)/masses(1) ) ;
			callback( "BB_mass_propn", hcMasses(2)/masses(2) ) ;

			callback( "non-AA-mass_propn", (hcMasses(1)+hcMasses(2))/(masses(1)+masses(2))) ;
			callback( "non-BB-mass_propn", (hcMasses(1)+hcMasses(0))/(masses(1)+masses(0))) ;
		}

		std::string get_summary( std::string const& prefix, std::size_t column_width ) const {
			return prefix + "CallMassComputation" ;
		}
	private:
		double const m_threshhold ;
		Genotypes m_hcGenotypes ;
	} ;

	struct MissingnessComputation: public SNPSummaryComputation {
		MissingnessComputation( double call_threshhold = 0.9 ): m_call_threshhold( call_threshhold ) {}
		void operator()(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			genfile::VariantDataReader&,
			ResultCallback callback
		) {
			assert( std::size_t( genotypes.rows() ) == ploidy.size() ) ;

			double missingness = double( genotypes.rows() ) - genotypes.array().sum() ;
			callback( "missing_proportion", missingness / double( genotypes.rows() ) ) ;

			bool const allDiploid = ( ploidy.array() == 2 ).cast< int >().sum() == ploidy.size() ;
			if( allDiploid ) {
				callback( "A", 0 ) ;
				callback( "B", 0 ) ;
				if( snp.number_of_alleles() == 2 ) {
					callback( "AA", genotypes.col(0).sum() ) ;
					callback( "AB", genotypes.col(1).sum() ) ;
					callback( "BB", genotypes.col(2).sum() ) ;
				}
				callback( "NULL", genotypes.rows() - genotypes.sum() ) ;
			} else {
				compute_haploid_diploid_counts( snp, genotypes, ploidy, callback ) ;
			}
			callback( "total", genfile::VariantEntry::Integer( genotypes.rows() )) ;
		}

		void compute_haploid_diploid_counts(
			VariantIdentifyingData const& snp,
			Genotypes const& genotypes,
			Ploidy const& ploidy,
			ResultCallback callback
		) {
			std::map< int, Eigen::VectorXd > counts ;
			std::map< int, double > null_counts ;
			counts[ -1 ] = Eigen::VectorXd::Zero( 3 ) ;
			counts[ 0 ] = Eigen::VectorXd::Zero( 3 ) ;
			counts[ 1 ] = Eigen::VectorXd::Zero( 3 ) ;
			counts[ 2 ] = Eigen::VectorXd::Zero( 3 ) ;
			std::map< int, std::size_t > sample_counts ;

			for( std::size_t i = 0; i < ploidy.size(); ++i ) {
				counts[ ploidy(i) ] += genotypes.row( i ) ;
				null_counts[ ploidy(i) ] += ( 1 - genotypes.row(i).sum() ) ;
				++sample_counts[ ploidy(i) ] ;
#if DEBUG_SNP_SUMMARY_COMPUTATION
				if( ploidy(i) == 1 && genotypes(i,2) != 0 ) {
					std::cerr << "! ( MissingnessComputation::compute_sex_chromosome_counts() ): individual " << (i+1) << "is male but has genotype " << genotypes.row(i) << "!!\n" ;
				}
#endif
			}

			callback( "A", counts[ 1 ]( 0 ) ) ;
			callback( "B", counts[ 1 ]( 1 ) ) ;
			callback( "AA", counts[ 2 ]( 0 ) ) ;
			callback( "AB", counts[ 2 ]( 1 ) ) ;
			callback( "BB", counts[ 2 ]( 2 ) ) ;
			callback( "NULL", null_counts[ 'm' ] + null_counts[ 'f' ] ) ;
			callback( "unknown_ploidy", counts[ -1 ].sum() + null_counts[ -1 ] ) ;
			assert( counts[ 1 ]( 2 ) == 0 ) ;
		}

		std::string get_summary( std::string const& prefix = "", std::size_t column_width = 20 ) const { return prefix + "MissingnessComputation" ; }
	private:
		double const m_call_threshhold ;
	} ;

	SNPSummaryComputation::UniquePtr SNPSummaryComputation::create(
		std::string const& name
	) {
		UniquePtr result ;
		if( name == "allele-frequencies" ) { result.reset( new stats::AlleleFrequencyComputation( "everything" )) ; }
		else if( name == "allele-counts" ) { result.reset( new stats::AlleleFrequencyComputation( "counts" )) ; }
		else if( name == "HWE" ) { result.reset( new stats::HWEComputation()) ; }
		else if( name == "missingness" ) { result.reset( new stats::MissingnessComputation()) ; }
		else if( name == "info" ) { result.reset( new stats::InfoComputation()) ; }
		else if( name == "call-mass-proportion" ) { result.reset( new stats::CallMassComputation()) ; }
		else if( name == "intensity-stats" ) { result.reset( new stats::IntensitySummaryComputation() ) ; }
		else if( name == "multi-allele-counts" ) { result.reset( new stats::AlleleCountComputation() ) ; }
		else {
			throw genfile::BadArgumentError( "SNPSummaryComputation::create()", "name=\"" + name + "\"" ) ;
		}
		return result ;
	}
}


/******************************************************************************
*       SOFA, Simulation Open-Framework Architecture, development version     *
*                (c) 2006-2017 INRIA, USTL, UJF, CNRS, MGH                    *
*                                                                             *
* This program is free software; you can redistribute it and/or modify it     *
* under the terms of the GNU Lesser General Public License as published by    *
* the Free Software Foundation; either version 2.1 of the License, or (at     *
* your option) any later version.                                             *
*                                                                             *
* 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 GNU Lesser General Public License *
* for more details.                                                           *
*                                                                             *
* You should have received a copy of the GNU Lesser General Public License    *
* along with this program. If not, see <http://www.gnu.org/licenses/>.        *
*******************************************************************************
* Authors: The SOFA Team and external contributors (see Authors.txt)          *
*                                                                             *
* Contact information: contact@sofa-framework.org                             *
******************************************************************************/
#include "ManifoldEdgeSetTopologyContainer.h"

#include <sofa/core/visual/VisualParams.h>

#include <sofa/core/ObjectFactory.h>
// Use BOOST GRAPH LIBRARY :

#include <boost/config.hpp>
#include <iostream>
#include <vector>
#include <utility>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>

#include <boost/graph/cuthill_mckee_ordering.hpp>
#include <boost/graph/bandwidth.hpp>

namespace sofa
{

namespace component
{

namespace topology
{

using namespace std;
using namespace sofa::defaulttype;

SOFA_DECL_CLASS(ManifoldEdgeSetTopologyContainer)
int ManifoldEdgeSetTopologyContainerClass = core::RegisterObject("ManifoldEdge set topology container")
        .add< ManifoldEdgeSetTopologyContainer >()
        ;

ManifoldEdgeSetTopologyContainer::ManifoldEdgeSetTopologyContainer()
    : EdgeSetTopologyContainer( )
{}

void ManifoldEdgeSetTopologyContainer::init()
{
    // load edges
    EdgeSetTopologyContainer::init();

    // the edgesAroundVertex is needed to recognize if the edgeSet is manifold
    createEdgesAroundVertexArray();

    computeConnectedComponent();
    checkTopology();
}

void ManifoldEdgeSetTopologyContainer::createEdgesAroundVertexArray()
{
    if(!hasEdges())	//  this method should only be called when edges exist
    {
#ifndef NDEBUG
        sout << "Warning. [ManifoldEdgeSetTopologyContainer::createEdgesAroundVertexArray] edge array is empty." << endl;
#endif
        createEdgeSetArray();
    }

    if(hasEdgesAroundVertex())
    {
        clearEdgesAroundVertex();
    }

    m_edgesAroundVertex.resize( getNbPoints() );

    helper::ReadAccessor< Data< sofa::helper::vector<Edge> > > m_edge = d_edge;
    for (unsigned int edge = 0; edge < m_edge.size(); ++edge)
    {
        // check to how many edges is the end vertex of each edge connnected to
        unsigned int size1 = m_edgesAroundVertex[ m_edge[edge][1] ].size();

        // adding edge i in the edge shell of both points, while respecting the manifold orientation
        // (ie : the edge will be added in second position for its first extremity point, and in first position for its second extremity point)

        m_edgesAroundVertex[ m_edge[edge][0] ].push_back( edge );

        if(size1==0)
        {
            m_edgesAroundVertex[ m_edge[edge][1] ].push_back( edge );
        }
        else if(size1==1)
        {
            unsigned int nextEdge = m_edgesAroundVertex[ m_edge[edge][1] ][0];
            m_edgesAroundVertex[ m_edge[edge][1] ][0] = edge;
            m_edgesAroundVertex[ m_edge[edge][1] ].push_back( nextEdge );
        }
        else
        {
            // not manifold !!!
            m_edgesAroundVertex[ m_edge[edge][1] ].push_back( edge );

            sout << "Error. [ManifoldEdgeSetTopologyContainer::createEdgesAroundVertexArray] The given EdgeSet is not manifold." << endl;
        }
    }
}

// Return the number of connected components
int ManifoldEdgeSetTopologyContainer::getNumberConnectedComponents(sofa::helper::vector<unsigned int>& components) // const
{
    computeConnectedComponent();

    components = m_ComponentVertexArray;
    return m_ConnectedComponentArray.size();
}

void ManifoldEdgeSetTopologyContainer::computeConnectedComponent()
{
    using namespace boost;
    typedef adjacency_list <vecS, vecS, undirectedS> Graph;

    if(isvoid_ConnectedComponent())
    {

        Graph G;

        const sofa::helper::vector<Edge> &ea=getEdgeArray();
        for (unsigned int k=0; k<ea.size(); ++k)
        {
            add_edge(ea[k][0], ea[k][1], G);
        }

        m_ComponentVertexArray.resize(num_vertices(G));
        int num = connected_components(G, &m_ComponentVertexArray[0]);

        std::vector< std::vector<int> > components(num);
        for(int i=0; i<num; i++)
        {
            components[i].resize(4);
            components[i][0]=0;
            components[i][1]=-1;
            components[i][2]=-1;
            components[i][3]=-1;
        }

        for(unsigned int j=0; j<m_ComponentVertexArray.size(); j++)
        {

            components[m_ComponentVertexArray[j]][0]+=1;
            components[m_ComponentVertexArray[j]][1]=j;

            if((getEdgesAroundVertex(j)).size()==1)
            {

                if((getEdge((getEdgesAroundVertex(j))[0]))[0]==j)
                {
                    components[m_ComponentVertexArray[j]][2]=j;
                }
                else   // (getEdge((getEdgesAroundVertex(j))[0]))[1]==j
                {
                    components[m_ComponentVertexArray[j]][3]=j;
                }
            }
        }

        for(int i=0; i<num; i++)
        {

            bool is_closed = (components[i][2]==-1 && components[i][3]==-1);
            if(is_closed)
            {
                components[i][2]=components[i][1];
            }
            ConnectedComponent cc= ConnectedComponent(components[i][2], components[i][3], components[i][0], i);
            m_ConnectedComponentArray.push_back(cc);
        }

    }
    else
    {
        return;
    }
}

bool ManifoldEdgeSetTopologyContainer::checkTopology() const
{
#ifndef NDEBUG
    bool ret = true;

    if (hasEdgesAroundVertex())
    {
        for (unsigned int i=0; i<m_edgesAroundVertex.size(); ++i)
        {
            const sofa::helper::vector<unsigned int> &es = m_edgesAroundVertex[i];

            if(es.size() != 1 && es.size() != 2)
            {
                //serr << "ERROR: ManifoldEdgeSetTopologyContainer::checkTopology() fails ."<<sendl;
                std::cout << "*** CHECK FAILED : check_manifold_edge_vertex_shell, i = " << i << std::endl;
                ret = false;
            }
        }
    }

    return ret &&  EdgeSetTopologyContainer::checkTopology();
#else
    return true;
#endif
}

void ManifoldEdgeSetTopologyContainer::clear()
{
    m_ComponentVertexArray.clear();
    m_ConnectedComponentArray.clear();

    EdgeSetTopologyContainer::clear();
}

} // namespace topology

} // namespace component

} // namespace sofa


#include "simulationsem.h"

#include <iostream>

#include <boost/filesystem.hpp>

#include <opencv2/highgui/highgui.hpp>

#include "elm/core/signal.h"
#include "elm/core/cv/mat_utils.h"
#include "elm/core/inputname.h"
#include "elm/encoding/populationcode_derivs/mutex_populationcode.h"
#include "elm/io/readmnist.h"
#include "elm/layers/layer_y.h"
#include "sem/layers/layerfactorysem.h"
#include "sem/layers/layer_z.h"

using namespace cv;
using namespace std;
namespace bfs=boost::filesystem;
using namespace elm;

const string SimulationSEM::NAME_STIMULUS  = "stimulus";
const string SimulationSEM::NAME_POP_CODE  = "pc";
const string SimulationSEM::NAME_SPIKES_Y  = "y";
const string SimulationSEM::NAME_SPIKES_Z  = "z";
const string SimulationSEM::NAME_WEIGHTS   = "w";

SimulationSEM::SimulationSEM()
    : nb_learners_(40)
{
    pop_code_ = InitPopulationCode();
    y_ = InitLayerY();
}

void SimulationSEM::Learn()
{
    ReadMNISTImages r;
    //bfs::path p("C:\\Users\\woodstock\\dev\\data\\MNIST\\train-images.idx3-ubyte");
    //bfs::path p("/media/206CDC456CDC177E/Users/woodstock/dev/data/MNIST/train-images.idx3-ubyte");
    bfs::path p("/media/win/Users/woodstock/dev/data/MNIST/t10k-images.idx3-ubyte");
    r.ReadHeader(p.string().c_str());

    Signal sig;

    while(!r.Is_EOF()) {

        sig.Clear();

        Mat1f img = r.Next();

        sig.Append(NAME_STIMULUS, img);

        pop_code_->Activate(sig);
        pop_code_->Response(sig);

        const int T=20;
        for(int t=0; t<T; t++) {

            y_->Activate(sig);
            y_->Response(sig);

            if(!z_) {

                z_ = InitLearners(static_cast<int>(sig.MostRecentMat1f(NAME_SPIKES_Y).total()), 10);
            }

            z_->Activate(sig);
            dynamic_pointer_cast<base_LearningLayer>(z_)->Learn();
        }

        z_->Clear(); // clear before moving on to the next stimulus
    }
}

void SimulationSEM::Test()
{
    ReadMNISTImages r;
    //bfs::path p("C:\\Users\\woodstock\\dev\\data\\MNIST\\t10k-images.idx3-ubyte");
    bfs::path p("/media/win/Users/woodstock/dev/data/MNIST/t10k-images.idx3-ubyte");
    r.ReadHeader(p.string().c_str());

    Signal sig;

    while(!r.Is_EOF()) {

        sig.Clear();

        Mat1f img = r.Next();
        //cv::imshow("i", img);
        sig.Append(NAME_STIMULUS, img);

        pop_code_->Activate(sig);
        pop_code_->Response(sig);

        y_->Activate(sig);
        y_->Response(sig);

        z_->Activate(sig);
    }
}

void SimulationSEM::Eval()
{
    Signal signal;
    z_->Response(signal);
    SimulationSEM::VisualizeOnOffWeights(signal.MostRecentMat1f(NAME_WEIGHTS));
}

LayerShared SimulationSEM::InitPopulationCode() const
{
    LayerConfig cfg;
    cfg.Input(MutexPopulationCode::KEY_INPUT_STIMULUS, NAME_STIMULUS);
    cfg.Output(MutexPopulationCode::KEY_OUTPUT_POP_CODE, NAME_POP_CODE);

    return LayerFactorySEM::CreateShared("MutexPopulationCode", cfg, cfg);
}

LayerShared SimulationSEM::InitLayerY() const
{
    PTree params;
    params.put(LayerY::PARAM_FREQ, 1000.f);
    params.put(LayerY::PARAM_DELTA_T_MSEC, 1.f);

    LayerConfig cfg;
    cfg.Params(params);

    LayerIONames io;
    io.Input(LayerY::KEY_INPUT_STIMULUS, NAME_POP_CODE);
    io.Output(LayerY::KEY_OUTPUT_RESPONSE, NAME_SPIKES_Y);

    return LayerFactorySEM::CreateShared("LayerY", cfg, io);
}

LayerShared SimulationSEM::InitLearners(int nb_features, int history_length) const
{
    PTree params;
    params.put(LayerZ::PARAM_NB_AFFERENTS, nb_features);
    params.put(LayerZ::PARAM_LEN_HISTORY, history_length);
    params.put(LayerZ::PARAM_NB_OUTPUT_NODES, nb_learners_);
    LayerConfig cfg;
    cfg.Params(params);

    cfg.Input(LayerZ::KEY_INPUT_SPIKES, NAME_SPIKES_Y);
    cfg.Output(LayerZ::KEY_OUTPUT_SPIKES, NAME_SPIKES_Z);
    cfg.Output(LayerZ::KEY_OUTPUT_WEIGHTS, NAME_WEIGHTS);

    return LayerFactorySEM::CreateShared("LayerZ", cfg, cfg);
}

void SimulationSEM::VisualizeOnOffWeights(const Mat1f &weights)
{
    // these weights are interlaced in wether they represent on or off activity
    // we'll reshape the matrix for easier visualization
    Mat1f weights_exp;
    exp(weights, weights_exp); // weights are initially in log scale

    for(size_t i=0; i<nb_learners_; i++) {

        Mat1f w = weights_exp.row(i);
        w = w.reshape(1, static_cast<int>(w.total()/2.));

        Mat1f w_on = w.col(0);
        Mat1f w_off = w.col(1);

        //cout<<w_on.t()<<endl;
        //cout<<w_off.t()<<endl;

        imshow("on", elm::ConvertTo8U(w_on).reshape(1, 28));
        imshow("off", elm::ConvertTo8U(w_off).reshape(1, 28));
        waitKey();
    }
}


/**
 * $Id$
 *
 * Copyright (C)
 * 2015 - $Date$
 *     Martin Wolf <ndhist@martin-wolf.org>
 *
 * This file is distributed under the BSD 2-Clause Open Source License
 * (See LICENSE file).
 *
 */
#ifndef NDHIST_STATS_MEDIAN_HPP_INCLUDED
#define NDHIST_STATS_MEDIAN_HPP_INCLUDED 1

#include <boost/python.hpp>

#include <ndhist/ndhist.hpp>

namespace ndhist {
namespace stats {
namespace py {

/**
 * @brief Calculates the median value along the given axis of the given ndhist
 *     object.
 *     It generates a projection along the given axis and then calculates the
 *     median value.
 *     The median value is defined as the axis value where half of the sum of
 *     the weights are below and above that value.
 *     If None is given as axis, the median value for all axes of the ndhist
 *     object will be calculated and returned as a tuple. But if the
 *     dimensionality of the ndhist object is 1, a scalar value is returned.
 *
 * @note This function is only defined for ndhist objects with POD type axis
 *     values AND POD type weight values.
 */
boost::python::object
median(
    ndhist const & h
  , boost::python::object const & axis = boost::python::object()
);

}// namespace py
}// namespace stats
}// namespace ndhist

#endif // !NDHIST_STATS_MEDIAN_HPP_INCLUDED


#include <cmath>

#include <boost/scoped_ptr.hpp>
/*#include <boost/accumulators/accumulators.hpp>
 #include <boost/accumulators/statistics/stats.hpp>
 #include <boost/accumulators/statistics/min.hpp>
 #include <boost/accumulators/statistics/mean.hpp>
 #include <boost/accumulators/statistics/variance.hpp>
 #include <boost/accumulators/statistics/max.hpp>*/

#include <BnSimulator/util/utility.hpp>
#include <BnSimulator/util/state_util.hpp>
#include <BnSimulator/core/BooleanNetwork.hpp>
#include <BnSimulator/experiment/DamianiPlotter.hpp>

namespace bn {

using namespace std;

using namespace boost;

DamianiPlotter::PlotData DamianiPlotter::computePlot(BooleanNetwork& net,
		const size_t order) const {
	assert(order > 0);
	PlotData data(maxSteps);
	for (size_t p = 0; p < probes; ++p) {
		State s0 = util::random_state(net.size());
		State sx = util::perturb_state_randomly(s0, order);
		assert(util::hamming_distance(s0, sx) == order);
		scoped_ptr<vector<State> > t0(traceNetwork(net, s0));
		scoped_ptr<vector<State> > tx(traceNetwork(net, sx));
		for (size_t i = 0; i < maxSteps; ++i) {
			data[i] += (static_cast<double> (util::hamming_distance((*t0)[i],
					(*tx)[i])) / net.size()) / probes;
		}
	}
	return data;
}

DamianiPlotter::PlotData DamianiPlotter::computePlot(BooleanNetwork& net,
		const double d) const {
	assert(d >= 0 && d <= 1);
	return computePlot(net,
			static_cast<size_t> (std::floor(net.size() * d)));
}

vector<State>* DamianiPlotter::traceNetwork(BooleanNetwork& net,
		const State& s0) const {
	net.setState(s0);
	vector<State>* trajectory = new vector<State> (maxSteps);
	for (size_t t = 0; t < maxSteps; ++t, net.update()) {
		(*trajectory)[t] = net.getState();
	}
	return trajectory;
}

} // namespace bn


#include <boost/multiprecision/gmp.hpp>


/*
 * Test oda boost::gregorian::to_iso_extended_string_type
 * Dmitriy Vetutnev, ODANT 2020
*/

#include <boost/date_time.hpp>
#include <boost/locale/encoding_utf.hpp>

#include <string>
#include <iostream>
#include <cstdlib>

int main(int, char**) {

    const auto time = boost::posix_time::second_clock::local_time();
    const auto date = boost::gregorian::to_iso_extended_string_type<wchar_t>(time.date());

    const std::string result = boost::locale::conv::utf_to_utf<char>(date);
    std::cout << result << std::endl;

    return EXIT_SUCCESS;
}


#ifndef plane_calibration_SRC_CALIBRATION_VALIDATION_HPP_
#define plane_calibration_SRC_CALIBRATION_VALIDATION_HPP_

#include <memory>
#include <utility>
#include <Eigen/Dense>

#include "camera_model.hpp"
#include "calibration_parameters.hpp"
#include "visualizer_interface.hpp"

namespace plane_calibration
{

class CalibrationValidation
{
public:
  class Config
  {
  public:
    Config()
    {
      too_low_buffer = 0.0;
      max_too_low_ratio = 0.0;
      max_mean = 0.0;
    }

    double too_low_buffer;
    double max_too_low_ratio;
    double max_mean;
  };

  CalibrationValidation(const CameraModel& camera_model, const CalibrationParametersPtr& parameters,
                        const Config& config, const VisualizerInterfacePtr& depth_visualizer);

  void updateConfig(const Config& new_config);

  bool angleOffsetValid(const std::pair<double, double>& angles);
  bool groundPlaneFitsData(const Eigen::MatrixXf& ground_plane, const Eigen::MatrixXf& data, const bool& debug = false);
  bool groundPlaneHasDataBelow(const Eigen::MatrixXf& ground_plane, const Eigen::MatrixXf& data, const bool& debug =
                                   false);

protected:
  void getDifferenceAndNotNanCount(const Eigen::MatrixXf& ground_plane, const Eigen::MatrixXf& data,
                                   Eigen::MatrixXf& difference, int& not_nan_count);
  bool checkTooLow(const Eigen::MatrixXf& difference, const int& not_nan_count, double& too_low_ratio);

  mutable std::mutex mutex_;
  CameraModel camera_model_;
  CalibrationParametersPtr parameters_;
  Config config_;
  VisualizerInterfacePtr depth_visualizer_;

  Eigen::MatrixXf last_ground_plane_;
};
typedef std::shared_ptr<CalibrationValidation> CalibrationValidationPtr;

} /* end namespace */

#endif


#ifndef _NCTX_PY_GRAPH
#define _NCTX_PY_GRAPH

#include "pythonmodule.hpp"
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/reverse_graph.hpp>
#include "python_cpp/iterator_edges.hpp"
#include "python_cpp/iterator_vertices.hpp"
#include "python_cpp/iterator_adjacent.hpp"

namespace nctx { namespace python {

  template<bool directed>
  class GraphContainer{

    public:
      typedef typename std::conditional<directed,
          gDirected,
          gUndirected>::type Graph;
      typedef typename b::graph_traits<Graph>::vertex_descriptor vertex;
      typedef typename b::graph_traits<Graph>::edge_descriptor edge;

      typedef typename b::reverse_graph<Graph, Graph&> reverse;
      typedef typename b::graph_traits< reverse >::adjacency_iterator parents;

      GraphContainer(){};

      size_t num_vertices(){
        return b::num_vertices(g);
      }
      size_t num_edges(){
        return b::num_edges(g);
      }

      boost::iterator_range<boost::range_detail::integer_iterator<unsigned long> > make_vertex_iterator(){
        return b::make_iterator_range(b::vertices(g));
      }
      v_iterator<size_t, Graph> get_vertex_iterator(){
        auto p = b::vertices(g);
        return v_iterator<size_t,Graph>(p.first, p.second);
      }
      a_iterator<size_t, Graph> get_children_iterator(const vertex v){
        auto p = b::adjacent_vertices(v,g);
        return a_iterator<size_t, Graph>(p.first, p.second);
      }
      a_iterator<size_t, reverse> get_parent_iterator(const vertex v){
        reverse g_tmp = make_reverse_graph(g);
        auto p = b::adjacent_vertices(v,g_tmp);
        return a_iterator<size_t, reverse>(p.first, p.second);
      }
      e_iterator<Graph> get_edge_iterator(){
        auto p = b::edges(g);
        return e_iterator<Graph>(p.first, p.second, num_edges());
      }
      e_iterator<Graph, typename b::graph_traits<Graph>::in_edge_iterator> get_inedge_iterator(const vertex v){
        auto p = b::in_edges(v, g);
        return e_iterator<Graph, typename b::graph_traits<Graph>::in_edge_iterator>(p.first, p.second, p.second - p.first);
      }
      e_iterator<Graph, typename b::graph_traits<Graph>::out_edge_iterator> get_outedge_iterator(const vertex v){
        auto p = b::out_edges(v, g);
        return e_iterator<Graph, typename b::graph_traits<Graph>::out_edge_iterator>(p.first, p.second, p.second - p.first);
      }

      size_t source(Edge<Graph>& e){
        return b::source(e.get_edge(), g);
      }
      size_t target(Edge<Graph>& e){
        return b::target(e.get_edge(), g);
      }
      vertex add_vertex(){
        return b::add_vertex(g);
      }
      bool add_edge(vertex v0, vertex v1){
        std::pair<edge, bool> result = b::add_edge(v0,v1,g);
        return result.second;
      }

      auto make_index_map(){
        return b::get(b::vertex_index, g);
      }

      Graph& get_graph(){
        return g;
      }
    private:
      Graph g;
  };

}}

#endif


#include <boost/python.hpp>
#include "boost/python/extract.hpp"
#include "boost/python/numeric.hpp"
#include <numpy/ndarrayobject.h>
#include <iostream>
#include <string>
#include <stdlib.h>

#include "MERCLazyGreedy.h"
#include "MERCInputImage.h"
#include "MERCOutputImage.h"
#include "Image.h"
#include "ImageIO.h"

using namespace boost::python;
using namespace std;
class ErsPy {
public:
	ErsPy() { lambda_ = 0.5; sigma_ = 5.0; }
	double lambda_;
	double sigma_;
	void SetLambda(double lambda) { lambda_ = lambda; };
	void SetSigma(double sigma) { sigma_ = sigma; };
	boost::python::object ComputeSegmentation(boost::python::numeric::array &inputImg, int nC) {
		PyArrayObject* pyImg = (PyArrayObject*)PyArray_FROM_O(inputImg.ptr());
		int rgbFlag = (pyImg->nd == 3) ? 1 : 0;
		//std::cout << plim->nd;
		int height = *(pyImg->dimensions);
		int width  = *(pyImg->dimensions + 1);
		int conn8 = 1;
		int kernel = 0;
		uchar* imgData = (uchar *)pyImg->data;
		Image<uchar> inputImage;
		MERCInputImage<uchar> input;
		// Create Iamge
		inputImage.Resize(width, height, false);
		// Read the image from MATLAB
		for (int col = 0; col < width; col++)
			for (int row = 0; row < height; row++)
				inputImage.Access(col, row) = imgData[row*width + col];
		// Read the image for segmentation
		input.ReadImage(&inputImage, conn8);
		// Entropy rate superpixel segmentation
		MERCLazyGreedy merc;
		merc.ClusteringTreeIF(input.nNodes_, input, kernel, sigma_, lambda_*1.0*nC, nC);
		vector<int> label = MERCOutputImage::DisjointSetToLabel(merc.disjointSet_);
		float *out = new float[height*width];
		for (int col = 0; col < width; col++)
			for (int row = 0; row < height; row++)
				out[col + row*width] = (float)label[col + row*width];
		int ndims = 2;
		npy_intp dims[2];
		//dims[0] = height; dims[1] = width; dims[2] = nDisp;
		dims[0] = height; dims[1] = width;
		PyObject * pyObj = PyArray_SimpleNewFromData(ndims, dims, NPY_FLOAT, out);
		boost::python::handle<> handle(pyObj);
		boost::python::numeric::array arr(handle);
		delete[] out;
		return boost::python::make_tuple(arr.copy());
	}
};

BOOST_PYTHON_MODULE(erspy)
{
	boost::python::numeric::array::set_module_and_type("numpy", "ndarray");
	import_array();
	class_<ErsPy>("ers")
		.def("ComputeSegmentation", &ErsPy::ComputeSegmentation)
		.def("SetSigma", &ErsPy::SetSigma)
		.def("SetLambda", &ErsPy::SetLambda)
		;
}


#include <iomanip>
#include <iostream>

#include <armadillo>
#include <boost/math/distributions/normal.hpp>

#include "simulate.h"

using namespace std;

int main(int argc, char *argv[]) {
  int N = stoi(argv[1]);          // 1/2 sample size
  double ALPHA = stod(argv[2]);   // level of significance
  double POWER = stod(argv[3]);   // power
  cout << calculateVariance(N, ALPHA, POWER) << endl;
  return 0;
}


#ifndef _COCONUT_PULP_PRIMITIVE_PRIMITIVE_HPP_
#define _COCONUT_PULP_PRIMITIVE_PRIMITIVE_HPP_

#include <boost/variant.hpp>

#include <coconut-tools/exceptions/RuntimeError.hpp>
#include <coconut-tools/serialisation.hpp>

#include "coconut/milk/graphics/PixelFormat.hpp"

#include "Position.hpp"
#include "Vector.hpp"
#include "Colour.hpp"
#include "Scalar.hpp"
#include "TextureCoordinate.hpp"

namespace coconut {
namespace pulp {
namespace primitive {

class Primitive {
public:

	class IncompatiblePixelFormat : public coconut_tools::exceptions::RuntimeError {
	public:

		IncompatiblePixelFormat(milk::graphics::PixelFormat pixelFormat);

	};

	Primitive() = default;

	/* implicit */ Primitive(Scalar scalar) :
		data_(scalar)
	{
	}

	/* implicit */ Primitive(Position position) :
		data_(position)
	{
	}

	/* implicit */ Primitive(Vector vector) :
		data_(vector)
	{
	}

	/* implicit */ Primitive(Colour colour) :
		data_(colour)
	{
	}

	/* implicit */ Primitive(TextureCoordinate textureCoordinate) :
		data_(textureCoordinate)
	{
	}

	void storeAs(void* buffer, milk::graphics::PixelFormat pixelFormat) const; // pixel format? co z macierzami?

private:

	using Data = boost::variant< // TODO: replace with std::variant when available
		Scalar,
		Position,
		Vector,
		Colour,
		TextureCoordinate
		>;

	Data data_;

	friend void serialise(coconut_tools::Serialiser& serialiser, const Primitive& aPrimitive);

	friend void serialise(coconut_tools::Deserialiser& serialiser, Primitive& aPrimitive);

};

} // namespace primitive

using primitive::Primitive;

} // namespace pulp
} // namespace coconut

#endif /* _COCONUT_PULP_PRIMITIVE_PRIMITIVE_HPP_ */


#define BOOST_TEST_MODULE pcraster geo raster_boundaries
#include <boost/test/unit_test.hpp>
#define private public
#include "geo_rasterboundaries.h"

BOOST_AUTO_TEST_CASE(index_left)
{
  using namespace geo;

  RasterBoundaries<size_t> boundaries(3, 2);
  BOOST_CHECK(boundaries.indexLeft(0, 0) == 2);
  BOOST_CHECK(boundaries.indexLeft(0, 1) == 3);
  BOOST_CHECK(boundaries.indexLeft(1, 0) == 7);
  BOOST_CHECK(boundaries.indexLeft(1, 1) == 8);
  BOOST_CHECK(boundaries.indexLeft(2, 0) == 12);
  BOOST_CHECK(boundaries.indexLeft(2, 1) == 13);
}

BOOST_AUTO_TEST_CASE(index_top)
{
  using namespace geo;

  RasterBoundaries<size_t> boundaries(3, 2);
  BOOST_CHECK(boundaries.indexTop(0, 0) == 0);
  BOOST_CHECK(boundaries.indexTop(0, 1) == 1);
  BOOST_CHECK(boundaries.indexTop(1, 0) == 5);
  BOOST_CHECK(boundaries.indexTop(1, 1) == 6);
  BOOST_CHECK(boundaries.indexTop(2, 0) == 10);
  BOOST_CHECK(boundaries.indexTop(2, 1) == 11);
}

BOOST_AUTO_TEST_CASE(index_right)
{
  using namespace geo;

  RasterBoundaries<size_t> boundaries(3, 2);
  BOOST_CHECK(boundaries.indexRight(0, 0) == 3);
  BOOST_CHECK(boundaries.indexRight(0, 1) == 4);
  BOOST_CHECK(boundaries.indexRight(1, 0) == 8);
  BOOST_CHECK(boundaries.indexRight(1, 1) == 9);
  BOOST_CHECK(boundaries.indexRight(2, 0) == 13);
  BOOST_CHECK(boundaries.indexRight(2, 1) == 14);
}

BOOST_AUTO_TEST_CASE(index_bottom)
{
  using namespace geo;

  RasterBoundaries<size_t> boundaries(3, 2);
  BOOST_CHECK(boundaries.indexBottom(0, 0) == 5);
  BOOST_CHECK(boundaries.indexBottom(0, 1) == 6);
  BOOST_CHECK(boundaries.indexBottom(1, 0) == 10);
  BOOST_CHECK(boundaries.indexBottom(1, 1) == 11);
  BOOST_CHECK(boundaries.indexBottom(2, 0) == 15);
  BOOST_CHECK(boundaries.indexBottom(2, 1) == 16);
}

BOOST_AUTO_TEST_CASE(boundaries)
{
  using namespace geo;

  RasterBoundaries<size_t> boundaries(3, 2);

  boundaries.leftBoundary(0, 0) = 2;
  boundaries.leftBoundary(0, 1) = 3;
  boundaries.leftBoundary(1, 0) = 7;
  boundaries.leftBoundary(1, 1) = 8;
  boundaries.leftBoundary(2, 0) = 12;
  boundaries.leftBoundary(2, 1) = 13;

  boundaries.rightBoundary(0, 1) = 4;
  boundaries.rightBoundary(1, 1) = 9;
  boundaries.rightBoundary(2, 1) = 14;

  boundaries.topBoundary(0, 0) = 0;
  boundaries.topBoundary(0, 1) = 1;
  boundaries.topBoundary(1, 0) = 5;
  boundaries.topBoundary(1, 1) = 6;
  boundaries.topBoundary(2, 0) = 10;
  boundaries.topBoundary(2, 1) = 11;

  boundaries.bottomBoundary(2, 0) = 15;
  boundaries.bottomBoundary(2, 1) = 16;

  BOOST_CHECK(boundaries.leftBoundary(0, 0) == 2);
  BOOST_CHECK(boundaries.topBoundary(0, 0) == 0);
  BOOST_CHECK(boundaries.rightBoundary(0, 0) == 3);
  BOOST_CHECK(boundaries.bottomBoundary(0, 0) == 5);

  BOOST_CHECK(boundaries.leftBoundary(1, 1) == 8);
  BOOST_CHECK(boundaries.topBoundary(1, 1) == 6);
  BOOST_CHECK(boundaries.rightBoundary(1, 1) == 9);
  BOOST_CHECK(boundaries.bottomBoundary(1, 1) == 11);

  BOOST_CHECK(boundaries.rightBoundary(2, 0) == boundaries.leftBoundary(2, 1));
  BOOST_CHECK(boundaries.topBoundary(2, 0) == boundaries.bottomBoundary(1, 0));
}


// For conditions of distribution and use, see copyright notice in license.txt

#include <boost/test/unit_test.hpp>

#include "CoreStdIncludes.h"
#include "Core.h"
#include "Foundation.h"

BOOST_AUTO_TEST_SUITE(core)

BOOST_AUTO_TEST_CASE( exception )
{
    Exception e;
    try
    {
        e = Exception("exception test");
        throw e;
        BOOST_CHECK(false);
    } catch (Exception e)
    {
        BOOST_CHECK_EQUAL(std::string(e.what()), "exception test");
    }
}

BOOST_AUTO_TEST_CASE( vector3 )
{
    Vector3df test;
    BOOST_CHECK_EQUAL (test, Vector3df(0, 0, 0));

    test = Vector3df(1, 1, 1);
    BOOST_CHECK_EQUAL (test, Vector3D<f32>::UNIT_SCALE_X);

    test = Vector3df(1, 0, 0);
    BOOST_CHECK_EQUAL (test, Vector3D<f32>::UNIT_X);
    BOOST_CHECK_EQUAL (test.getLength(), 1.f);

    test = -test;
    BOOST_CHECK_EQUAL (test, Vector3D<f32>::NEGATIVE_UNIT_X);

    test *= 5.f;
    test.invert();
    BOOST_CHECK_EQUAL (test, Vector3df(5, 0, 0));
    BOOST_CHECK_EQUAL (test.getLengthSQ(), 25.f);

    test.normalize();
    BOOST_CHECK_EQUAL (test.getLength(), 1.f);

    test.set(2, 0, 0);
    Vector3df other;
    BOOST_CHECK_EQUAL (test.getDistanceFrom(other), 2.f);
    BOOST_CHECK_EQUAL (test.getDistanceFromSQ(other), 4.f);

    other.set(2, 2, 0);
    BOOST_CHECK_EQUAL (test.crossProduct(other), Vector3df(0, 0, 4));
    BOOST_CHECK_EQUAL (test.dotProduct(other), 4.f);

    other = test * Vector3D<f32>::UNIT_SCALE_X;
    BOOST_CHECK_EQUAL (test, other);

    test /= 2.f;
    BOOST_CHECK_EQUAL (test, Vector3D<f32>::UNIT_X);

    Vector3di test_str;
    test_str.set(2, 3, 4);
    std::stringstream ss;
    ss << test_str;
    Vector3di other_str(1, 1, 1);
    ss >> other_str;
    BOOST_CHECK_EQUAL (test_str, other_str);
    ss.clear();

    Vector3df test_fv(1.5f, 0.5f, 1.5f);
    Vector3df other_fv(1.5f, 0.5f, 1.5f);
    ss << test_fv;
    ss >> test_fv;
    BOOST_CHECK (equals(test_fv.x, other_fv.x) && equals(test_fv.y, other_fv.y) && equals(test_fv.z, other_fv.z));

    test_fv.set(0, 0, 0);
    other_fv.set(0, 0, 1);
    std::string test_fv_str = ToString(test_fv);
    other_fv = ParseString<Vector3df>(test_fv_str);
    BOOST_CHECK (equals(test_fv.x, other_fv.x) && equals(test_fv.y, other_fv.y) && equals(test_fv.z, other_fv.z));
}

BOOST_AUTO_TEST_CASE( quaternion )
{
    Quaternion test;
    BOOST_CHECK_EQUAL (test, Quaternion::IDENTITY);

    test.set(1, 2, 3, 1);
    test.normalize();
    Quaternion other = test * Quaternion::IDENTITY;
    BOOST_CHECK_EQUAL (test, other);

    test.makeIdentity();
    BOOST_CHECK_EQUAL (test, Quaternion::IDENTITY);

    test = Quaternion(0, 0, 0, 0);
    BOOST_CHECK_EQUAL (test, Quaternion::ZERO);

    test.makeIdentity();
    test *= 10.f;
    BOOST_CHECK_EQUAL (test, Quaternion(0, 0, 0, 10.f));

    test.makeIdentity();
    Vector3df testVector(1, 2, 3);
    testVector = test * testVector;
    BOOST_CHECK_EQUAL (testVector, Vector3df(1, 2, 3));

    test = Quaternion(-PI, 0, 0);
    BOOST_CHECK_EQUAL (test, Quaternion(-1, 0, 0, 0));
    test = Quaternion(0, HALF_PI, 0);
    BOOST_CHECK_EQUAL (test, Quaternion(0, 0.70710683f, 0, 0.70710683f));
}

BOOST_AUTO_TEST_CASE( string_utils )
{
    BOOST_CHECK_EQUAL (ToString(123),  std::string("123"));

    BOOST_CHECK (ToWString("test") == std::wstring(L"test"));

    BOOST_CHECK_EQUAL (ParseString<unsigned int>("543210"),  543210);
    BOOST_CHECK_EQUAL (ParseString<int>("-1"),  -1);
}

BOOST_AUTO_TEST_SUITE_END()


#include <armadillo>

#include "UnitarySparseMatrix.h"

struct NewBasisDecomposition::Impl {
    arma::sp_mat sparse_basis;

  public:
    Impl() = default;
};

NewBasisDecomposition::NewBasisDecomposition() : pImpl {std::make_unique<Impl>()} {}
NewBasisDecomposition::~NewBasisDecomposition() = default;
NewBasisDecomposition::NewBasisDecomposition(NewBasisDecomposition&&) noexcept = default;
NewBasisDecomposition& NewBasisDecomposition::operator=(NewBasisDecomposition&&) noexcept = default;

struct IteratorImpl: public NewBasisDecomposition::Iterator {
    arma::SpMat<double>::col_iterator iter;
    const arma::SpMat<double>::col_iterator end;

    IteratorImpl(arma::SpMat<double>::col_iterator iter1, arma::SpMat<double>::col_iterator iter2) :
        iter(iter1),
        end(iter2) {}

    bool hasNext() const override {
        return iter != end;
    }

    IndexValueItem getNext() override {
        auto pair = iter;
        ++iter;
        return {static_cast<uint32_t>(pair.row()), *pair};
    }

    ~IteratorImpl() = default;
};

std::unique_ptr<NewBasisDecomposition::Iterator>
NewBasisDecomposition::GetNewIterator(size_t index_of_vector) const {
    return std::make_unique<IteratorImpl>(
        pImpl->sparse_basis.begin_col(index_of_vector),
        pImpl->sparse_basis.end_col(index_of_vector));
}

std::ostream& operator<<(std::ostream& os, const NewBasisDecomposition& decomposition) {
    os << decomposition.pImpl->sparse_basis << std::endl;
    return os;
}

uint32_t NewBasisDecomposition::size() const {
    return pImpl->sparse_basis.n_cols;
}

bool NewBasisDecomposition::empty() const {
    return pImpl->sparse_basis.n_cols == 0;
}

bool NewBasisDecomposition::vempty(uint32_t index_of_vector) const {
    return pImpl->sparse_basis.col(index_of_vector).n_nonzero == 0;
}

void NewBasisDecomposition::clear() {
    pImpl->sparse_basis.reset();
}

void NewBasisDecomposition::move_vector_from(
    uint32_t i,
    NewBasisDecomposition& decomposition_from) {
    tensor_size = decomposition_from.tensor_size;
    pImpl->sparse_basis.resize(tensor_size, pImpl->sparse_basis.n_cols + 1);
    pImpl->sparse_basis.col(pImpl->sparse_basis.n_cols - 1) =
        decomposition_from.pImpl->sparse_basis.col(i);
    // TODO: Is it good idea?
    //    decomposition_from.pImpl->sparse_basis.col(i).zeros();
}

void NewBasisDecomposition::move_all_from(NewBasisDecomposition& decomposition_from) {
    tensor_size = decomposition_from.tensor_size;
    pImpl->sparse_basis.resize(
        tensor_size,
        pImpl->sparse_basis.n_cols + decomposition_from.pImpl->sparse_basis.n_cols);
    pImpl->sparse_basis.cols(
        pImpl->sparse_basis.n_cols - decomposition_from.pImpl->sparse_basis.n_cols,
        pImpl->sparse_basis.n_cols - 1) = decomposition_from.pImpl->sparse_basis;
    decomposition_from.clear();
}

void NewBasisDecomposition::copy_vector_from(
    uint32_t i,
    const NewBasisDecomposition& decomposition_from) {
    tensor_size = decomposition_from.tensor_size;
    pImpl->sparse_basis.resize(tensor_size, pImpl->sparse_basis.n_cols + 1);
    pImpl->sparse_basis.col(pImpl->sparse_basis.n_cols - 1) =
        decomposition_from.pImpl->sparse_basis.col(i);
}

void NewBasisDecomposition::copy_all_from(const NewBasisDecomposition& decomposition_from) {
    tensor_size = decomposition_from.tensor_size;
    pImpl->sparse_basis.resize(
        tensor_size,
        pImpl->sparse_basis.n_cols + decomposition_from.pImpl->sparse_basis.n_cols);
    pImpl->sparse_basis.cols(
        pImpl->sparse_basis.n_cols - decomposition_from.pImpl->sparse_basis.n_cols,
        pImpl->sparse_basis.n_cols - 1) = decomposition_from.pImpl->sparse_basis;
}

void NewBasisDecomposition::add_to_position(double value, uint32_t i, uint32_t j) {
    pImpl->sparse_basis(j, i) += value;
}

double NewBasisDecomposition::operator()(uint32_t i, uint32_t j) const {
    return pImpl->sparse_basis(j, i);
}

void NewBasisDecomposition::resize(uint32_t new_size) {
    pImpl->sparse_basis.resize(tensor_size, new_size);
}

bool NewBasisDecomposition::is_zero(uint32_t i, uint32_t j) const {
    // TODO: epsilon
    return std::abs(pImpl->sparse_basis(j, i)) < 0.001;
}

void NewBasisDecomposition::erase_if_zero() {
    for (auto&& el : pImpl->sparse_basis) {
        // TODO: epsilon
        if (std::abs(el) < 0.001) {
            el = 0.0;
        }
    }
}


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 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
//==============================================================================
#define NT2_UNIT_MODULE "nt2 boost.simd.ieee toolbox - saturate/simd Mode"

//////////////////////////////////////////////////////////////////////////////
// unit test behavior of boost.simd.ieee components in simd mode
//////////////////////////////////////////////////////////////////////////////

#include <boost/simd/toolbox/ieee/include/functions/saturate.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <boost/simd/include/constants/valmax.hpp>
#include <boost/simd/include/constants/valmin.hpp>
#include <boost/simd/include/constants/zero.hpp>

NT2_TEST_CASE_TPL ( saturate
                  , (boost::int8_t)(boost::int16_t)(boost::int32_t)
                    (boost::uint8_t)(boost::uint16_t)(boost::uint32_t)
                  )
{
  using boost::simd::Zero;
  using boost::simd::Valmax;
  using boost::simd::Valmin;
  using boost::simd::saturate;
  using boost::simd::native;
  using boost::simd::splat;

  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef native<T,ext_t>               n_t;

  typedef typename boost::dispatch::meta::upgrade<n_t,signed>::type   signed_t;
  typedef typename boost::dispatch::meta::upgrade<n_t,unsigned>::type usigned_t;
  typedef typename boost::dispatch::meta::upgrade<T,signed>::type   ssigned_t;
  typedef typename boost::dispatch::meta::upgrade<T,unsigned>::type susigned_t;

  NT2_TEST_EQUAL( saturate<n_t>( splat<signed_t>( ssigned_t(Valmin<T>())-1) )
                , splat<signed_t>(Valmin<T>())
                );

  NT2_TEST_EQUAL( saturate<n_t>( splat<signed_t>( ssigned_t(Valmin<T>())) )
                , splat<signed_t>(Valmin<T>())
                );

  NT2_TEST_EQUAL( saturate<n_t>( Zero<signed_t>() )
                , Zero<signed_t>()
                );

  NT2_TEST_EQUAL( saturate<n_t>( splat<signed_t>(Valmax<T>()) )
                , splat<signed_t>(Valmax<T>())
                );

  NT2_TEST_EQUAL( saturate<n_t>( splat<signed_t>( ssigned_t(Valmax<T>()) + 1))
                , splat<signed_t>(Valmax<T>())
                );

  NT2_TEST_EQUAL( saturate<n_t>( Zero<usigned_t>() )
                , Zero<usigned_t>()
                );

  NT2_TEST_EQUAL( saturate<n_t>( splat<usigned_t>(Valmax<T>()) )
                , splat<usigned_t>(Valmax<T>())
                );

  NT2_TEST_EQUAL( saturate<n_t>( splat<usigned_t>( ssigned_t(Valmax<T>()) + 1))
                , splat<usigned_t>(Valmax<T>())
                );
}


//=======================================================================
// Copyright 2007 Aaron Windsor
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/boyer_myrvold_planar_test.hpp>
#include <boost/property_map/property_map.hpp>
#include <boost/property_map/vector_property_map.hpp>
#include <boost/core/lightweight_test.hpp>

using namespace boost;

struct VertexIndexUpdater
{
    template < typename Graph > void reset(Graph& g)
    {
        typename property_map< Graph, vertex_index_t >::type index
            = get(vertex_index, g);
        typename graph_traits< Graph >::vertex_iterator vi, vi_end;
        typename graph_traits< Graph >::vertices_size_type cnt = 0;
        for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi)
            put(index, *vi, cnt++);
    }
};

struct NoVertexIndexUpdater
{
    template < typename Graph > void reset(Graph&) {}
};

template < typename Graph, typename VertexIndexUpdater >
void test_K_5(VertexIndexUpdater vertex_index)
{
    typedef typename graph_traits< Graph >::vertex_descriptor vertex_t;

    Graph g;
    vertex_t v1 = add_vertex(g);
    vertex_t v2 = add_vertex(g);
    vertex_t v3 = add_vertex(g);
    vertex_t v4 = add_vertex(g);
    vertex_t v5 = add_vertex(g);
    vertex_index.reset(g);

    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v1, v2, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v1, v3, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v1, v4, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v1, v5, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v2, v3, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v2, v4, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v2, v5, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v3, v4, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v3, v5, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));

    // This edge should make the graph non-planar
    add_edge(v4, v5, g);
    BOOST_TEST(!boyer_myrvold_planarity_test(g));
}

template < typename Graph, typename VertexIndexUpdater >
void test_K_3_3(VertexIndexUpdater vertex_index)
{
    typedef typename graph_traits< Graph >::vertex_descriptor vertex_t;

    Graph g;
    vertex_t v1 = add_vertex(g);
    vertex_t v2 = add_vertex(g);
    vertex_t v3 = add_vertex(g);
    vertex_t v4 = add_vertex(g);
    vertex_t v5 = add_vertex(g);
    vertex_t v6 = add_vertex(g);
    vertex_index.reset(g);

    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v1, v4, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v1, v5, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v1, v6, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v2, v4, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v2, v5, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v2, v6, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v3, v4, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));
    add_edge(v3, v5, g);
    BOOST_TEST(boyer_myrvold_planarity_test(g));

    // This edge should make the graph non-planar
    add_edge(v3, v6, g);
    BOOST_TEST(!boyer_myrvold_planarity_test(g));
}

// This test creates a maximal planar graph on num_vertices vertices,
// then, if num_vertices is at least 5, adds an additional edge to
// create a non-planar graph.

template < typename Graph, typename VertexIndexUpdater >
void test_maximal_planar(
    VertexIndexUpdater vertex_index, std::size_t num_vertices)
{
    typedef typename graph_traits< Graph >::vertex_descriptor vertex_t;

    Graph g;
    std::vector< vertex_t > vmap;
    for (std::size_t i = 0; i < num_vertices; ++i)
        vmap.push_back(add_vertex(g));

    vertex_index.reset(g);

    BOOST_TEST(boyer_myrvold_planarity_test(g));
    // create a cycle
    for (std::size_t i = 0; i < num_vertices; ++i)
    {
        add_edge(vmap[i], vmap[(i + 1) % num_vertices], g);
        BOOST_TEST(boyer_myrvold_planarity_test(g));
    }

    // triangulate the interior of the cycle.
    for (std::size_t i = 2; i < num_vertices - 1; ++i)
    {
        add_edge(vmap[0], vmap[i], g);
        BOOST_TEST(boyer_myrvold_planarity_test(g));
    }

    // triangulate the exterior of the cycle.
    for (std::size_t i = 3; i < num_vertices; ++i)
    {
        add_edge(vmap[1], vmap[i], g);
        BOOST_TEST(boyer_myrvold_planarity_test(g));
    }

    // Now add an additional edge, forcing the graph to be non-planar.
    if (num_vertices > 4)
    {
        add_edge(vmap[2], vmap[4], g);
        BOOST_TEST(!boyer_myrvold_planarity_test(g));
    }
}

int main(int, char*[])
{
    typedef adjacency_list< vecS, vecS, undirectedS,
        property< vertex_index_t, int > >
        VVgraph_t;

    typedef adjacency_list< vecS, listS, undirectedS,
        property< vertex_index_t, int > >
        VLgraph_t;

    typedef adjacency_list< listS, vecS, undirectedS,
        property< vertex_index_t, int > >
        LVgraph_t;

    typedef adjacency_list< listS, listS, undirectedS,
        property< vertex_index_t, int > >
        LLgraph_t;

    typedef adjacency_list< setS, setS, undirectedS,
        property< vertex_index_t, int > >
        SSgraph_t;

    test_K_5< VVgraph_t >(NoVertexIndexUpdater());
    test_K_3_3< VVgraph_t >(NoVertexIndexUpdater());
    test_maximal_planar< VVgraph_t >(NoVertexIndexUpdater(), 3);
    test_maximal_planar< VVgraph_t >(NoVertexIndexUpdater(), 6);
    test_maximal_planar< VVgraph_t >(NoVertexIndexUpdater(), 10);
    test_maximal_planar< VVgraph_t >(NoVertexIndexUpdater(), 20);
    test_maximal_planar< VVgraph_t >(NoVertexIndexUpdater(), 50);

    test_K_5< VLgraph_t >(VertexIndexUpdater());
    test_K_3_3< VLgraph_t >(VertexIndexUpdater());
    test_maximal_planar< VLgraph_t >(VertexIndexUpdater(), 3);
    test_maximal_planar< VLgraph_t >(VertexIndexUpdater(), 6);
    test_maximal_planar< VLgraph_t >(VertexIndexUpdater(), 10);
    test_maximal_planar< VLgraph_t >(VertexIndexUpdater(), 20);
    test_maximal_planar< VLgraph_t >(VertexIndexUpdater(), 50);

    test_K_5< LVgraph_t >(NoVertexIndexUpdater());
    test_K_3_3< LVgraph_t >(NoVertexIndexUpdater());
    test_maximal_planar< LVgraph_t >(NoVertexIndexUpdater(), 3);
    test_maximal_planar< LVgraph_t >(NoVertexIndexUpdater(), 6);
    test_maximal_planar< LVgraph_t >(NoVertexIndexUpdater(), 10);
    test_maximal_planar< LVgraph_t >(NoVertexIndexUpdater(), 20);
    test_maximal_planar< LVgraph_t >(NoVertexIndexUpdater(), 50);

    test_K_5< LLgraph_t >(VertexIndexUpdater());
    test_K_3_3< LLgraph_t >(VertexIndexUpdater());
    test_maximal_planar< LLgraph_t >(VertexIndexUpdater(), 3);
    test_maximal_planar< LLgraph_t >(VertexIndexUpdater(), 6);
    test_maximal_planar< LLgraph_t >(VertexIndexUpdater(), 10);
    test_maximal_planar< LLgraph_t >(VertexIndexUpdater(), 20);
    test_maximal_planar< LLgraph_t >(VertexIndexUpdater(), 50);

    test_K_5< SSgraph_t >(VertexIndexUpdater());
    test_K_3_3< SSgraph_t >(VertexIndexUpdater());
    test_maximal_planar< SSgraph_t >(VertexIndexUpdater(), 3);
    test_maximal_planar< SSgraph_t >(VertexIndexUpdater(), 6);
    test_maximal_planar< SSgraph_t >(VertexIndexUpdater(), 10);
    test_maximal_planar< SSgraph_t >(VertexIndexUpdater(), 20);
    test_maximal_planar< SSgraph_t >(VertexIndexUpdater(), 50);

    return boost::report_errors();
}