/**
 * Addle source code
 * @file
 * @copyright Copyright 2020 Eleanor Hawk
 * @copyright Modification and distribution permitted under the terms of the
 * MIT License. See "LICENSE" for full details.
 */

#ifndef MULTIARRAY_HPP
#define MULTIARRAY_HPP

#include <type_traits>
#include <boost/multi_array.hpp>
#include <boost/array.hpp>

#include <QSharedData>
#include <QList>
#include <QSize>
#include <QPoint>
#include <QRect>
namespace Addle {

/**
 * @class MultiArray
 * @brief Implicitly shared wrapper for Boost.MultiArray
 *
 * Some extra sugar is given for 2D arrays.
 */
template<typename T, std::size_t N, typename Allocator = std::allocator<T>>
class MultiArray
{
    struct MultiArrayData : QSharedData
    {
        MultiArrayData() = default;

        explicit MultiArrayData(const QList<typename boost::multi_array<T, N, Allocator>::size_type>& sizes,
            const typename boost::multi_array<T, N, Allocator>::storage_order_type& store,
            const Allocator& alloc)
            : arr(sizes, store, alloc)
        {
        }

        explicit MultiArrayData(const boost::detail::multi_array::extent_gen<N>& ranges,
            const typename boost::multi_array<T, N, Allocator>::storage_order_type& store,
            const Allocator& alloc)
            : arr(ranges, store, alloc)
        {
        }

        MultiArrayData(const boost::multi_array<T,N>& x)
            : arr(x)
        {
        }

        MultiArrayData(const MultiArrayData&) = default;

        boost::multi_array<T, N, Allocator> arr;
    };
public:
    typedef typename boost::multi_array<T, N, Allocator>::element                element;
    typedef typename boost::multi_array<T, N, Allocator>::value_type             value_type;
    typedef typename boost::multi_array<T, N, Allocator>::reference              reference;
    typedef typename boost::multi_array<T, N, Allocator>::const_reference        const_reference;
    typedef typename boost::multi_array<T, N, Allocator>::difference_type        difference_type;
    typedef typename boost::multi_array<T, N, Allocator>::iterator               iterator;
    typedef typename boost::multi_array<T, N, Allocator>::const_iterator         const_iterator;
    typedef typename boost::multi_array<T, N, Allocator>::reverse_iterator       reverse_iterator;
    typedef typename boost::multi_array<T, N, Allocator>::const_reverse_iterator const_reverse_iterator;
    typedef typename boost::multi_array<T, N, Allocator>::size_type              size_type;
    typedef typename boost::multi_array<T, N, Allocator>::index                  index;
    typedef typename boost::multi_array<T, N, Allocator>::index_gen              index_gen;
    typedef typename boost::multi_array<T, N, Allocator>::index_range            index_range;
    typedef typename boost::multi_array<T, N, Allocator>::extent_gen             extent_gen;
    typedef typename boost::multi_array<T, N, Allocator>::extent_range           extent_range;
    typedef typename boost::multi_array<T, N, Allocator>::storage_order_type     storage_order_type;

    static const std::size_t dimensionality = N;

    MultiArray() : _data(new MultiArrayData) {}
    MultiArray(const MultiArray&) = default;
    MultiArray(MultiArray&& other)
        : _data(other._data)
    {
        other._data = nullptr;
    }

    explicit MultiArray(const QList<size_type>& sizes,
        const storage_order_type& store = boost::c_storage_order(),
        const Allocator& alloc = Allocator())
        : _data(new MultiArrayData(sizes, store, alloc))
    {
    }

    template<typename = typename std::enable_if<N == 2>::type>
    explicit MultiArray(QSize size,
        const storage_order_type& store = boost::c_storage_order(),
        const Allocator& alloc = Allocator())
        : _data(new MultiArrayData(boost::extents[size.width()][size.height()], store, alloc))
    {
    }

    MultiArray(const boost::multi_array<T, N, Allocator>& x)
    {
    }

    MultiArray& operator=(const MultiArray&) = default;

    inline const boost::multi_array<T, N, Allocator>& constArray() const { return _data->arr; }
    inline const boost::multi_array<T, N, Allocator>& array() const { return _data->arr; }
    inline boost::multi_array<T, N, Allocator>& array() { return _data->arr; }

    inline QList<size_type> shape() const
    {
        QList<size_type> shape;
        shape.reserve(N);
        for (int i = 0; i < N; i++)
        {
            shape.append(_data->arr.shape()[i]);
        }
        return shape;
    }

    template<typename = typename std::enable_if<N == 2>::type>
    inline QSize size() const
    {
        return QSize(_data->arr.shape()[0], _data->arr.shape()[1]);
    }

    template<typename = typename std::enable_if<N == 2>::type>
    inline QRect rect() const
    {
        return QRect(QPoint(), size());
    }

    template<typename = typename std::enable_if<N == 2>::type>
    inline size_type width() const
    {
        return _data->arr.shape()[0];
    }

    template<typename = typename std::enable_if<N == 2>::type>
    inline size_type height() const
    {
        return _data->arr.shape()[1];
    }

    inline size_type num_elements() const
    {
        return _data->arr.num_elements();
    }

    inline iterator begin() { return _data->arr.begin(); }
    inline const_iterator begin() const { return _data->arr.begin(); }

    inline iterator end() { return _data->arr.end(); }
    inline const_iterator end() const { return _data->arr.end(); }

    inline reverse_iterator rbegin() { return _data->arr.rbegin(); }
    inline const_reverse_iterator rbegin() const { return _data->arr.rbegin(); }

    inline reverse_iterator rend() { return _data->arr.rend(); }
    inline const_reverse_iterator rend() const { return _data->arr.rend(); }

    template <typename ExtentList>
    inline void resize(const ExtentList& extents) { _data->arr.resize(extents); }

    inline typename std::enable_if<N == 2, void>::type
    resize(QSize size) { _data->arr.resize(boost::extents[size.width()][size.height()]); }

    inline typename std::enable_if<N == 2, void>::type
    resize(size_type width, size_type height) { _data->arr.resize(boost::extents[width][height]); }

    template <typename IndexList>
    inline element& rAt(const IndexList& indices) { return _data->arr(indices); }

    inline typename std::enable_if<N == 2, element&>::type
    rAt(QPoint pos) { return _data->arr[pos.x()][pos.y()]; }

    inline typename std::enable_if<N == 2, element&>::type
    rAt(index x, index y) { return _data->arr[x][y]; }

    template <typename IndexList>
    void insert(const IndexList& indices, const element& value) { _data->arr(indices) = value; }

    inline typename std::enable_if<N == 2, void>::type
    insert(QPoint pos, const element& value) { _data->arr[pos.x()][pos.y()] = value; }

    inline typename std::enable_if<N == 2, void>::type
    insert(index x, index y, const element& value) { _data->arr[x][y] = value; }

    inline const element& at(const QList<index>& indices) const { return _data->arr(indices); }

    inline typename std::enable_if<N == 2, const element&>::type
    at(QPoint pos) const { return _data->arr[pos.x()][pos.y()]; }

    inline typename std::enable_if<N == 2, const element&>::type
    at(index x, index y) const { return _data->arr[x][y]; }

    inline reference operator[](index i) { return _data->arr[i]; }
    inline const_reference operator[](index i) const { return _data->arr[i]; }

    inline bool boundContains(const QList<index>& indices) const
    {
        for (auto i : indices)
        {
            if (i < 0) return false;
            if (i >= _data->arr.shape()[i]) return false;
        }
        return true;
    }

    inline typename std::enable_if<N == 2, bool>::type
    boundContains(QPoint pos) const
    {
        return pos.x() >= 0 && pos.x() < width()
            && pos.y() >= 0 && pos.y() < height();
    }

    inline typename std::enable_if<N == 2, bool>::type
    boundContains(int x, int y) const
    {
        return x >= 0 && x < width()
            && y >= 0 && y < height();
    }

private:
    QSharedDataPointer<MultiArrayData> _data;
};

template<typename T, class Allocator = std::allocator<T>>
class ConstRowViewer
{
public:
    class iterator
    {
        using index_range = boost::multi_array_types::index_range;

    public:
        typedef typename boost::multi_array_ref<T, 2>::template const_array_view<1>::type row_type;

        iterator() = default;
        iterator(const iterator&) = default;

        inline bool operator==(const iterator& x) const { return _arr_impl == x._arr_impl && _index == x._index; }
        inline bool operator!=(const iterator& x) const { return !(*this == x); }

        inline row_type operator*() const { return (*_arr_impl)[boost::indices[index_range()][_index]]; }

        inline iterator& operator++() { ++_index; return *this; }

    private:
        iterator(const boost::multi_array<T, 2, Allocator>& arr_impl, int index)
            : _arr_impl(&arr_impl), _index(index)
        {
        }

        const boost::multi_array<T, 2, Allocator>* _arr_impl = nullptr;
        int _index = 0;

        friend class ConstRowViewer;
    };

    ConstRowViewer(const MultiArray<T, 2, Allocator>& arr)
        : _arr(arr)
    {
    }

    iterator begin() const
    {
        return iterator(_arr.array(), 0);
    }

    iterator end() const
    {
        return iterator(_arr.array(), _arr.height());
    }

private:
    const MultiArray<T, 2, Allocator> _arr;
};

template<typename T, class Allocator = std::allocator<T>>
class MutableRowViewer
{
public:
    class iterator
    {
        using index_range = boost::multi_array_types::index_range;

    public:
        typedef typename boost::multi_array_ref<T, 2>::template array_view<1>::type row_type;

        iterator() = default;
        iterator(const iterator&) = default;

        inline bool operator==(const iterator& x) const { return _arr_impl == x._arr_impl && _index == x._index; }
        inline bool operator!=(const iterator& x) const { return !(*this == x); }

        inline row_type operator*() const { return (*_arr_impl)[boost::indices[index_range()][_index]]; }

        inline iterator& operator++() { ++_index; return *this; }

    private:
        iterator(boost::multi_array<T, 2, Allocator>& arr_impl, int index)
            : _arr_impl(&arr_impl), _index(index)
        {
        }

        boost::multi_array<T, 2, Allocator>* _arr_impl = nullptr;
        int _index = 0;

        friend class MutableRowViewer;
    };

    MutableRowViewer(MultiArray<T, 2, Allocator>& arr)
        : _arr(arr)
    {
    }

    iterator begin() const
    {
        return iterator(_arr.array(), 0);
    }

    iterator end() const
    {
        return iterator(_arr.array(), _arr.height());
    }

private:
    MultiArray<T, 2, Allocator>& _arr;
};

} // namespace Addle
#endif // MULTIARRAY_HPP


// -*- C++ -*-
//
// Copyright Sylvain Bougerel 2009 - 2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file COPYING or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#define BOOST_TEST_DYN_LINK
#define SPATIAL_ENABLE_ASSERT // detect interal issues that should not occur

#include <boost/test/unit_test.hpp>
#include "../../src/function.hpp"
#include "spatial_test_types.hpp"

using namespace spatial;

BOOST_AUTO_TEST_CASE( test_access_minus )
{
  accessor_minus<at_accessor, int2, int> minus;
  BOOST_CHECK_EQUAL(0, minus(0, zeros, zeros));
  BOOST_CHECK_EQUAL(0, minus(1, zeros, zeros));
  BOOST_CHECK_EQUAL(-1, minus(0, ones, twos));
  BOOST_CHECK_EQUAL(-1, minus(1, ones, twos));
  BOOST_CHECK_EQUAL(1, minus(0, twos, ones));
  BOOST_CHECK_EQUAL(1, minus(1, twos, ones));
}

BOOST_AUTO_TEST_CASE( test_bracket_minus )
{
  bracket_minus<int2, int> minus;
  BOOST_CHECK_EQUAL(0, minus(0, zeros, zeros));
  BOOST_CHECK_EQUAL(0, minus(1, zeros, zeros));
  BOOST_CHECK_EQUAL(-1, minus(0, ones, twos));
  BOOST_CHECK_EQUAL(-1, minus(1, ones, twos));
  BOOST_CHECK_EQUAL(1, minus(0, twos, ones));
  BOOST_CHECK_EQUAL(1, minus(1, twos, ones));
}

BOOST_AUTO_TEST_CASE( test_paren_minus )
{
  paren_minus<int2, int> minus;
  BOOST_CHECK_EQUAL(0, minus(0, zeros, zeros));
  BOOST_CHECK_EQUAL(0, minus(1, zeros, zeros));
  BOOST_CHECK_EQUAL(-1, minus(0, ones, twos));
  BOOST_CHECK_EQUAL(-1, minus(1, ones, twos));
  BOOST_CHECK_EQUAL(1, minus(0, twos, ones));
  BOOST_CHECK_EQUAL(1, minus(1, twos, ones));
}

BOOST_AUTO_TEST_CASE( test_iterator_minus )
{
  iterator_minus<int2, int> minus;
  BOOST_CHECK_EQUAL(0, minus(0, zeros, zeros));
  BOOST_CHECK_EQUAL(0, minus(1, zeros, zeros));
  BOOST_CHECK_EQUAL(-1, minus(0, ones, twos));
  BOOST_CHECK_EQUAL(-1, minus(1, ones, twos));
  BOOST_CHECK_EQUAL(1, minus(0, twos, ones));
  BOOST_CHECK_EQUAL(1, minus(1, twos, ones));
}

BOOST_AUTO_TEST_CASE( test_access_less )
{
  // This test is hard to craft, how to make sure I'm testing "less" and not
  // the accessor?
  int2 x(0, 1);
  int2 y(1, 0);
  accessor_less<at_accessor, int2> compare;
  BOOST_CHECK(compare(0, x, y));
  BOOST_CHECK(compare(0, x, 0, y));
  BOOST_CHECK(!compare(0, y, x));
  BOOST_CHECK(!compare(0, y, 0, x));
  BOOST_CHECK(!compare(1, x, y));
  BOOST_CHECK(!compare(1, x, 1, y));
  BOOST_CHECK(compare(1, y, x));
  BOOST_CHECK(compare(1, y, 1, x));
  BOOST_CHECK(!compare(0, x, x));
  BOOST_CHECK(!compare(0, x, 0, x));
  BOOST_CHECK(compare(0, x, 1, x));
  BOOST_CHECK(!compare(0, x, 1, y));
}

BOOST_AUTO_TEST_CASE( test_bracket_less )
{
  // This has been used before this test was carried on, but at least we will
  // know here that something could fail.
  int2 x(0, 1);
  int2 y(1, 0);
  bracket_less<int2> compare;
  BOOST_CHECK(compare(0, x, y));
  BOOST_CHECK(compare(0, x, 0, y));
  BOOST_CHECK(!compare(0, y, x));
  BOOST_CHECK(!compare(0, y, 0, x));
  BOOST_CHECK(!compare(1, x, y));
  BOOST_CHECK(!compare(1, x, 1, y));
  BOOST_CHECK(compare(1, y, x));
  BOOST_CHECK(compare(1, y, 1, x));
  BOOST_CHECK(!compare(0, x, x));
  BOOST_CHECK(!compare(0, x, 0, x));
  BOOST_CHECK(compare(0, x, 1, x));
  BOOST_CHECK(!compare(0, x, 1, y));
}

BOOST_AUTO_TEST_CASE( test_iterator_less )
{
  int2 x(0, 1);
  int2 y(1, 0);
  iterator_less<int2> compare;
  BOOST_CHECK(compare(0, x, y));
  BOOST_CHECK(compare(0, x, 0, y));
  BOOST_CHECK(!compare(0, y, x));
  BOOST_CHECK(!compare(0, y, 0, x));
  BOOST_CHECK(!compare(1, x, y));
  BOOST_CHECK(!compare(1, x, 1, y));
  BOOST_CHECK(compare(1, y, x));
  BOOST_CHECK(compare(1, y, 1, x));
  BOOST_CHECK(!compare(0, x, x));
  BOOST_CHECK(!compare(0, x, 0, x));
  BOOST_CHECK(compare(0, x, 1, x));
  BOOST_CHECK(!compare(0, x, 1, y));
}

BOOST_AUTO_TEST_CASE( test_paren_less )
{
  int2 x; x[0] = 0; x[1] = 1;
  int2 y; y[0] = 1; y[1] = 0;
  paren_less<int2> compare;
  BOOST_CHECK(compare(0, x, y));
  BOOST_CHECK(compare(0, x, 0, y));
  BOOST_CHECK(!compare(0, y, x));
  BOOST_CHECK(!compare(0, y, 0, x));
  BOOST_CHECK(!compare(1, x, y));
  BOOST_CHECK(!compare(1, x, 1, y));
  BOOST_CHECK(compare(1, y, x));
  BOOST_CHECK(compare(1, y, 1, x));
  BOOST_CHECK(!compare(0, x, x));
  BOOST_CHECK(!compare(0, x, 0, x));
  BOOST_CHECK(compare(0, x, 1, x));
  BOOST_CHECK(!compare(0, x, 1, y));
}


#pragma once

#include <Eigen/Dense>

namespace convexnmf {

using Scalar    = double;
using Matrix    = Eigen::MatrixXd;
using Vector    = Eigen::VectorXd;
using MatrixRef = Eigen::Ref<const Matrix>;
using VectorRef = Eigen::Ref<const Vector>;

using MatrixXf    = Eigen::MatrixXf;
using VectorXf    = Eigen::VectorXf;
using VectorXfRef = Eigen::Ref<const VectorXf>;
using MatrixXfRef = Eigen::Ref<const MatrixXf>;
} // namespace convexnmf


#include <iostream>
#include <list>
#include <fstream>
#include <random>
#include <vector>
#include <string>
#include <limits.h>
#include <float.h>
#include <assert.h>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <map>
#include <queue>
#include <ctime>
#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <signal.h>

// forward class declaration and global variables
#include "ForwardClassDeclarations.h"
#include "GlobalVariables.h"

// RNG
#include "RNG_wrapper.h"
#include "RNG_wrapper.cpp"

int main()
{
	RNG_wrapper rngw(15);
	uint32_t nbZeros = 0;
	uint32_t nbOnes = 0;
	for (int i = 0 ; i<500000; ++i)
		rngw.get_1b() ? nbOnes++: nbZeros++;
	std::cout << nbZeros << ":" << nbOnes << "\n";

	{
		uint32_t N = 8;
		std::vector<uint32_t> counts(N);
		for (int i = 0 ; i<500000000; ++i)
			counts[rngw.uniform_int_distribution(N-1)]++;

		for (int i = 0 ; i < counts.size(); ++i)
			std::cout << counts[i] << ":";
		std::cout << "\n";
	}

	{
		uint32_t N = 8;
		std::uniform_int_distribution<uint32_t> d(0,N-1);
		std::vector<uint32_t> counts(N);
		for (int i = 0 ; i<500000000; ++i)
			counts[d(rngw.getRNG())]++;

		for (int i = 0 ; i < counts.size(); ++i)
			std::cout << counts[i] << ":";
		std::cout << "\n";
	}
}