// (C) Copyright Jeremy Siek 2001.
// 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)

#ifndef BOOST_PERMUTATION_HPP
#define BOOST_PERMUTATION_HPP

#include <vector>
#include <memory>
#include <functional>
#include <algorithm>
#include <boost/graph/detail/shadow_iterator.hpp>

namespace boost {

template <class Iter1, class Iter2>
void permute_serial(Iter1 permuter, Iter1 last, Iter2 result)
{
#ifdef BOOST_NO_STD_ITERATOR_TRAITS
  typedef std::ptrdiff_t D:
#else
  typedef typename std::iterator_traits<Iter1>::difference_type D;
#endif

  D n = 0;
  while (permuter != last) {
    std::swap(result[n], result[*permuter]);
    ++n;
    ++permuter;
  }
}

template <class InIter, class RandIterP, class RandIterR>
void permute_copy(InIter first, InIter last, RandIterP p, RandIterR result)
{
#ifdef BOOST_NO_STD_ITERATOR_TRAITS
  typedef std::ptrdiff_t i = 0;
#else
  typename std::iterator_traits<RandIterP>::difference_type i = 0;
#endif
  for (; first != last; ++first, ++i)
    result[p[i]] = *first;
}

namespace detail {

template <class RandIter, class RandIterPerm, class D, class T>
void permute_helper(RandIter first, RandIter last, RandIterPerm p, D, T)
{
  D i = 0, pi, n = last - first, cycle_start;
  T tmp;
  std::vector<int> visited(n, false);

  while (i != n) { // continue until all elements have been processed
    cycle_start = i;
    tmp = first[i];
    do { // walk around a cycle
      pi = p[i];
      visited[pi] = true;
      std::swap(tmp, first[pi]);
      i = pi;
    } while (i != cycle_start);

    // find the next cycle
    for (i = 0; i < n; ++i)
      if (visited[i] == false)
        break;
  }
}

} // namespace detail

template <class RandIter, class RandIterPerm>
void permute(RandIter first, RandIter last, RandIterPerm p)
{
  detail::permute_helper(first, last, p, last - first, *first);
}

// Knuth 1.3.3, Vol. 1 p 176
// modified for zero-based arrays
// time complexity?
//
// WARNING: T must be a signed integer!
template <class PermIter>
void invert_permutation(PermIter X, PermIter Xend)
{
#ifdef BOOST_NO_STD_ITERATOR_TRAITS
  typedef std::ptrdiff_t T:
#else
  typedef typename std::iterator_traits<PermIter>::value_type T;
#endif
  T n = Xend - X;
  T m = n;
  T j = -1;

  while (m > 0) {
    T i = X[m-1] + 1;
    if (i > 0) {
      do {
        X[m-1] = j - 1;
        j = -m;
        m = i;
        i = X[m-1] + 1;
      } while (i > 0);
      i = j;
    }
    X[m-1] = -i - 1;
    --m;
  }
}

// Takes a "normal" permutation array (and its inverse), and turns it
// into a BLAS-style permutation array (which can be thought of as a
// serialized permutation).
template <class Iter1, class Iter2, class Iter3>
inline void serialize_permutation(Iter1 q, Iter1 q_end, Iter2 q_inv, Iter3 p)
{
#ifdef BOOST_NO_STD_ITERATOR_TRAITS
  typedef std::ptrdiff_t P1;
  typedef std::ptrdiff_t P2;
  typedef std::ptrdiff_t D;
#else
  typedef typename std::iterator_traits<Iter1>::value_type P1;
  typedef typename std::iterator_traits<Iter2>::value_type P2;
  typedef typename std::iterator_traits<Iter1>::difference_type D;
#endif
  D n = q_end - q;
  for (D i = 0; i < n; ++i) {
    P1 qi = q[i];
    P2 qii = q_inv[i];
    *p++ = qii;
    std::swap(q[i], q[qii]);
    std::swap(q_inv[i], q_inv[qi]);
  }
}

// Not used anymore, leaving it here for future reference.
template <typename Iter, typename Compare>
void merge_sort(Iter first, Iter last, Compare cmp)
{
  if (first + 1 < last) {
    Iter mid = first + (last - first)/2;
    merge_sort(first, mid, cmp);
    merge_sort(mid, last, cmp);
    std::inplace_merge(first, mid, last, cmp);
  }
}

// time: N log N + 3N + ?
// space: 2N
template <class Iter, class IterP, class Cmp, class Alloc>
inline void sortp(Iter first, Iter last, IterP p, Cmp cmp, Alloc alloc)
{
  typedef typename std::iterator_traits<IterP>::value_type P;
  typedef typename std::iterator_traits<IterP>::difference_type D;
  D n = last - first;
  std::vector<P, Alloc> q(n);
  for (D i = 0; i < n; ++i)
    q[i] = i;
  std::sort(make_shadow_iter(first, q.begin()),
            make_shadow_iter(last, q.end()),
            shadow_cmp<Cmp>(cmp));
  invert_permutation(q.begin(), q.end());
  std::copy(q.begin(), q.end(), p);
}

template <class Iter, class IterP, class Cmp>
inline void sortp(Iter first, Iter last, IterP p, Cmp cmp)
{
  typedef typename std::iterator_traits<IterP>::value_type P;
  sortp(first, last, p, cmp, std::allocator<P>());
}

template <class Iter, class IterP>
inline void sortp(Iter first, Iter last, IterP p)
{
  typedef typename std::iterator_traits<Iter>::value_type T;
  typedef typename std::iterator_traits<IterP>::value_type P;
  sortp(first, last, p, std::less<T>(), std::allocator<P>());
}

template <class Iter, class IterP, class Cmp, class Alloc>
inline void sortv(Iter first, Iter last, IterP p, Cmp cmp, Alloc alloc)
{
  typedef typename std::iterator_traits<IterP>::value_type P;
  typedef typename std::iterator_traits<IterP>::difference_type D;
  D n = last - first;
  std::vector<P, Alloc> q(n), q_inv(n);
  for (D i = 0; i < n; ++i)
    q_inv[i] = i;
  std::sort(make_shadow_iter(first, q_inv.begin()),
            make_shadow_iter(last, q_inv.end()),
            shadow_cmp<Cmp>(cmp));
  std::copy(q_inv, q_inv.end(), q.begin());
  invert_permutation(q.begin(), q.end());
  serialize_permutation(q.begin(), q.end(), q_inv.end(), p);
}

} // namespace boost

#endif // BOOST_PERMUTATION_HPP


#ifndef _H_SERIES
#define _H_SERIES

#include "agw.h"

#include <string>
#include <vector>
#include <memory>
#include <cmath>
#include <wx/string.h>
#include "json/json.h"
#include <wx/datetime.h>
#include <boost/date_time/gregorian/gregorian.hpp>
#include <exception>
//#include <lua.hpp>
#include "fromlua.h"

namespace agw {
    class SeriesException : public std::exception {

    };

    const double nanDouble = NAN;
    const float nanFloat =  NAN;

    enum SeriesUnit {
        DEFAULT,
        DATE_JULIAN,
        DATE_JULIAN_MOD,
        DATE_MONTH_NUM, // (1-12)
        DATE_YEAR_MONTH  // Year * 12 + month (0-11)
    };

    std::string unitAsString(SeriesUnit unit);
    SeriesUnit toSeriesUnit(const std::string& v);

    // base class
    class Series {
    public:
        Series();
        virtual ~Series();

        Series(const Series& cs);
        const Series& operator=(const Series& cs);

        virtual void SaveJSON(Json::Value& json);
        virtual void ReadJSON(const Json::Value& json);

        virtual double operator[] (size_t ix) const = 0;
        virtual void fromString(size_t ix, const std::string& s) { ignore(ix); ignore(s); }
        //virtual std::string toString(size_t ix){}
         virtual void append(double value) = 0;
         virtual void append(const std::string& s){ ignore(s); }

        virtual void set(size_t ix, double value) = 0;
        virtual void setSize(size_t pts) = 0;
        virtual void assign(size_t n, double value)
        {
            ignore(n);ignore(value);
        }
        // return number valid values (not number of slots)
        virtual size_t getDataLimits(double& dmin, double& dmax);

        size_t size() const{ return pts_; }

        void setLabel(const std::string& label)
        {
            label_ = label;
        }
        const std::string& getLabel()
        {
            return label_;
        }
        const std::string& get_jype() {
            return jype_;
        }
        bool isAltered() const { return altered_; }
        bool isOrdered() const { return ordered_; }

        SeriesUnit units() const { return units_; }
        void units(SeriesUnit value) { units_ = value; }

        size_t calcDataLimits()
        {
            return getDataLimits(dataMin_, dataMax_);
        }

        double dataMin() const { return dataMin_; }
        double dataMax() const { return dataMax_; }

        int saveToFile(const std::string& path); // return file size
        std::string toJsonText();

    protected:

        size_t      pts_;
        double      dataMin_;
        double      dataMax_;
        SeriesUnit	units_;
        bool        altered_;
        bool		ordered_;
        std::string jype_;
        std::string label_;

        friend class SeriesRay;

    };

    // vector of gregorian date time (32-bits) from boost gregorian
    class DateSeries : public Series {
        virtual void fromString(size_t ix,const std::string& s);
        virtual void append(const std::string& s);

        virtual std::string toString(size_t ix);
        virtual void setSize(size_t pts);
        void init();

    protected:
        std::vector<boost::gregorian::date>  data_;
    };

    typedef std::shared_ptr<Series> SeriesPtr;

    class FloatSeries : public Series {
        void init()
        {
            jype_ = "float";
        }
    public:
        typedef std::vector<float> Data;

        FloatSeries()
        {
            init();
        }

        const Data& data()  const { return data_; }
        FloatSeries(const FloatSeries& cs);
        const FloatSeries& operator=(const FloatSeries& cs);

        FloatSeries(std::vector<float>& f, SeriesUnit value, const std::string& label)
        {
            init();
            move(f);
            units_ = value;
            label_ = label;
        }
        virtual ~FloatSeries();

        void prepend(float init_value, size_t n);
        void append(float init_value, size_t n);

        void addToValue(size_t ix, double value);

        virtual double operator[] (size_t ix) const;
        virtual void set(size_t ix, double value);
        virtual void append(double value);
        virtual void setSize(size_t pts);
        virtual void assign(size_t n, double value)
        {
            data_.assign(n, value);
            pts_ = data_.size();
        }
        void clear()
        {
            data_.clear();
            pts_ = 0;
        }
        void move(std::vector<float>& f);

        virtual void SaveJSON(Json::Value& json);
        virtual void ReadJSON(const Json::Value& json);
    protected:
        std::vector<float>  data_;

        friend class OffsetFloatSeries;
    };

    // there are 0 to n-1 valid stored points, but these are indexed using offset_ , and there are nanFloat
    // offset_ points before, and tail_ points after
    // total (virtual size) is offset_ + pts_ + tail_;, of which (0 to pts_-1) are stored, but may have missing values of nanFloat
    // only the [] and set functions use this.

    class OffsetFloatSeries : public FloatSeries {
        void init()
        {
            jype_ = "offset_float";
        }

    public:
        OffsetFloatSeries(uint offset, uint pts, uint tail) : offset_(offset), tail_(tail)
        {
            setSize(pts);
        }

        OffsetFloatSeries(FloatSeries& f, uint offset, uint tail) : FloatSeries(f.data_, f.units_, f.label_), offset_(offset), tail_(tail)
        {
        }
        virtual double operator[] (size_t ix) const
        {
            if (ix >= offset_)
            {
                auto vix = ix - offset_;
                if (vix < pts_)
                {
                    return data_[vix];
                }
            }
            return nanDouble;
        }
        void prepend(double init_value, size_t n)
        {
            data_.insert(data_.begin(), n, init_value);
            pts_ += n;
        }
        void append(double init_value, size_t n)
        {
            data_.insert(data_.end(), n, init_value);
            pts_ += n;
        }
        virtual void set(size_t ix, double value)
        {
            if (ix >= offset_)
            {
                auto vix = ix - offset_;
                if (vix < pts_)
                {
                    data_[vix] = value;
                }
            }
        }
        virtual void SaveJSON(Json::Value& json);
        virtual void ReadJSON(const Json::Value& json);

    protected:
        uint offset_;
        uint tail_;

    };

    // given a number of points, generate a line from equation of index value (from  0 to n-1
    // specify first and last value, eg 200 points, from -10.0 to 10.0
    class LineSeries : public Series {
    protected:

        void init()
        {
            jype_ = "line";
        }

        void calc()
        {
            scale_ = (v2_ - v1_) / (pts_ - 1);
            offset_ = v1_;
        }
    public:
        double v1_;
        double v2_;
        double scale_;
        double offset_;

        LineSeries()
        {
            init();
            set(2, 0.0, 1.0);

        }
        LineSeries(size_t n, double v1, double v2)
        {
            init();
            set(n,v1,v2);

        }
        // cannot set number of points to be 1
        void set(size_t n,double v1, double v2 )
        {
             if ( n <= 1)
                n = 2;

             pts_ = n;
             v1_ = v1;
             v2_ = v2;

             calc();
            // (0, v1_),   pts_-1, v2_)

        }
        // append has no meaning
        virtual void append(double value) { ignore(value);  }
        virtual double operator[] (size_t ix) const
        {
            return ix  * scale_ + offset_;
        }
        virtual void setSize(size_t pts)
        {
            if(pts<=1)
                pts=2;
            pts_ = pts;
            calc();
        }

        virtual void set(size_t ix, double value)
        {
            if (ix == 0)
            {
                v1_ = value;
            }
            else
            {
                v2_ = value;
                pts_ = ix + 1;
            }
            calc();
        }

        virtual void SaveJSON(Json::Value& json);
        virtual void ReadJSON(const Json::Value& json);

    };

    class DoubleSeries : public Series {
        void init()
        {
            jype_ = "double";
        }

    public:
        typedef std::vector<double> Data;

        DoubleSeries()
        {
           init();
        }
        // move data constructor
        DoubleSeries(std::vector<double>& f, SeriesUnit value, const std::string& label)
        {
            init();
            move(f);
            units_ = value;
            label_ = label;
        }
        virtual ~DoubleSeries();

        std::vector<double>& data() { return data_; }
        virtual double operator[] (size_t ix) const;
        virtual void set(size_t ix, double value);
        virtual void append(double value);
        virtual void setSize(size_t pts);
        virtual void assign(size_t n, double value)
        {
            data_.assign(n, value);
        }
        void move(std::vector<double>& f);
        void copy(const std::vector<double>& f);

        virtual void SaveJSON(Json::Value& json);
        virtual void ReadJSON(const Json::Value& json);
    private:
        std::vector<double>  data_;
    };

    // A virtual time base with Year/Month steps, countains all values inbetween without storing them.
    // This ignores calendar breakdowns in the past. Both first and last endpoints are included, in the range
    // month values are an enumerator starting at zero for wxDateTime
    class DateYearMonth : public Series {

    protected:
        int year1_;
        int month1_;
        int year2_;
        int month2_;

        void calcPts();

        void init()
        {
            jype_ = "year-month-range";
            units_ = DATE_YEAR_MONTH;
        }
    public:
        DateYearMonth() : year1_(0), month1_(0), year2_(0), month2_(0)
        {
            init();
            calcPts();
        }

        DateYearMonth(int year1, int month1, int year2, int month2) :
            year1_(year1), month1_(month1), year2_(year2), month2_(month2)
        {
            init();
            calcPts();

        }

        DateYearMonth(const DateYearMonth&);
        const DateYearMonth& operator=(const DateYearMonth&);

        void setStartDate(int month, int year) { month1_ = month; year1_ = year; calcPts(); }
        void setEndDate(int month, int year) { month2_ = month; year2_ = year; calcPts(); }
        void getStartDate(int& month, int& year) const {month = month1_; year = year1_;}
        void getEndDate(int& month, int& year) const {month = month2_; year = year2_;}

        DateYearMonth(const wxDateTime& firstMonth, const wxDateTime& lastMonth)
        {
            init();
            year1_ = firstMonth.GetYear();
            month1_ = firstMonth.GetMonth();

            year2_ = lastMonth.GetYear();
            month2_ = lastMonth.GetMonth();

            calcPts();
        }

        // return YEAR*12 + MONTH (0-11) ie number of months
        virtual double operator[] (size_t ix) const;
        virtual size_t getDataLimits(double& dmin, double& dmax);

        virtual void set(size_t ix, double value) { ignore(ix);  ignore(value); }
        virtual void setSize(size_t pts) { ignore(pts); }

        virtual void append(double value) { ignore(value); }

        virtual void SaveJSON(Json::Value& json);
        virtual void ReadJSON(const Json::Value& json);

    };

    struct NormalStats {
        double mean_;
        double stddev_;
        size_t n_;
        double minval_;
        double maxval_;
        bool   valid_;

        std::string toString();

        NormalStats(double m = 0.0, double s = 0.0, size_t n = 0) : mean_(m), stddev_(s), n_(n)
            ,minval_(0.0), maxval_(0.0), valid_(false)
        {
        }

        ~NormalStats(){}
        void calc(const std::vector<float> &data);
        void calc(const std::vector<double>& data);
        void calc(const Series& x);
        static void setup_require(lua_State* L);
        static int   init_lib(lua_State* L);
        static int   toString(lua_State* L);
        static int   NewStats(lua_State* L);
        static int   lMin(lua_State* L);
        static int   lMax(lua_State* L);
        static int   lSize(lua_State* L);
        static int   lAvg(lua_State* L);
        static int   lStddev(lua_State* L);
        static int   lCalc(lua_State* L);

    };
    struct RCorrelate {
        NormalStats xs;
        NormalStats ys;
        double rval_;

        RCorrelate()  : xs(), ys(), rval_(0.0)
        {
        }
        void calc(const Series& x, const Series& y);
    };

}; // namespace agw

#endif


/* Copyright (C) 2010 Ion Torrent Systems, Inc. All Rights Reserved */
#include <gtest/gtest.h>
#include <stdexcept>
#include <stdio.h>
#include <armadillo>
#include "IonH5File.h"
#include "IonH5Arma.h"

using namespace std;
using namespace arma;
TEST(H5Handle_Test, QuickMatrixTest) {
  Mat<float> mymat = ones<Mat<float> >(5,3);
  Mat<float>::iterator i;
  int count = 0;
  for (i = mymat.begin(); i != mymat.end(); i++) {
    *i = count++;
  }
  //  mymat.raw_print(cout);
  H5Arma::WriteMatrix("tmp.h5:/path/to/mat", mymat, false);
  Mat<float> m;
  H5Arma::ReadMatrix("tmp.h5:/path/to/mat", m);
  Mat<float>::iterator gold, test;
  for (gold = mymat.begin(), test = m.begin(); gold != mymat.end(); gold++, test++) {
    float g = *gold;
    float t = *test;
    EXPECT_EQ(t, g);
  }
}

TEST(H5Handle_Test, MatrixTest) {
  Mat<float> mymat = ones<Mat<float> >(5,3);
  Mat<float>::iterator i;
  int count = 0;
  for (i = mymat.begin(); i != mymat.end(); i++) {
    *i = count++;
  }
  //  mymat.raw_print(cout);
  {
    H5File file;
    file.SetFile("tmp.h5");
    file.Open(true);
    H5DataSet *ds = H5Arma::CreateDataSet(file,"/tmp/fun/mat", mymat, 0);
    //H5DataSet *ds = file.CreateDataSet("mat", mymat, 0);
    H5Arma::WriteMatrix(*ds, mymat);
    ds->Close();
  }
  {
    H5File file;
    file.SetFile("tmp.h5");
    file.Open();
    H5DataSet *ds = file.OpenDataSet("/tmp/fun/mat");
    //    H5DataSet *ds = file.OpenDataSet("mat");
    Mat<float> m;
    H5Arma::ReadMatrix(*ds, m);
    //    m.raw_print(cout);
    Mat<float>::iterator gold, test;
    for (gold = mymat.begin(), test = m.begin(); gold != mymat.end(); gold++, test++) {
      float g = *gold;
      float t = *test;
      EXPECT_EQ(t, g);
    }
    ds->Close();
  }

 }

TEST(H5Handle_Test, WriteReadTest) {
  float data[12];
  for (size_t i = 0; i < ArraySize(data); i++) {
    data[i] = i;
  }
  {
    H5File file;
    file.SetFile("tmp.h5");
    file.Open(true);
    hsize_t dims[] = {3,4};
    hsize_t chunking[] = {3,4};
    H5DataSet *ds =  file.CreateDataSet("/tmp/here/testdata", 2, dims, chunking, 2, H5T_NATIVE_FLOAT);

    size_t starts[] = {0,0};
    size_t ends[] = {3,4};
    //ds->WriteRangeData(starts, ends, ArraySize(data), data);
	ds->WriteRangeData(starts, ends, data);
    ds->Close();
  }
  {
    H5File file;
    file.SetFile("tmp.h5");
    file.Open();
    H5DataSet *ds = file.OpenDataSet("/tmp/here/testdata");
    float query[12];
    size_t starts[] = {0,0};
    size_t ends[] = {3,4};
    ds->ReadRangeData(starts, ends, ArraySize(query), query);
    for (size_t i = 0; i < ArraySize(data); i++) {
      if (data[i] != query[i]) {
        EXPECT_EQ(query[i], data[i]);
      }
    }

    {
      float update[4];
      size_t starts[] = {1,2};
      size_t ends[] = {3,4};
      size_t count = 0;
      for (size_t i = 11; i >= 8; i--) {
        update[count++] = i;
      }
      //ds->WriteRangeData(starts, ends, ArraySize(update), update);
	  ds->WriteRangeData(starts, ends, update);
      float query[12];
      size_t rstarts[] = {0,0};
      size_t rends[] = {3,4};
      float gold[12] = {0,1,2,3,4,5,11,10,8,9,9,8};
      ds->ReadRangeData(rstarts, rends, ArraySize(query), query);
      for (size_t i = 0; i < 1; i++) {
        EXPECT_EQ(gold[i], query[i]);
      }

    }
    //    ds->Close();
    ds = file.OpenDataSet("shouldbenull");
    EXPECT_EQ(NULL,ds);

  }
}


//
// Copyright 2020 Debabrata Mandal <mandaldebabrata123@gmail.com>
//
// 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/gil/extension/histogram/std.hpp>
#include <boost/gil/image_view.hpp>
#include <boost/gil/image.hpp>
#include <boost/core/lightweight_test.hpp>

// Supported Container types for histogram
#include <vector>
#include <map>
#include <array>
#include <unordered_map>

// Basic tests to make sure compatible container types produce
// expected output histogram.

namespace gil = boost::gil;

gil::gray8_image_t img1(4, 4, gil::gray8_pixel_t(1));
gil::gray8_view_t v1 = view(img1);

gil::rgb8_image_t img2(4, 4, gil::rgb8_pixel_t(1));
gil::rgb8_view_t v2 = view(img2);

gil::gray16_image_t img3(4, 4, gil::gray16_pixel_t(1));
gil::gray16_view_t v3 = view(img3);

template<typename T>
bool check_equal(T &cont1, T &cont2, std::size_t size)
{
    bool ch = true;
    for(std::size_t i = 0; i < size; ++i)
    {
        ch = ch & (cont1[i] == cont2[i]);
    }
    return ch;
}

template<typename T>
bool check_equal(T &cont1, T &cont2)
{
    bool ch = true;
    for(auto &it : cont1)
    {
        ch = ch & (cont1[it.first] == cont2[it.first]);
    }
    return ch;
}

void check_fill_histogram_vector()
{
    std::vector<int> c1, c1_expected(256,0);
    c1_expected[1] = 16;
    gil::fill_histogram(v1, c1);
    BOOST_TEST(check_equal(c1, c1_expected, c1_expected.size()));

    c1_expected[1] = 32;
    gil::fill_histogram(v1, c1, true);
    BOOST_TEST(check_equal(c1, c1_expected, c1_expected.size()));

    std::vector<int> c2, c2_expected;
    gil::fill_histogram(v2, c2);
    gil::fill_histogram(gil::color_converted_view<gil::gray8_pixel_t>(v2), c2_expected);
    BOOST_TEST(check_equal(c2, c2_expected, c2_expected.size()));
}

void check_fill_histogram_array()
{
    std::array<int, 256> c1{0}, c1_expected{0};
    c1_expected[1] = 16;
    gil::fill_histogram(v1, c1);
    BOOST_TEST(check_equal(c1, c1_expected, c1_expected.size()));

    c1_expected[1] = 32;
    gil::fill_histogram(v1, c1, true);
    BOOST_TEST(check_equal(c1, c1_expected, c1_expected.size()));

    std::array<int, 256> c2{0}, c2_expected{0};
    gil::fill_histogram(v1, c2);
    gil::fill_histogram(gil::color_converted_view<gil::gray8_pixel_t>(v2), c2_expected);
    BOOST_TEST(check_equal(c2, c2_expected, c2_expected.size()));

    // Check binning
    std::array<int, 256> c3{0}, c3_expected{0};
    c3_expected[0] = 16;
    gil::fill_histogram(v3, c3);
    BOOST_TEST(check_equal(c3, c3_expected, c3_expected.size()));
}

void check_fill_histogram_map()
{
    std::map<int, int> c1, c1_expected;
    c1_expected[1] = 16;
    gil::fill_histogram(v1, c1);
    BOOST_TEST(check_equal(c1, c1_expected));

    c1_expected[1] = 32;
    gil::fill_histogram(v1, c1, true);
    BOOST_TEST(check_equal(c1, c1_expected));

    std::map<int, int> c2, c2_expected;
    gil::fill_histogram(v2, c2);
    gil::fill_histogram(gil::color_converted_view<gil::gray8_pixel_t>(v2), c2_expected);
    BOOST_TEST(check_equal(c2, c2_expected));
}

void check_cumulative_histogram_vector()
{
    std::vector<int> v(8);
    for (std::size_t i = 0; i < v.size(); i++)
    {
        v[i] = 1;
    }
    auto v1 = gil::cumulative_histogram(v);
    bool check = true;
    for (std::size_t i = 0; i < v.size(); i++)
    {
        if(v1[i] != int(i) + 1)
            check = false;
    }
    BOOST_TEST(check);
}

void check_cumulative_histogram_array()
{
    std::array<int, 8> arr;
    for (std::size_t i = 0; i < arr.size(); i++)
    {
        arr[i] = 1;
    }
    auto arr1 = gil::cumulative_histogram(arr);
    bool check = true;
    for (std::size_t i = 0; i < arr.size(); i++)
    {
        if(arr1[i] != int(i) + 1)
            check = false;
    }
    BOOST_TEST(check);
}

void check_cumulative_histogram_map()
{
    std::map<int, int> mp;
    for (std::size_t i = 0; i < 8; i++)
    {
        mp[i] = 1;
    }
    auto mp1 = gil::cumulative_histogram(mp);
    bool check = true;
    for (std::size_t i = 0; i < mp.size(); i++)
    {
        if(mp1[i] != int(i) + 1)
            check = false;
    }
    BOOST_TEST(check);
}

int main()
{
    check_fill_histogram_vector();
    check_fill_histogram_array();
    check_fill_histogram_map();

    check_cumulative_histogram_vector();
    check_cumulative_histogram_array();
    check_cumulative_histogram_map();

    return boost::report_errors();
}


//==============================================================================
//         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
//==============================================================================
#ifndef BOOST_SIMD_CONSTANT_CONSTANTS_MINF_HPP_INCLUDED
#define BOOST_SIMD_CONSTANT_CONSTANTS_MINF_HPP_INCLUDED

#include <boost/simd/include/functor.hpp>
#include <boost/simd/meta/float.hpp>
#include <boost/simd/meta/int_c.hpp>
#include <boost/simd/meta/double.hpp>
#include <boost/simd/sdk/meta/make_dependent.hpp>
#include <boost/simd/constant/hierarchy.hpp>
#include <boost/simd/sdk/config.hpp>

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

     Represents the Minf constant in generic contexts.

     @par Models:
        Hierarchy
   **/
    struct Valmin;

    struct Minf : ext::pure_constant_<Minf>
    {
      typedef double                    default_type;
      typedef ext::pure_constant_<Minf> parent;

      /// INTERNAL ONLY
      template<class Target, class Dummy=void>
      struct apply : meta::make_dependent<Valmin, Dummy>::type::template apply<Target,Dummy> {};
      template<class... Args>
      static BOOST_FORCEINLINE BOOST_AUTO_DECLTYPE dispatch(Args&&... args)
      BOOST_AUTO_DECLTYPE_BODY( dispatching_Minf( ext::adl_helper(), static_cast<Args&&>(args)... ) )
    };

    /// INTERNAL ONLY
    template<class T, class Dummy>
    struct  Minf::apply<boost::dispatch::meta::single_<T>,Dummy>
          : meta::single_<0xFF800000> {};

    /// INTERNAL ONLY
    template<class T, class Dummy>
    struct  Minf::apply<boost::dispatch::meta::double_<T>,Dummy>
          : meta::double_<0xFFF0000000000000ULL> {};
  }
  namespace ext
  {
   template<class Site, class... Ts>
   BOOST_FORCEINLINE generic_dispatcher<tag::Minf, Site> dispatching_Minf(adl_helper, boost::dispatch::meta::unknown_<Site>, boost::dispatch::meta::unknown_<Ts>...)
   {
     return generic_dispatcher<tag::Minf, Site>();
   }
   template<class... Args>
   struct impl_Minf;
  }
  /*!
    Generates  -inf IEEE value (\f$ - \infty \f$) for floating types and minimum value
    for integer types

    @par Semantic:

    @code
    T r = Minf<T>();
    @endcode

    is similar to:

    @code
    if T is integral
      r = Valmin<T>()
    else if T is floating
      r =  IEEE -inf value;
    @endcode
  **/
  BOOST_SIMD_CONSTANT_IMPLEMENTATION(boost::simd::tag::Minf, Minf)
} }

#include <boost/simd/constant/include/constants/valmin.hpp>
#include <boost/simd/constant/common.hpp>

#endif


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

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

 * See LICENSE for the license information

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

/**
 *  @file   testSymbolicFactorGraph.cpp
 *  @brief  Unit tests for symbolic factor graphs
 *  @author Christian Potthast
 **/

#include <gtsam/symbolic/SymbolicFactorGraph.h>

#include <gtsam/base/TestableAssertions.h>
#include <gtsam/symbolic/SymbolicBayesNet.h>
#include <gtsam/symbolic/SymbolicBayesTree.h>
#include <gtsam/symbolic/SymbolicConditional.h>
#include <gtsam/symbolic/tests/symbolicExampleGraphs.h>

#include <CppUnitLite/TestHarness.h>

#include <boost/assign/std/set.hpp>

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

/* ************************************************************************* */
TEST(SymbolicFactorGraph, keys1) {
  KeySet expected;
  expected += 0, 1, 2, 3, 4;
  KeySet actual = simpleTestGraph1.keys();
  EXPECT(expected == actual);
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, keys2) {
  KeySet expected;
  expected += 0, 1, 2, 3, 4, 5;
  KeySet actual = simpleTestGraph2.keys();
  EXPECT(expected == actual);
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, eliminateFullSequential) {
  // Test with simpleTestGraph1
  Ordering order;
  order += 0, 1, 2, 3, 4;
  SymbolicBayesNet actual1 = *simpleTestGraph1.eliminateSequential(order);
  EXPECT(assert_equal(simpleTestGraph1BayesNet, actual1));

  // Test with Asia graph
  SymbolicBayesNet actual2 = *asiaGraph.eliminateSequential(asiaOrdering);
  EXPECT(assert_equal(asiaBayesNet, actual2));
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, eliminatePartialSequential) {
  // Eliminate 0 and 1
  const Ordering order = list_of(0)(1);

  const SymbolicBayesNet expectedBayesNet =
      list_of(SymbolicConditional(0, 1, 2))(SymbolicConditional(1, 2, 3, 4));

  const SymbolicFactorGraph expectedSfg = list_of(SymbolicFactor(2, 3))(
      SymbolicFactor(4, 5))(SymbolicFactor(2, 3, 4));

  SymbolicBayesNet::shared_ptr actualBayesNet;
  SymbolicFactorGraph::shared_ptr actualSfg;
  boost::tie(actualBayesNet, actualSfg) =
      simpleTestGraph2.eliminatePartialSequential(Ordering(list_of(0)(1)));

  EXPECT(assert_equal(expectedSfg, *actualSfg));
  EXPECT(assert_equal(expectedBayesNet, *actualBayesNet));

  SymbolicBayesNet::shared_ptr actualBayesNet2;
  SymbolicFactorGraph::shared_ptr actualSfg2;
  boost::tie(actualBayesNet2, actualSfg2) =
      simpleTestGraph2.eliminatePartialSequential(
          list_of(0)(1).convert_to_container<KeyVector>());

  EXPECT(assert_equal(expectedSfg, *actualSfg2));
  EXPECT(assert_equal(expectedBayesNet, *actualBayesNet2));
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, eliminateFullMultifrontal) {
  Ordering ordering;
  ordering += 0, 1, 2, 3;
  SymbolicBayesTree actual1 = *simpleChain.eliminateMultifrontal(ordering);
  EXPECT(assert_equal(simpleChainBayesTree, actual1));

  SymbolicBayesTree actual2 = *asiaGraph.eliminateMultifrontal(asiaOrdering);
  EXPECT(assert_equal(asiaBayesTree, actual2));
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, eliminatePartialMultifrontal) {
  SymbolicBayesTree expectedBayesTree;
  SymbolicConditional::shared_ptr root =
      std::make_shared<SymbolicConditional>(
          SymbolicConditional::FromKeys(list_of(4)(5)(1), 2));
  expectedBayesTree.insertRoot(
      std::make_shared<SymbolicBayesTreeClique>(root));

  SymbolicFactorGraph expectedFactorGraph =
      list_of(SymbolicFactor(0, 1))(SymbolicFactor(0, 2))(SymbolicFactor(1, 3))(
          SymbolicFactor(2, 3))(SymbolicFactor(1));

  SymbolicBayesTree::shared_ptr actualBayesTree;
  SymbolicFactorGraph::shared_ptr actualFactorGraph;
  std::tie(actualBayesTree, actualFactorGraph) =
      simpleTestGraph2.eliminatePartialMultifrontal(Ordering(list_of(4)(5)));

  EXPECT(assert_equal(expectedFactorGraph, *actualFactorGraph));
  EXPECT(assert_equal(expectedBayesTree, *actualBayesTree));

  SymbolicBayesTree expectedBayesTree2;
  SymbolicBayesTreeClique::shared_ptr root2 =
      std::make_shared<SymbolicBayesTreeClique>(
          std::make_shared<SymbolicConditional>(4, 1));
  root2->children.push_back(std::make_shared<SymbolicBayesTreeClique>(
      std::make_shared<SymbolicConditional>(5, 4)));
  expectedBayesTree2.insertRoot(root2);

  SymbolicBayesTree::shared_ptr actualBayesTree2;
  SymbolicFactorGraph::shared_ptr actualFactorGraph2;
  boost::tie(actualBayesTree2, actualFactorGraph2) =
      simpleTestGraph2.eliminatePartialMultifrontal(
          list_of<Key>(4)(5).convert_to_container<KeyVector>());

  EXPECT(assert_equal(expectedFactorGraph, *actualFactorGraph2));
  EXPECT(assert_equal(expectedBayesTree2, *actualBayesTree2));
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, marginalMultifrontalBayesNet) {
  SymbolicBayesNet expectedBayesNet =
      list_of(SymbolicConditional(0, 1, 2))(SymbolicConditional(1, 2, 3))(
          SymbolicConditional(2, 3))(SymbolicConditional(3));

  SymbolicBayesNet actual1 = *simpleTestGraph2.marginalMultifrontalBayesNet(
      Ordering(list_of(0)(1)(2)(3)));
  EXPECT(assert_equal(expectedBayesNet, actual1));
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, eliminate_disconnected_graph) {
  SymbolicFactorGraph fg;
  fg.push_factor(0, 1);
  fg.push_factor(0, 2);
  fg.push_factor(1, 2);
  fg.push_factor(3, 4);

  // create expected Chordal bayes Net
  SymbolicBayesNet expected;
  expected.push_back(std::make_shared<SymbolicConditional>(0, 1, 2));
  expected.push_back(std::make_shared<SymbolicConditional>(1, 2));
  expected.push_back(std::make_shared<SymbolicConditional>(2));
  expected.push_back(std::make_shared<SymbolicConditional>(3, 4));
  expected.push_back(std::make_shared<SymbolicConditional>(4));

  Ordering order;
  order += 0, 1, 2, 3, 4;
  SymbolicBayesNet actual = *fg.eliminateSequential(order);

  EXPECT(assert_equal(expected, actual));
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, marginals) {
  // Create factor graph
  SymbolicFactorGraph fg;
  fg.push_factor(0, 1);
  fg.push_factor(0, 2);
  fg.push_factor(1, 4);
  fg.push_factor(2, 4);
  fg.push_factor(3, 4);

  // eliminate
  Ordering ord(list_of(3)(4)(2)(1)(0));
  auto actual = fg.eliminateSequential(ord);
  SymbolicBayesNet expected;
  expected.emplace_shared<SymbolicConditional>(3, 4);
  expected.emplace_shared<SymbolicConditional>(4, 1, 2);
  expected.emplace_shared<SymbolicConditional>(2, 0, 1);
  expected.emplace_shared<SymbolicConditional>(1, 0);
  expected.emplace_shared<SymbolicConditional>(0);
  EXPECT(assert_equal(expected, *actual));

  {
    // jointBayesNet
    Ordering ord(list_of(0)(4)(3));
    auto actual = fg.eliminatePartialSequential(ord);
    SymbolicBayesNet expectedBN;
    expectedBN.emplace_shared<SymbolicConditional>(0, 1, 2);
    expectedBN.emplace_shared<SymbolicConditional>(4, 1, 2, 3);
    expectedBN.emplace_shared<SymbolicConditional>(3, 1, 2);
    EXPECT(assert_equal(expectedBN, *(actual.first)));
  }

  {
    // jointBayesNet
    Ordering ord(list_of(0)(2)(3));
    auto actual = fg.eliminatePartialSequential(ord);
    SymbolicBayesNet expectedBN;
    expectedBN.emplace_shared<SymbolicConditional>(0, 1, 2);
    expectedBN.emplace_shared<SymbolicConditional>(2, 1, 4);
    expectedBN.emplace_shared<SymbolicConditional>(3, 4);
    EXPECT(assert_equal(expectedBN, *(actual.first)));
  }

  {
    // conditionalBayesNet
    Ordering ord(list_of(0)(2));
    auto actual = fg.eliminatePartialSequential(ord);
    SymbolicBayesNet expectedBN;
    expectedBN.emplace_shared<SymbolicConditional>(0, 1, 2);
    expectedBN.emplace_shared<SymbolicConditional>(2, 1, 4);
    EXPECT(assert_equal(expectedBN, *(actual.first)));
  }
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, constructFromBayesNet) {
  // create expected factor graph
  SymbolicFactorGraph expected;
  expected.push_factor(0, 1, 2);
  expected.push_factor(1, 2);
  expected.push_factor(1);

  // create Bayes Net
  SymbolicBayesNet bayesNet;
  bayesNet += SymbolicConditional(0, 1, 2);
  bayesNet += SymbolicConditional(1, 2);
  bayesNet += SymbolicConditional(1);

  // create actual factor graph from a Bayes Net
  SymbolicFactorGraph actual(bayesNet);

  CHECK(assert_equal(expected, actual));
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, constructFromBayesTree) {
  // create expected factor graph
  SymbolicFactorGraph expected;
  expected.push_factor(_E_, _L_, _B_);
  expected.push_factor(_S_, _B_, _L_);
  expected.push_factor(_T_, _E_, _L_);
  expected.push_factor(_X_, _E_);

  // create actual factor graph
  SymbolicFactorGraph actual(asiaBayesTree);

  CHECK(assert_equal(expected, actual));
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, push_back) {
  // Create two factor graphs and expected combined graph
  SymbolicFactorGraph fg1, fg2, expected;

  fg1.push_factor(1);
  fg1.push_factor(0, 1);

  fg2.push_factor(1, 2);
  fg2.push_factor(0, 2);

  expected.push_factor(1);
  expected.push_factor(0, 1);
  expected.push_factor(1, 2);
  expected.push_factor(0, 2);

  // combine
  SymbolicFactorGraph actual;
  actual.push_back(fg1);
  actual.push_back(fg2);
  CHECK(assert_equal(expected, actual));

  // combine in second way
  SymbolicFactorGraph actual2 = fg1;
  actual2.push_back(fg2);
  CHECK(assert_equal(expected, actual2));
}

/* ************************************************************************* */
TEST(SymbolicFactorGraph, add_factors) {
  SymbolicFactorGraph fg1;
  fg1.push_factor(10);
  fg1 += SymbolicFactor::shared_ptr();  // empty slot!
  fg1.push_factor(11);

  SymbolicFactorGraph fg2;
  fg2.push_factor(1);
  fg2.push_factor(2);

  SymbolicFactorGraph expected;
  expected.push_factor(10);
  expected.push_factor(1);
  expected.push_factor(11);
  expected.push_factor(2);
  const FactorIndices expectedIndices = list_of(1)(3);
  const FactorIndices actualIndices = fg1.add_factors(fg2, true);

  EXPECT(assert_equal(expected, fg1));
  EXPECT(assert_container_equality(expectedIndices, actualIndices));

  expected.push_factor(1);
  expected.push_factor(2);
  const FactorIndices expectedIndices2 = list_of(4)(5);
  const FactorIndices actualIndices2 = fg1.add_factors(fg2, false);

  EXPECT(assert_equal(expected, fg1));
  EXPECT(assert_container_equality(expectedIndices2, actualIndices2));
}

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


#include <burst/container/make_list.hpp>
#include <burst/container/make_set.hpp>
#include <burst/container/make_vector.hpp>
#include <burst/range/difference.hpp>

#include <doctest/doctest.h>

#include <boost/range/algorithm/for_each.hpp>
#include <boost/range/algorithm/set_algorithm.hpp>

#include <functional>
#include <vector>

TEST_SUITE("difference")
{
    TEST_CASE("Разность двух диапазонов – это элементы первого диапазона, "
        "которые отсутствуют во втором диапазоне")
    {
        const auto natural = burst::make_vector({1, 2, 3, 4, 5, 6});
        const auto     odd = burst::make_vector({1,    3,    5   });
        //                                          ^     ^     ^

        const auto difference = burst::difference(natural, odd);

        const auto even = burst::make_vector({2, 4, 6});
        CHECK(difference == even);
    }

    TEST_CASE("Допускает мультимножества")
    {
        const auto    minuend = burst::make_vector({1, 1, 1, 2, 2, 3});
        const auto subtrahend = burst::make_vector({1, 1,    2      });
        //                                                ^     ^  ^

        const auto difference = burst::difference(minuend, subtrahend);

        const auto expected = burst::make_vector({1, 2, 3});
        CHECK(difference == expected);
    }

    TEST_CASE("Разность диапазона с самим собой — пустой диапазон")
    {
        const auto set = burst::make_vector({'a', 'b', 'c'});

        const auto difference = burst::difference(set, set);

        CHECK(difference.empty());
    }

    TEST_CASE("Разность диапазона с пустым диапазоном – это тот же диапазон")
    {
        const auto set = burst::make_vector({3, 2, 1});
        const auto empty_set = std::vector<int>{};

        const auto difference = burst::difference(set, empty_set, std::greater<>{});

        CHECK(difference == set);
    }

    TEST_CASE("Разность пустого диапазона с любым непустым диапазоном — пустой диапазон")
    {
        const auto empty_set = std::vector<int>{};
        const auto set = burst::make_set({1, 2, 3});

        const auto difference = burst::difference(empty_set, set);

        CHECK(difference.empty());
    }

    TEST_CASE("Разность пилообразно дополняющих друг друга диапазонов равна уменьшаемому")
    {
        const auto    minuend = burst::make_list({'h',      'f',      'd',      'b'     });
        const auto subtrahend = burst::make_list({     'g',      'e',      'c',      'a'});
        //                                         ^         ^         ^         ^

        const auto difference = burst::difference(minuend, subtrahend, std::greater<>{});

        CHECK(difference == minuend);
    }

    TEST_CASE("Если вычитаемый диапазон окружает уменьшаемый, то их разность равна уменьшаемому")
    {
        const auto    minuend =   burst::make_list({       4, 6, 8       });
        const auto subtrahend = burst::make_vector({-1, 2,         10, 11});
        //                                                 ^  ^  ^

        const auto difference = burst::difference(minuend, subtrahend);

        CHECK(difference == minuend);
    }

    TEST_CASE("Допускает диапазоны разных типов")
    {
        const auto    minuend =   burst::make_list({0,    4,    6,    7, 9, 10    });
        const auto subtrahend = burst::make_vector({0, 2,    5, 6, 6,       10, 11});
        //                                                ^           ^  ^

        const auto difference = burst::difference(minuend, subtrahend);

        const auto expected = burst::make_vector({4, 7, 9});
        CHECK(difference == expected);
    }

    TEST_CASE("Результирующий диапазон изменяем")
    {
        auto    minuend = burst::make_vector({1,    3,    5, 7, 9});
        auto subtrahend = burst::make_vector({   2, 3, 4, 5      });
        //                                    ^              ^  ^

        auto difference = burst::difference(minuend, subtrahend);

        boost::for_each(difference, [] (auto & x) { x *= 2; });

        const auto expected = burst::make_vector({2, 14, 18});
        CHECK(difference == expected);
    }
}


/*
 * GridTools
 *
 * Copyright (c) 2014-2019, ETH Zurich
 * All rights reserved.
 *
 * Please, refer to the LICENSE file in the root directory.
 * SPDX-License-Identifier: BSD-3-Clause
 */
#pragma once

#include <cmath>

#include <boost/mpl/and.hpp>
#include <boost/type_traits.hpp>

#include "../../common/array.hpp"
#include "../../common/generic_metafunctions/is_all_integrals.hpp"
#include "../../common/gt_assert.hpp"
#include "../../common/gt_math.hpp"
#include "../../common/layout_map.hpp"
#include "../../meta/utility.hpp"
#include "alignment.hpp"
#include "halo.hpp"

namespace gridtools {

    /** \ingroup storage
     * @{
     */

    /* forward declaration */
    template <typename T>
    struct is_alignment;

    /*
     * @brief struct used to replace masked dimensions with size 1. otherwise the
     * passed dimension is returned unmodified.
     * @tparam LayoutArg layout map entry
     */
    template <int LayoutArg>
    struct handle_masked_dims {
        template <typename Dim>
        GT_FUNCTION static constexpr uint_t extend(Dim d) {
            GT_STATIC_ASSERT(
                boost::is_integral<Dim>::value, GT_INTERNAL_ERROR_MSG("Dimensions has to be integral type."));
            return error_or_return((d > 0),
                ((LayoutArg == -1) ? 1 : d),
                "Tried to instantiate storage info with zero or negative dimensions");
        }
    };

    /*
     * @brief function used to provide an aligned dimension
     * @tparam Alignment alignment information
     * @tparam Length Layout map length
     * @tparam LayoutArg layout map entry
     * @return return aligned dimension if it should be aligned, otherwise return as is.
     */
    template <typename Alignment, int_t MaxLayoutV, int LayoutArg, typename Int>
    GT_FUNCTION constexpr uint_t pad_dimensions(Int dimension) {
        GT_STATIC_ASSERT(is_alignment<Alignment>::value, GT_INTERNAL_ERROR_MSG("Passed type is no alignment type"));
        return ((Alignment::value > 1) && (LayoutArg == MaxLayoutV))
                   ? gt_ceil((float)dimension / (float)Alignment::value) * Alignment::value
                   : dimension;
    }

    /*
     * @brief helper struct used to compute the strides in a constexpr manner
     * @tparam T the layout map type
     */
    template <typename T>
    struct get_strides_aux;

    template <int... LayoutArgs>
    struct get_strides_aux<layout_map<LayoutArgs...>> {
        typedef layout_map<LayoutArgs...> layout_map_t;

        template <int N, typename... Dims>
        GT_FUNCTION static constexpr enable_if_t<N != -1 && N != layout_map_t::unmasked_length - 1, uint_t> get_stride(
            Dims... d) {
            return (get_value_from_pack(get_index_of_element_in_pack(0, N + 1, LayoutArgs...), d...)) *
                   get_stride<N + 1>(d...);
        }

        template <int N, typename... Dims>
        GT_FUNCTION static constexpr enable_if_t<N == -1, uint_t> get_stride(Dims...) {
            return 0;
        }

        template <int N, typename... Dims>
        GT_FUNCTION static constexpr enable_if_t<N != -1 && N == layout_map_t::unmasked_length - 1, uint_t> get_stride(
            Dims...) {
            return 1;
        }
    };

    /*
     * @brief struct used to compute the strides given the dimensions (e.g., 128x128x80)
     * @tparam Layout layout map
     */
    template <typename Layout>
    struct get_strides;

    template <int... LayoutArgs>
    struct get_strides<layout_map<LayoutArgs...>> {
        template <typename... Dims,
            enable_if_t<sizeof...(Dims) == sizeof...(LayoutArgs) && is_all_integral<Dims...>::value, int> = 0>
        GT_FUNCTION static constexpr array<uint_t, sizeof...(LayoutArgs)> get_stride_array(Dims... ds) {
            typedef layout_map<LayoutArgs...> Layout;
            return (array<uint_t, Layout::masked_length>){
                get_strides_aux<Layout>::template get_stride<LayoutArgs>(ds...)...};
        }
    };

    /**
     * @}
     */
} // namespace gridtools


/**

\file
\author Datta Ramadasan
//==============================================================================
//         Copyright 2015 INSTITUT PASCAL UMR 6602 CNRS/Univ. Clermont II
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================

*/

#ifndef __OPTIMISATION2_BA_MAKE_TYPE_HPP__
#define __OPTIMISATION2_BA_MAKE_TYPE_HPP__

#include <libv/lma/global.hpp>
#include <boost/mpl/vector.hpp>
#include "container.hpp"
#include <libv/lma/ttt/mpl/for.hpp>

namespace lma
{
  typedef bf::pair<mpl::_1,mpl::_1> pair_;

  template<class A, class B, class Float> struct MakeTupleTable
  { typedef bf::pair<bf::pair<A,B>,Table<A,B,Float>> type;};

  template<class A, class Float> struct MakeTupleTable<A,A,Float>
  { typedef bf::pair<bf::pair<A,A>,Table<A,A,Float,Symetric>> type;};

  template<class A, class Float> struct VectorToPairStruct  { typedef bf::pair<A,lma::VectorColumn<A,Float>> type; };
  template<class T> struct VectorToKey { typedef T type; };
}

#endif


#include "DataStructures/Arrays/ArrayQuestions.h"

#include <boost/test/unit_test.hpp>
#include <string>
#include <vector>

using
  DataStructures::Arrays::ArrayQuestions::CrackingTheCodingInterview::
    is_unique_character_string;
using DataStructures::Arrays::ArrayQuestions::LeetCode::max_profit;
using std::string;
using std::vector;

BOOST_AUTO_TEST_SUITE(DataStructures)
BOOST_AUTO_TEST_SUITE(Arrays)
BOOST_AUTO_TEST_SUITE(ArrayQuestions_tests)

//------------------------------------------------------------------------------
/// \ref https://en.cppreference.com/w/cpp/language/ascii
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(ASCIIDecimalConversionToChar)
{
  int i {33};

  BOOST_TEST(static_cast<char>(i) == '!');
}

BOOST_AUTO_TEST_CASE(CharConversionToASCIIDecimal)
{
  BOOST_TEST(static_cast<int>('#') == 35);

  const char c {'('};

  BOOST_TEST(static_cast<int>(c) == 40);
}

//------------------------------------------------------------------------------
/// \ref Gayle Laakmann McDowell, 6th Ed.
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_SUITE(CrackingTheCodingInterview_tests)

//------------------------------------------------------------------------------
/// \ref pp. 90, 1.1 Is Unique, McDowell, 6th. Ed.
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(IsUniqueCharacterStringWorksWithCArray)
{
  const string duplicate_string {"GeeksforGeeks"};
  const string not_duplicate_string {"algorithm"};

  BOOST_TEST(!is_unique_character_string(duplicate_string));
  BOOST_TEST(is_unique_character_string(not_duplicate_string));
}

BOOST_AUTO_TEST_SUITE_END() // CrackingTheCodingInterview_tests

BOOST_AUTO_TEST_SUITE(Leetcode_tests)

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(BestTimeToBuyAndSellStockIIElementaryCases)
{
  {
    vector<int> prices {42};
    BOOST_TEST(max_profit(prices) == 0);
  }
  {
    vector<int> prices {7, 2};
    BOOST_TEST(max_profit(prices) == 0);
  }
  {
    vector<int> prices {3, 5};
    BOOST_TEST(max_profit(prices) == 2);
  }
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(BestTimeToBuyAndSellStockIIWithPointers)
{
  {
    const vector<int> prices {7, 1, 5, 3, 6, 4};

  }
  {
    const vector<int> prices {1, 2, 3, 4, 5};

  }
  {
    const vector<int> prices {7, 6, 4, 3, 1};

  }
}

BOOST_AUTO_TEST_SUITE_END() // Leetcode_tests

BOOST_AUTO_TEST_SUITE_END() // ArrayQuestions_tests
BOOST_AUTO_TEST_SUITE_END() // Arrays
BOOST_AUTO_TEST_SUITE_END() // DataStructures