#include <boost/archive/iterators/transform_width.hpp>


/**
 * @file
 * This file is part of SeisSol.
 *
 * @author Sebastian Rettenberger (sebastian.rettenberger @ tum.de, http://www5.in.tum.de/wiki/index.php/Sebastian_Rettenberger)
 *
 * @section LICENSE
 * Copyright (c) 2015, SeisSol Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @section DESCRIPTION
 **/

#include <cassert>
#include <cmath>

#include <hdf5.h>

#include <Eigen/Dense>

#include "utils/args.h"

#include "Geometry/refinement/RefinerUtils.h"

template<typename T>
T pow2(T v)
{
	return v*v;
}

int main(int argc, char* argv[])
{
	utils::Args args;
	args.addOption("diff", 'd', "Maximum allowed difference (Default 0.001)", utils::Args::Required, false);
	args.addOption("verbose", 'V', "Enable verbose output", utils::Args::No, false);
	args.addAdditionalOption("output.h5", "The SeisSol wave field output file");

	switch (args.parse(argc, argv)) {
	case utils::Args::Error:
		return 1;
	case utils::Args::Help:
		return 127;
	default:
		break;
	}

	bool verbose = args.getArgument("verbose", false);

	hid_t hdfFile = H5Fopen(args.getAdditionalArgument<const char*>("output.h5"),
			H5F_ACC_RDONLY, H5P_DEFAULT);

	// Read vertices
	hid_t vertData = H5Dopen(hdfFile, "/geometry", H5P_DEFAULT);
	hid_t space = H5Dget_space(vertData);
	hsize_t dims[2]; // Assuming to dimensions
	H5Sget_simple_extent_dims(space, dims, 0L);

	double* vertices = new double[dims[0]*dims[1]];
	H5Dread(vertData, H5T_NATIVE_DOUBLE, H5S_ALL, H5S_ALL, H5P_DEFAULT, vertices);

	H5Sclose(space);
	H5Dclose(vertData);

	// Read cells
	hid_t cellData = H5Dopen(hdfFile, "/connect", H5P_DEFAULT);
	space = H5Dget_space(cellData);
	H5Sget_simple_extent_dims(space, dims, 0L);

	unsigned long* cells = new unsigned long[dims[0]*dims[1]];
	H5Dread(cellData, H5T_NATIVE_ULONG, H5S_ALL, H5S_ALL, H5P_DEFAULT, cells);

	hsize_t numCells = dims[0];

	H5Sclose(space);
	H5Dclose(cellData);

	// Read the 9 variables
	double* var[9];
	const char* varNames[9] = {"/sigma_xx", "/sigma_yy", "/sigma_zz", "/sigma_xy", "/sigma_yz", "/sigma_xz", "/u", "/v", "/w"};

	hid_t memSpace = H5Screate_simple(1, &numCells, &numCells);
	H5Sselect_all(memSpace);

	for (unsigned int i = 0; i < 9; i++) {
		hid_t varData = H5Dopen(hdfFile, varNames[i], H5P_DEFAULT);
		space = H5Dget_space(varData);
		H5Sget_simple_extent_dims(space, dims, 0L);

		assert(dims[1] == numCells);
		var[i] = new double[dims[1]];

		hsize_t start[2] = {0, 0};
		hsize_t count[2] = {1, numCells};
		H5Sselect_hyperslab(space, H5S_SELECT_SET, start, 0L, count, 0L);

		H5Dread(varData, H5T_NATIVE_DOUBLE, memSpace, space, H5P_DEFAULT, var[i]);

		H5Sclose(space);
		H5Dclose(varData);
	}

	H5Sclose(memSpace);

	H5Fclose(hdfFile);

	// Compute parameters
	const double maxDiff = args.getArgument("diff", 0.001);

      Eigen::Vector3d vec_n(1, 1, 1);
      const double n = 1.0/(vec_n.norm());
      const double mu = 1.0;
      const double lambda = 2.0;
      const double rho = 1.0;

      const double r1[9] = {
                      rho * (-2.0*pow2(n)*mu - 2.0*pow2(n)*mu + lambda + 2.0*mu),
			rho * (2.0*pow2(n)*mu + lambda),
			rho * (2.0*pow2(n)*mu + lambda),
			2.0 * n * mu * n * rho,
			2.0 * mu * n * rho * n,
			2.0 * mu * n * rho * n,
			n * sqrt(rho * (lambda + 2.0*mu)),
			n * sqrt(rho * (lambda + 2.0*mu)),
			sqrt(rho * (lambda + 2.0*mu)) * n
	};

	const double r8[9] = {
			2.0 * mu * n * rho * pow2(n) * n,
			-2.0 * mu * n * rho * pow2(n) * n,
			0,
			mu * rho * n * (2.0*pow2(n) + pow2(n) - 1) * n,
			-pow2(n) * mu * rho * pow2(n),
			mu * n * pow2(n) * rho * n,
			-n * n * n * sqrt(rho*mu),
			pow2(n) * n * sqrt(rho*mu),
			0
	};

	const Eigen::Vector3d k = 2.0 * M_PI / 100 * Eigen::Vector3d(1, 1, 1);

	// Check cells
	bool failed = false;

	for (unsigned int i = 0; i < numCells; i++) {
		const Eigen::Vector3d a = Eigen::Vector3d(&vertices[cells[i*4] * 3]);
		const Eigen::Vector3d b = Eigen::Vector3d(&vertices[cells[i*4 + 1] * 3]);
		const Eigen::Vector3d c = Eigen::Vector3d(&vertices[cells[i*4 + 2] * 3]);
		const Eigen::Vector3d d = Eigen::Vector3d(&vertices[cells[i*4 + 3] * 3]);

		seissol::refinement::Tetrahedron<double> tet(a, b, c, d);
		const Eigen::Vector3d center = tet.center();

		const double kx = k.dot(center);

		for (unsigned int j = 0; j < 9; j++) {
			const double value = r1[j]*sin(kx) + r8[j]*sin(kx);

			if (fabs(var[j][i] - value) > maxDiff) {
				failed = true;
				if (verbose)
					std::cout << var[j][i] << ' ' << value << ' ' << fabs(var[j][i] - value) << ' ';
			} else {
				if (verbose)
					std::cout << "ok" << ' ';
			}
		}

		if (verbose)
			std::cout << std::endl;

		if (failed && !verbose)
			// We can stop in this case
			break;
	}

	// Cleanup
	for (unsigned int i = 0; i < 9; i++) {
		delete [] var[i];
	}

	delete [] vertices;
	delete [] cells;

	return failed ? 1 : 0;
}


//=======================================================================
// Copyright (c) 2013 Piotr Wygocki
//
// 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)
//=======================================================================
/**
 * @file facility_location_swap.hpp
* @brief
 * @author Piotr Wygocki
 * @version 1.0
 * @date 2013-07-08
 */
#ifndef PAAL_FACILITY_LOCATION_SWAP_HPP
#define PAAL_FACILITY_LOCATION_SWAP_HPP

#include "paal/data_structures/facility_location/facility_location_solution_traits.hpp"
#include "paal/utils/type_functions.hpp"
#include "paal/data_structures/combine_iterator.hpp"

#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/iterator/transform_iterator.hpp>

#include <cassert>
#include <vector>
#include <numeric>
#include <cstdlib>

namespace paal {
namespace local_search {

/**
 * @brief swap type
 *
 * @tparam T
 */
template <typename T> class Swap {
  public:
    /**
     * @brief constructor
     *
     * @param from
     * @param to
     */
    Swap(T from, T to) : m_from(from), m_to(to) {}

    Swap() = default;

    /**
     * @brief from getter
     *
     * @return
     */
    T get_from() const { return m_from; }

    /**
     * @brief to getter
     *
     * @return
     */
    T get_to() const { return m_to; }

    /**
     * @brief form setter
     *
     * @param from
     */
    void set_from(T from) { m_from = from; }

    /**
     * @brief from setter
     *
     * @param to
     */
    void set_to(T to) { m_to = to; }

  private:
    T m_from;
    T m_to;
};

/// operator() creates Swap  from (from, to)
struct make_swap {
    /// operator()
    template <typename T> Swap<T> operator()(T from, T to) const {
        return Swap<T>(from, to);
    }
};

/**
 * @brief gain functor for swap in facility location problem.
 *
 * @tparam VertexType
 */
struct facility_location_gain_swap {
    /**
     * @brief operator()
     *
     * @tparam Solution
     * @param sol
     * @param s
     *
     * @return
     */
    template <class Solution, class VertexType>
    auto operator()(Solution &sol, const Swap<VertexType> &s) const {

        auto ret = sol.add_facility_tentative(s.get_to());
        ret += sol.remove_facility_tentative(s.get_from());
        auto back = sol.add_facility_tentative(s.get_from());
        back += sol.remove_facility_tentative(s.get_to());
        assert(ret == -back);
        return -ret;
    }
};

/**
 * @brief commit functor for facility location problem
 */
struct facility_location_commit_swap {
    /**
     * @brief operator()
     *
     * @tparam Solution
     * @param sol
     * @param s
     */
    template <typename Solution, typename VertexType>
    bool operator()(Solution &sol, const Swap<VertexType> &s) const {
        sol.add_facility(s.get_to());
        sol.remove_facility(s.get_from());
        return true;
    }
};

/**
 * @brief get moves functor for facility location problem
 */
struct facility_locationget_moves_swap {

    /// operator()
    template <typename Solution>
    auto operator()(const Solution &s) const  {
        auto begin = data_structures::make_combine_iterator(
            make_swap{}, s.getChosenCopy(), s.getUnchosenCopy());
        decltype(begin) end;
        return boost::make_iterator_range(begin, end);
    }
};

} // local_search
} // paal

#endif // PAAL_FACILITY_LOCATION_SWAP_HPP


// #include <__config>                    // for std
// #include <__hash_table>                // for __hash_const_iterator, operator!=
#include <array>                           // for array
#include <boost/container/pmr/vector.hpp>  // for vector
#include <boost/container/vector.hpp>      // for operator!=, vec_iterator
#include <ckpttn/FMBiGainCalc.hpp>         // for FMBiGainCalc, part, net
#include <ckpttn/FMPmrConfig.hpp>          // for FM_MAX_DEGREE
#include <cstddef>                         // for size_t
#include <cstdint>                         // for uint8_t
#include <gsl/span>                        // for span
#include <initializer_list>                // for initializer_list
#include <vector>                          // for vector

#include "ckpttn/moveinfo.hpp"  // for MoveInfo
// #include <range/v3/view/remove_if.hpp>
// #include <transrangers.hpp>
// #include <range/v3/view/all.hpp>

using namespace std;

/**
 * @brief
 *
 * @param[in] net
 * @param[in] part
 */
template <typename Gnl>
void FMBiGainCalc<Gnl>::_init_gain(const typename Gnl::node_t& net, gsl::span<const uint8_t> part) {
    const auto degree = this->H.G.degree(net);
    if (degree < 2 || degree > FM_MAX_DEGREE)  // [[unlikely]]
    {
        return;  // does not provide any gain when moving
    }
    if (!special_handle_2pin_nets) {
        this->_init_gain_general_net(net, part);
        return;
    }
    switch (degree) {
        case 2:
            this->_init_gain_2pin_net(net, part);
            break;
        case 3:
            this->_init_gain_3pin_net(net, part);
            break;
        default:
            this->_init_gain_general_net(net, part);
    }
}

/**
 * @brief
 *
 * @param[in] net
 * @param[in] part
 */
template <typename Gnl> void FMBiGainCalc<Gnl>::_init_gain_2pin_net(const typename Gnl::node_t& net,
                                                                    gsl::span<const uint8_t> part) {
    auto netCur = this->H.G[net].begin();
    const auto w = *netCur;
    const auto v = *++netCur;

    const auto weight = this->H.get_net_weight(net);
    if (part[w] != part[v]) {
        this->totalcost += weight;
        // this->_modify_gain_va(weight, w, v);
        this->_modify_gain(w, weight);
        this->_modify_gain(v, weight);
    } else {
        // this->_modify_gain_va(-weight, w, v);
        this->_modify_gain(w, -weight);
        this->_modify_gain(v, -weight);
    }
}

/**
 * @brief
 *
 * @param[in] net
 * @param[in] part
 */
template <typename Gnl> void FMBiGainCalc<Gnl>::_init_gain_3pin_net(const typename Gnl::node_t& net,
                                                                    gsl::span<const uint8_t> part) {
    auto netCur = this->H.G[net].begin();
    const auto w = *netCur;
    const auto v = *++netCur;
    const auto u = *++netCur;

    const auto weight = this->H.get_net_weight(net);
    if (part[u] == part[v]) {
        if (part[w] == part[v]) {
            // this->_modify_gain_va(-weight, u, v, w);
            this->_modify_gain(u, -weight);
            this->_modify_gain(v, -weight);
            this->_modify_gain(w, -weight);
            return;
        }
        // this->_modify_gain_va(weight, w);
        this->_modify_gain(w, weight);
    } else if (part[w] == part[v]) {
        this->_modify_gain(u, weight);
    } else {
        this->_modify_gain(v, weight);
    }
    this->totalcost += weight;
}

/**
 * @brief
 *
 * @param[in] net
 * @param[in] part
 */
template <typename Gnl>
void FMBiGainCalc<Gnl>::_init_gain_general_net(const typename Gnl::node_t& net,
                                               gsl::span<const uint8_t> part) {
    auto num = array<size_t, 2>{0U, 0U};
    for (const auto& w : this->H.G[net]) {
        num[part[w]] += 1;
    }
    const auto weight = this->H.get_net_weight(net);

    // #pragma unroll
    for (const auto& k : {0U, 1U}) {
        if (num[k] == 0) {
            for (const auto& w : this->H.G[net]) {
                this->_modify_gain(w, -weight);
            }
        } else if (num[k] == 1) {
            for (const auto& w : this->H.G[net]) {
                if (part[w] == k) {
                    this->_modify_gain(w, weight);
                    break;
                }
            }
        }
    }

    if (num[0] > 0 && num[1] > 0) {
        this->totalcost += weight;
    }
}

/**
 * @brief
 *
 * @param[in] part
 * @param[in] move_info
 * @param[out] w
 * @return int
 */
template <typename Gnl>
auto FMBiGainCalc<Gnl>::update_move_2pin_net(gsl::span<const uint8_t> part,
                                             const MoveInfo<typename Gnl::node_t>& move_info) ->
    typename Gnl::node_t {
    auto netCur = this->H.G[move_info.net].begin();
    auto w = (*netCur != move_info.v) ? *netCur : *++netCur;
    const auto weight = this->H.get_net_weight(move_info.net);
    const int delta = (part[w] == move_info.fromPart) ? weight : -weight;
    this->deltaGainW = 2 * delta;
    return w;
}

/**
 * @brief
 *
 * @param[in] part
 * @param[in] move_info
 * @return ret_info
 */
template <typename Gnl>
void FMBiGainCalc<Gnl>::init_IdVec(const typename Gnl::node_t& v, const typename Gnl::node_t& net) {
    // auto rng = this->H.G[net] |
    //         ranges::views::remove_if([&](auto w) { return w == v; });
    // using namespace transrangers;
    // auto rng = filter([&](const auto& w) { return w != v; }, all(this->H.G[net]));
    // this->IdVec = FMPmr::vector<typename Gnl::node_t>(rng.begin(), rng.end(), &this->rsrc);

    this->IdVec.clear();
    auto&& rng = this->H.G[net];
    this->IdVec.reserve(rng.size() - 1);
    for (const auto& w : rng) {
        if (w == v) {
            continue;
        }
        this->IdVec.push_back(w);
    }
}

/**
 * @brief
 *
 * @param[in] part
 * @param[in] move_info
 * @return ret_info
 */
template <typename Gnl>
auto FMBiGainCalc<Gnl>::update_move_3pin_net(gsl::span<const uint8_t> part,
                                             const MoveInfo<typename Gnl::node_t>& move_info)
    -> vector<int> {
    // const auto& [net, v, fromPart, _] = move_info;
    auto num = array<size_t, 2>{0U, 0U};
    for (const auto& w : this->IdVec) {
        num[part[w]] += 1;
    }
    // for (const auto& w : this->H.G[move_info.net])
    // {
    //     if (w == move_info.v)
    //     {
    //         continue;
    //     }
    //     num[part[w]] += 1;
    //     IdVec.push_back(w);
    // }
    auto deltaGain = vector<int>{0, 0};
    auto weight = this->H.get_net_weight(move_info.net);
    const auto part_w = part[this->IdVec[0]];

    if (part_w != move_info.fromPart) {
        weight = -weight;
    }
    if (part_w == part[this->IdVec[1]]) {
        deltaGain[0] += weight;
        deltaGain[1] += weight;
    } else {
        deltaGain[0] += weight;
        deltaGain[1] -= weight;
    }
    return deltaGain;
}

/**
 * @brief
 *
 * @param[in] part
 * @param[in] move_info
 * @return ret_info
 */
template <typename Gnl>
auto FMBiGainCalc<Gnl>::update_move_general_net(gsl::span<const uint8_t> part,
                                                const MoveInfo<typename Gnl::node_t>& move_info)
    -> vector<int> {
    // const auto& [net, v, fromPart, toPart] = move_info;
    auto num = array<uint8_t, 2>{0, 0};
    // auto IdVec = vector<typename Gnl::node_t> {};
    for (const auto& w : this->IdVec) {
        num[part[w]] += 1;
    }

    // for (const auto& w : this->H.G[move_info.net])
    // {
    //     if (w == move_info.v)
    //     {
    //         continue;
    //     }
    //     num[part[w]] += 1;
    //     IdVec.push_back(w);
    // }
    const auto degree = this->IdVec.size();
    auto deltaGain = vector<int>(degree, 0);
    auto weight = this->H.get_net_weight(move_info.net);

    // #pragma unroll
    for (const auto& l : {move_info.fromPart, move_info.toPart}) {
        if (num[l] == 0) {
            for (size_t index = 0U; index != degree; ++index) {
                deltaGain[index] -= weight;
            }
        } else if (num[l] == 1) {
            for (size_t index = 0U; index != degree; ++index) {
                auto part_w = part[this->IdVec[index]];
                if (part_w == l) {
                    deltaGain[index] += weight;
                    break;
                }
            }
        }
        weight = -weight;
    }
    return deltaGain;
}

// instantiation

#include <py2cpp/set.hpp>              // for set
#include <xnetwork/classes/graph.hpp>  // for Graph

#include "ckpttn/netlist.hpp"  // for Netlist, SimpleNetlist

template class FMBiGainCalc<SimpleNetlist>;


// Copyright 2006. Peter Gottschling, Matthias Troyer, Rolf Bonderer
// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef LA_CONCEPTS_INCLUDE
#define LA_CONCEPTS_INCLUDE

#include <boost/config/concept_macros.hpp>

#ifdef __GXX_CONCEPTS__
#  include <concepts>
#else
#  ifdef LA_SHOW_WARNINGS
#    warning "Concepts are not used"
#  endif
#endif

#include <boost/numeric/linear_algebra/identity.hpp>
#include <boost/numeric/linear_algebra/is_invertible.hpp>
#include <boost/numeric/linear_algebra/inverse.hpp>
#include <boost/numeric/linear_algebra/operators.hpp>
#include <boost/numeric/linear_algebra/algebraic_concepts.hpp>
#include <complex>

// If desired one can disable the default concept maps with LA_NO_CONCEPT_MAPS

// We consider to change the namespace from math to numeric
// More precisely, the concepts may be moved into namespace numeric and the standard functions stay in math

/// Namespace for mathematical concepts
/** In contrast to the ones in algebra the concepts can require
    basic implementation concepts like std::CopyAssignable */
namespace math {

#ifdef __GXX_CONCEPTS__

// ==================================
// Classification of Arithmetic Types
// ==================================

// We omit this now because it became part of the standard concepts

#if 0

// In addtion to std::Integral
concept Float<typename T>
  : std::DefaultConstructible<T>, std::CopyConstructible<T>,
    std::LessThanComparable<T>, std::EqualityComparable<T>
{
  T operator+(T);
  T operator+(T, T);
  T& operator+=(T&, T);
  T operator-(T, T);
  T operator-(T);
  T& operator-=(T&, T);
  T operator*(T, T);
  T& operator*=(T&, T);
  T operator/(T, T);
  T& operator/=(T&, T);

  requires std::CopyAssignable<T>
        && std::SameType<std::CopyAssignable<T>::result_type, T&>;
}

concept_map Float<float> {}
concept_map Float<double> {}
concept_map Float<long double> {}

// The difference to Float is the lack of LessThanComparable
concept Complex<typename T>
  : std::DefaultConstructible<T>, std::CopyConstructible<T>,
    std::EqualityComparable<T>
{
  T operator+(T);
  T operator+(T, T);
  T& operator+=(T&, T);
  T operator-(T, T);
  T operator-(T);
  T& operator-=(T&, T);
  T operator*(T, T);
  T& operator*=(T&, T);
  T operator/(T, T);
  T& operator/=(T&, T);

  requires std::CopyAssignable<T>
        && std::SameType<std::CopyAssignable<T>::result_type, T&>;
}

template <typename T>
  requires Float<T>
concept_map Complex<std::complex<T> > {}

// TBD: Concept Arithmetic is useless like this, it should have operations and then be the base for
// Integral, Float and Complex
concept Arithmetic<typename T> {}

template <typename T>
  requires std::Integral<T>
concept_map Arithmetic<T> {}

template <typename T>
  requires Float<T>
concept_map Arithmetic<T> {}

template <typename T>
  requires Arithmetic<T>
concept_map Arithmetic< std::complex<T> > {}

#endif

// The following concepts are used to classify intrinsic arithmetic types.
// The standard concepts already define the syntactic requirements,
// i.e. the interface.
// However they sey nothing about the semantics.
// Therefore, user-defined types can model the syntactic/interface
// requirements while still having a different mathematical behavior.
// For that reason, we introduce concepts that are only used for intrinsic types.
// For them we can define concept_maps regarding semantic behavior as monoids.

concept IntrinsicSignedIntegral<typename T>
  : std::SignedIntegralLike<T>
{}

concept IntrinsicUnsignedIntegral<typename T>
  : std::UnsignedIntegralLike<T>
{}

concept IntrinsicFloatingPoint<typename T>
  : std::FloatingPointLike<T>
{}

// Intrinsic types are chategorized:

concept_map IntrinsicSignedIntegral<char> {}
concept_map IntrinsicSignedIntegral<signed char> {}
concept_map IntrinsicUnsignedIntegral<unsigned char> {}
concept_map IntrinsicSignedIntegral<short> {}
concept_map IntrinsicUnsignedIntegral<unsigned short> {}
concept_map IntrinsicSignedIntegral<int> {}
concept_map IntrinsicUnsignedIntegral<unsigned int> {}
concept_map IntrinsicSignedIntegral<long> {}
concept_map IntrinsicUnsignedIntegral<unsigned long> {}
concept_map IntrinsicSignedIntegral<long long> {}
concept_map IntrinsicUnsignedIntegral<unsigned long long> {}

concept_map IntrinsicFloatingPoint<float> {}
concept_map IntrinsicFloatingPoint<double> {}

// ================
// Utility Concepts
// ================

// Concepts for functions mapping to same type or convertible
auto concept UnaryIsoFunction<typename Operation, typename Element>
{
    requires std::Callable1<Operation, Element>;
    requires std::Convertible<std::Callable1<Operation, Element>::result_type, Element>;

    typename result_type = std::Callable1<Operation, Element>::result_type;
};

auto concept BinaryIsoFunction<typename Operation, typename Element>
{
    requires std::Callable2<Operation, Element, Element>;
    requires std::Convertible<std::Callable2<Operation, Element, Element>::result_type, Element>;

    typename result_type = std::Callable2<Operation, Element, Element>::result_type;
};

#if 0
auto concept CompatibleBinaryFunction<typename A1, typename A2, typename Result>
{
    typename result_type;
    result_type F(A1, A2);
    requires std::Convertible<result_type, Result>;
}
#endif

// ==================
// Algebraic Concepts
// ==================

auto concept Magma<typename Operation, typename Element>
    : BinaryIsoFunction<Operation, Element>
{
    requires std::CopyAssignable<Element>
	  && std::CopyAssignable<Element, BinaryIsoFunction<Operation, Element>::result_type>;
};

// For algebraic structures that are commutative but not associative
// As an example floating point numbers are commutative but not associative
//   w.r.t. addition and multiplication
auto concept CommutativeMagma<typename Operation, typename Element>
  : Magma<Operation, Element>,
    algebra::Commutative<Operation, Element>
{};

// SemiGroup is a refinement which must be nominal
auto concept SemiGroup<typename Operation, typename Element>
  : Magma<Operation, Element>,
    algebra::SemiGroup<Operation, Element>
{};

auto concept CommutativeSemiGroup<typename Operation, typename Element>
  : SemiGroup<Operation, Element>,
    CommutativeMagma<Operation, Element>
{};

// Adding identity
// auto
concept Monoid<typename Operation, typename Element>
  : SemiGroup<Operation, Element>,
    algebra::Monoid<Operation, Element>
{
    requires std::Convertible<identity_result_type, Element>;
};

auto concept CommutativeMonoid<typename Operation, typename Element>
  : CommutativeSemiGroup<Operation, Element>,
    Monoid<Operation, Element>
{};

concept PartiallyInvertibleMonoid<typename Operation, typename Element>
  : Monoid<Operation, Element>,
    algebra::Inversion<Operation, Element>
{
    typename is_invertible_result_type;
    is_invertible_result_type is_invertible(Operation, Element);
    requires std::Convertible<is_invertible_result_type, bool>;

    requires std::Convertible<inverse_result_type, Element>;

    // Does it overwrites the axiom from algebra::Inversion
    axiom Inversivity(Operation op, Element x)
    {
	// Only for invertible elements:
	if (is_invertible(op, x))
	    op( x, inverse(op, x) ) == identity(op, x);
	if ( is_invertible(op, x) )
	    op( inverse(op, x), x ) == identity(op, x);
    }
};

auto concept PartiallyInvertibleCommutativeMonoid<typename Operation, typename Element>
  : PartiallyInvertibleMonoid<Operation, Element>,
    CommutativeMonoid<Operation, Element>
{};

concept Group<typename Operation, typename Element>
  : PartiallyInvertibleMonoid<Operation, Element>,
    algebra::Group<Operation, Element>
{
    axiom AlwaysInvertible(Operation op, Element x)
    {
	is_invertible(op, x);
    }

    axiom GlobalInversivity(Operation op, Element x)
    {
	// In fact this is implied by AlwaysInvertible and inherited Inversion axiom
	// However, we don't rely on the compiler to deduce this
	op( x, inverse(op, x) ) == identity(op, x);
	op( inverse(op, x), x ) == identity(op, x);
    }
};

auto concept AbelianGroup<typename Operation, typename Element>
  : Group<Operation, Element>,
    PartiallyInvertibleCommutativeMonoid<Operation, Element>,
    algebra::AbelianGroup<Operation, Element>
{};

// ========================
// Additive scalar concepts
// ========================

concept AdditiveMagma<typename Element>
  : Magma< math::add<Element>, Element >
{
    typename plus_assign_result_type;
    plus_assign_result_type operator+=(Element& x, Element y);
    // requires std::Convertible<plus_assign_result_type, Element>;

    // Operator + is by default defined with +=
    typename addition_result_type;
    addition_result_type operator+(Element x, Element y);
#if 0
    {
	Element tmp(x);
	return tmp += y;                      defaults NYS
    }
#endif
    requires std::Convertible<addition_result_type, Element>;

    // Type consistency with Magma
    requires std::Convertible< addition_result_type,
                            Magma< math::add<Element>, Element >::result_type>;

    // SameType requires more rigorous specializations on pure algebraic functors
    // requires std::SameType< addition_result_type,
    // 	                    Magma< math::add<Element>, Element >::result_type>;

    axiom Consistency(math::add<Element> op, Element x, Element y)
    {
	op(x, y) == x + y;

	// Consistency definition between + and += might change later
        x + y == x += y;
	// Element tmp = x; tmp+= y; tmp == x + y; not proposal-compliant
    }
}

auto concept AdditiveCommutativeMagma<typename Element>
  : AdditiveMagma<Element>,
    CommutativeMagma< math::add<Element>, Element >
{};

auto concept AdditiveSemiGroup<typename Element>
  : AdditiveMagma<Element>,
    SemiGroup< math::add<Element>, Element >
{};

// We really need only one of the additive concepts for the requirements,
// the requirements of the other would be implied.
// Vice versa, to derive concept maps of nested concepts from
// concept maps of refined concepts, they are needed all.
auto concept AdditiveCommutativeSemiGroup<typename Element>
  : AdditiveSemiGroup<Element>,
    AdditiveCommutativeMagma<Element>,
    CommutativeSemiGroup< math::add<Element>, Element >
{};

concept AdditiveMonoid<typename Element>
  : AdditiveSemiGroup<Element>,
    Monoid< math::add<Element>, Element >
{
    Element zero(Element v);

    axiom Consistency (math::add<Element> op, Element x)
    {
	zero(x) == identity(op, x);
    }
};

// We really need only one of the additive concepts for the requirements,
// the requirements of the other would be implied.
// Vice versa, to derive concept maps of nested concepts from
// concept maps of refined concepts, they are needed all.
auto concept AdditiveCommutativeMonoid<typename Element>
  : AdditiveMonoid<Element>,
    AdditiveCommutativeSemiGroup<Element>,
    CommutativeMonoid< math::add<Element>, Element >
{};

concept AdditivePartiallyInvertibleMonoid<typename Element>
  : AdditiveMonoid<Element>,
    PartiallyInvertibleMonoid< math::add<Element>, Element >
{
    typename minus_assign_result_type;
    minus_assign_result_type operator-=(Element& x, Element y);
    // requires std::Convertible<minus_assign_result_type, Element>;

    // Operator - by default defined with -=
    typename subtraction_result_type;
    subtraction_result_type operator-(Element& x, Element y);
#if 0
    {
	Element tmp(x);
	return tmp -= y;                      defaults NYS
    }
#endif
    requires std::Convertible<subtraction_result_type, Element>;

    typename unary_result_type;
    unary_result_type operator-(Element x);
#if 0
    {
	return zero(x) - x;      defaults NYS
    }
#endif
    requires std::Convertible<unary_result_type, Element>;

    axiom Consistency(math::add<Element> op, Element x, Element y)
    {
	// consistency between additive and pure algebraic concept
	if ( is_invertible(op, y) )
	    op(x, inverse(op, y)) == x - y;
	if ( is_invertible(op, y) )
	    inverse(op, y) == -y;

	// consistency between unary and binary -
	if ( is_invertible(op, x) )
	    identity(op, x) - x == -x;

	// Might change later
	if ( is_invertible(op, y) )
	    x - y == x -= y;
	// Element tmp = x; tmp-= y; tmp == x - y; not proposal-compliant
    }

};

auto concept AdditivePartiallyInvertibleCommutativeMonoid<typename Element>
  : AdditivePartiallyInvertibleMonoid<Element>,
    AdditiveCommutativeMonoid<Element>,
    PartiallyInvertibleCommutativeMonoid< math::add<Element>, Element >
{};

auto concept AdditiveGroup<typename Element>
  : AdditivePartiallyInvertibleMonoid<Element>,
    Group< math::add<Element>, Element >
{};

auto concept AdditiveAbelianGroup<typename Element>
  : AdditiveGroup<Element>,
    AdditiveCommutativeMonoid<Element>,
    AbelianGroup< math::add<Element>, Element >
{};

// ============================
// Multiplitive scalar concepts
// ============================

concept MultiplicativeMagma<typename Element>
  : Magma< math::mult<Element>, Element >
{
    typename mult_assign_result_type;
    mult_assign_result_type operator*=(Element& x, Element y);
    // requires std::Convertible<mult_assign_result_type, Element>;

    // Operator * is by default defined with *=
    typename mult_result_type;
    mult_result_type operator*(Element x, Element y);
#if 0
    {
	Element tmp(x);
	return tmp *= y;                      defaults NYS
    }
#endif
    requires std::Convertible<mult_result_type, Element>;

    // Type consistency with Magma
    requires std::Convertible< mult_result_type,
                            Magma< math::mult<Element>, Element >::result_type>;

    // SameType requires more rigorous specializations on pure algebraic functors
    // requires std::SameType< mult_result_type,
    // 	                    Magma< math::mult<Element>, Element >::result_type>;

    axiom Consistency(math::mult<Element> op, Element x, Element y)
    {
	op(x, y) == x * y;

	// Consistency definition between * and *= might change later
        x * y == x *= y;
	// Element tmp = x; tmp*= y; tmp == x * y; not proposal-compliant
    }

}

auto concept MultiplicativeSemiGroup<typename Element>
  : MultiplicativeMagma<Element>,
    SemiGroup< math::mult<Element>, Element >
{};

auto concept MultiplicativeCommutativeSemiGroup<typename Element>
  : MultiplicativeSemiGroup<Element>,
    CommutativeSemiGroup< math::mult<Element>, Element >
{};

concept MultiplicativeMonoid<typename Element>
  : MultiplicativeSemiGroup<Element>,
    Monoid< math::mult<Element>, Element >
{
    Element one(Element v);

    axiom Consistency (math::mult<Element> op, Element x)
    {
	one(x) == identity(op, x);
    }
};

auto concept MultiplicativeCommutativeMonoid<typename Element>
  : MultiplicativeMonoid<Element>,
    MultiplicativeCommutativeSemiGroup<Element>,
    CommutativeMonoid< math::mult<Element>, Element >
{};

concept MultiplicativePartiallyInvertibleMonoid<typename Element>
  : MultiplicativeMonoid<Element>,
    PartiallyInvertibleMonoid< math::mult<Element>, Element >
{
    typename divide_assign_result_type;
    divide_assign_result_type operator/=(Element& x, Element y);
    // requires std::Convertible<divide_assign_result_type, Element>;

    // Operator / by default defined with /=
    typename division_result_type = Element;
    division_result_type operator/(Element x, Element y);
#if 0
    {
	Element tmp(x);
	return tmp /= y;                      defaults NYS
    }
#endif
    requires std::Convertible<division_result_type, Element>;

    axiom Consistency(math::mult<Element> op, Element x, Element y)
    {
	// consistency between multiplicative and pure algebraic concept
	if ( is_invertible(op, y) )
	    op(x, inverse(op, y)) == x / y;

	// Consistency between / and /=, might change later
	if ( is_invertible(op, y) )
	    x / y == x /= y;
	// Element tmp = x; tmp/= y; tmp == x / y; not proposal-compliant
    }
};

auto concept MultiplicativePartiallyInvertibleCommutativeMonoid<typename Element>
  : MultiplicativePartiallyInvertibleMonoid<Element>,
    MultiplicativeCommutativeMonoid<Element>,
    PartiallyInvertibleCommutativeMonoid< math::mult<Element>, Element >
{};

auto concept MultiplicativeGroup<typename Element>
  : MultiplicativeMonoid<Element>,
    Group< math::mult<Element>, Element >
{};

auto concept MultiplicativeAbelianGroup<typename Element>
  : MultiplicativeGroup<Element>,
    MultiplicativeCommutativeMonoid<Element>,
    AbelianGroup< math::mult<Element>, Element >
{};

// ======================================
// Algebraic concepts with two connectors
// ======================================

// -----------------
// Based on functors
// -----------------

// More generic, less handy to use

auto concept GenericRing<typename AddOp, typename MultOp, typename Element>
  : AbelianGroup<AddOp, Element>,
    SemiGroup<MultOp, Element>,
    algebra::Ring<AddOp, MultOp, Element>
{};

auto concept GenericCommutativeRing<typename AddOp, typename MultOp, typename Element>
  : GenericRing<AddOp, MultOp, Element>,
    CommutativeSemiGroup<MultOp, Element>
{};

auto concept GenericRingWithIdentity<typename AddOp, typename MultOp, typename Element>
  : GenericRing<AddOp, MultOp, Element>,
    Monoid<MultOp, Element>,
    algebra::RingWithIdentity<AddOp, MultOp, Element>
{};

auto concept GenericCommutativeRingWithIdentity<typename AddOp, typename MultOp, typename Element>
  : GenericRingWithIdentity<AddOp, MultOp, Element>,
    GenericCommutativeRing<AddOp, MultOp, Element>,
    CommutativeMonoid<MultOp, Element>
{};

// auto
concept GenericDivisionRing<typename AddOp, typename MultOp, typename Element>
  : GenericRingWithIdentity<AddOp, MultOp, Element>,
    algebra::DivisionRing<AddOp, MultOp, Element>
{
    requires std::Convertible<inverse_result_type, Element>;
};

auto concept GenericField<typename AddOp, typename MultOp, typename Element>
  : GenericDivisionRing<AddOp, MultOp, Element>,
    GenericCommutativeRingWithIdentity<AddOp, MultOp, Element>,
    algebra::Field<AddOp, MultOp, Element>
{};

// ------------------
// Based on operators
// ------------------

// Handier, less generic

// Alternative definitions use MultiplicativeMonoid<Element> for Ring
// and call such concepts Pseudo-Ring

auto concept Ring<typename Element>
  : AdditiveAbelianGroup<Element>,
    MultiplicativeSemiGroup<Element>,
    GenericRing<math::add<Element>, math::mult<Element>, Element>
{};

auto concept CommutativeRing<typename Element>
  : Ring<Element>,
    MultiplicativeCommutativeSemiGroup<Element>,
    GenericCommutativeRing<math::add<Element>, math::mult<Element>, Element>
{};

auto concept RingWithIdentity<typename Element>
  : Ring<Element>,
    MultiplicativeMonoid<Element>,
    GenericRingWithIdentity<math::add<Element>, math::mult<Element>, Element>
{};

auto concept CommutativeRingWithIdentity<typename Element>
  : RingWithIdentity<Element>,
    CommutativeRing<Element>,
    MultiplicativeCommutativeMonoid<Element>,
    GenericCommutativeRingWithIdentity<math::add<Element>, math::mult<Element>, Element>
{};

concept DivisionRing<typename Element>
  : RingWithIdentity<Element>,
    MultiplicativePartiallyInvertibleMonoid<Element>,
    GenericDivisionRing<math::add<Element>, math::mult<Element>, Element>
{
    axiom NonZeroDivisibility(Element x)
    {
	if (x != zero(x))
	    x / x == one(x);
    }
};

auto concept Field<typename Element>
  : DivisionRing<Element>,
    CommutativeRingWithIdentity<Element>,
    GenericField<math::add<Element>, math::mult<Element>, Element>
{};

// ======================
// Miscellaneous concepts
// ======================

// that shall find a better place later

// EqualityComparable will have the != when defaults are supported
// At this point the following won't needed anymore
auto concept FullEqualityComparable<typename T, typename U = T>
{
  //requires std::EqualityComparable<T, U>;

    bool operator==(const T&, const U&);
    bool operator!=(const T&, const U&);
};

// Closure of EqualityComparable under a binary operation:
// That is, the result of this binary operation is also EqualityComparable
// with itself and with the operand type.
auto concept Closed2EqualityComparable<typename Operation, typename Element>
  : BinaryIsoFunction<Operation, Element>
{
    requires FullEqualityComparable<Element>;
    requires FullEqualityComparable< BinaryIsoFunction<Operation, Element>::result_type >;
    requires FullEqualityComparable< Element, BinaryIsoFunction<Operation, Element>::result_type >;
    requires FullEqualityComparable< BinaryIsoFunction<Operation, Element>::result_type, Element >;
};

// LessThanComparable will have the other operators when defaults are supported
// At this point the following won't needed anymore
auto concept FullLessThanComparable<typename T, typename U = T>
{
    bool operator<(const T&, const U&);
    bool operator<=(const T&, const U&);
    bool operator>(const T&, const U&);
    bool operator>=(const T&, const U&);
};

// Same for LessThanComparable
auto concept Closed2LessThanComparable<typename Operation, typename Element>
  : BinaryIsoFunction<Operation, Element>
{
    requires FullLessThanComparable<Element>;
    requires FullLessThanComparable< BinaryIsoFunction<Operation, Element>::result_type >;
    requires FullLessThanComparable< Element, BinaryIsoFunction<Operation, Element>::result_type >;
    requires FullLessThanComparable< BinaryIsoFunction<Operation, Element>::result_type, Element >;
};

#if 0
auto concept NumericOperatorResultConvertible<typename T>
  : AddableWithAssign<T>,
    SubtractableWithAssign<T>,
    MultiplicableWithAssign<T>,
    DivisibleWithAssign<T>
{
    requires std::Convertible< AddableWithAssign<T>::result_type, T>;
    requires std::Convertible< SubtractableWithAssign<T>::result_type, T>;
    requires std::Convertible< MultiplicableWithAssign<T>::result_type, T>;
    requires std::Convertible< DivisibleWithAssign<T>::result_type, T>;
}
#endif

auto concept AdditionResultConvertible<typename T>
{
    typename result_type;
    result_type operator+(T t, T u);
    requires std::Convertible<result_type, T>;

    typename result_type;
    result_type operator+=(T& t, T u);
    requires std::Convertible<result_type, T>;
};

auto concept SubtractionResultConvertible<typename T>
{
    typename result_type;
    result_type operator-(T t, T u);
    requires std::Convertible<result_type, T>;

    typename result_type;
    result_type operator-=(T& t, T u);
    requires std::Convertible<result_type, T>;
};

auto concept NumericOperatorResultConvertible<typename T>
  : AdditionResultConvertible<T>,
    SubtractionResultConvertible<T>
{};

// ====================
// Default Concept Maps
// ====================

#ifndef LA_NO_CONCEPT_MAPS

// ==============
// Integral Types
// ==============

template <typename T>
  requires IntrinsicSignedIntegral<T>
concept_map CommutativeRingWithIdentity<T> {}

template <typename T>
  requires IntrinsicUnsignedIntegral<T>
concept_map AdditiveCommutativeMonoid<T> {}

template <typename T>
  requires IntrinsicUnsignedIntegral<T>
concept_map MultiplicativeCommutativeMonoid<T> {}

// ====================
// Floating Point Types
// ====================

template <typename T>
  requires IntrinsicFloatingPoint<T>
concept_map Field<T> {}

template <typename T>
  requires IntrinsicFloatingPoint<T>
concept_map Field< std::complex<T> > {}

// ===========
// Min and Max
// ===========

// Draft version: defined generously unless there will be problems with some types

template <typename Element>
concept_map CommutativeMonoid< max<Element>, Element >
{
    // Why do we need this?
    typedef Element identity_result_type;
}

template <typename Element>
concept_map CommutativeMonoid< min<Element>, Element >
{
    // Why do we need this?
    typedef Element identity_result_type;
}

#endif // LA_NO_CONCEPT_MAPS

// Here come some mathematical concepts to be defined later

/// Specify the semantic Behavior of natural numbers (TBD)
/** Mathematic properties are in most cases only approximated
    but not held exactly. Rigorous definition would impede
    usage of real processors. **/
concept NaturalNumber<typename T> {}

/// Specify the semantic Behavior of integral numbers (TBD)
/** Mathematic properties are in most cases only approximated
    but not held exactly. Rigorous definition would impede
    usage of real processors.
    It is arguable if this is really a refinement **/
concept IntegralNumber<typename T> : NaturalNumber<T> {}

/// Specify the semantic Behavior of complex numbers (TBD)
/** Mathematic properties are in most cases only approximated
    but not held exactly. Rigorous definition would impede
    usage of real processors. **/
concept ComplexNumber<typename T> {}

/// Specify the semantic Behavior of real numbers (TBD)
/** Mathematic properties are in most cases only approximated
    but not held exactly. Rigorous definition would impede
    usage of real processors. **/
concept RealNumber<typename T> : ComplexNumber<T> {}

#endif // __GXX_CONCEPTS__

// =================================================
// Concept to specify return type of abs (and norms)
// =================================================

#ifdef __GXX_CONCEPTS__

// Concept to specify to specify projection of scalar value to comparable type
// For instance as return type of abs
// Minimalist definition for maximal applicability
auto concept Magnitude<typename T>
{
    typename type = T;
};

template <typename T>
concept_map Magnitude<std::complex<T> >
{
    typedef T type;
}

// Concept for norms etc., which are real values in mathematical definitions
auto concept RealMagnitude<typename T>
  : Magnitude<T>
{
    requires FullEqualityComparable<type>;
    requires FullLessThanComparable<type>;

    requires Field<type>;

    type sqrt(type);
    // typename sqrt_result;
    // sqrt_result sqrt(type);
    // requires std::Convertible<sqrt_result, type>;

    // using std::abs;
    type abs(T);
}

#else  // now without concepts

template <typename T>
struct Magnitude
{
    typename type = T;
};

template <typename T>
struct Magnitude<std::complex<T> >
{
    typedef T type;
}

template <typename T> struct RealMagnitude
  : public Magnitude<T>
{}

#endif  // __GXX_CONCEPTS__

// Type trait version both available with and w/o concepts (TBD: Macro finally :-( )
// For the moment everything is its own magnitude type, unless stated otherwise
template <typename T>
struct magnitude_type_trait
{
    typedef T type;
};

template <typename T>
struct magnitude_type_trait< std::complex<T> >
{
    typedef T type;
};

// =========================================
// Concepts for convenience (many from Rolf)
// =========================================

#ifdef __GXX_CONCEPTS__

//The following concepts Addable, Subtractable etc. differ from std::Addable, std::Subtractable
//etc. in so far that no default for result_type is provided, thus allowing automated return type deduction

auto concept Addable<typename T, typename U = T>
{
    typename result_type;
    result_type operator+(const T& t, const U& u);
};

// Usually + and += are both defined
// + can be efficiently derived from += but not vice versa
auto concept AddableWithAssign<typename T, typename U = T>
{
    typename assign_result_type;
    assign_result_type operator+=(T& x, U y);

    // Operator + is by default defined with +=
    typename result_type;
    result_type operator+(T x, U y);
#if 0
    {
	// Default requires std::CopyConstructible, without default not needed
	Element tmp(x);
	return tmp += y;                      defaults NYS
    }
#endif
};

auto concept Subtractable<typename T, typename U = T>
{
    typename result_type;
    result_type operator-(const T& t, const U& u);
};

// Usually - and -= are both defined
// - can be efficiently derived from -= but not vice versa
auto concept SubtractableWithAssign<typename T, typename U = T>
{
    typename assign_result_type;
    assign_result_type operator-=(T& x, U y);

    // Operator - is by default defined with -=
    typename result_type;
    result_type operator-(T x, U y);
#if 0
    {
	// Default requires std::CopyConstructible, without default not needed
	Element tmp(x);
	return tmp -= y;                      defaults NYS
    }
#endif
};

auto concept Multiplicable<typename T, typename U = T>
{
    typename result_type;
    result_type operator*(const T& t, const U& u);
};

// Usually * and *= are both defined
// * can be efficiently derived from *= but not vice versa
auto concept MultiplicableWithAssign<typename T, typename U = T>
{
    typename assign_result_type;
    assign_result_type operator*=(T& x, U y);

    // Operator * is by default defined with *=
    typename result_type;
    result_type operator*(T x, U y);
#if 0
    {
	// Default requires std::CopyConstructible, without default not needed
	Element tmp(x);
	return tmp *= y;                      defaults NYS
    }
#endif
};

auto concept Divisible<typename T, typename U = T>
{
    typename result_type;
    result_type operator / (const T&, const U&);
};

// Usually * and *= are both defined
// * can be efficiently derived from *= but not vice versa
auto concept DivisibleWithAssign<typename T, typename U = T>
{
    typename assign_result_type;
    assign_result_type operator*=(T& x, U y);

    // Operator * is by default defined with *=
    typename result_type;
    result_type operator*(T x, U y);
#if 0
    {
	// Default requires std::CopyConstructible, without default not needed
	Element tmp(x);
	return tmp *= y;                      defaults NYS
    }
#endif
};

auto concept Transposable<typename T>
{
    typename result_type;
    result_type trans(T&);
};

// Unary Negation -> Any suggestions for better names?! Is there a word as "negatable"?!
auto concept Negatable<typename S>
{
    typename result_type = S;
    result_type operator-(const S&);
};

// Or HasAbs?
using std::abs;
auto concept AbsApplicable<typename S>
{
    // There are better ways to define abs than the way it is done in std
    // Likely we replace the using one day
    typename result_type;
    result_type abs(const S&);
};

using std::conj;
auto concept HasConjugate<typename S>
{
    typename result_type;
    result_type conj(const S&);
};

// We need the following; might be placed somewhere else later
template <Float T>
concept_map HasConjugate<T>
{
    typedef T result_type;
    result_type conj(const T& s) {return s;}
}

// Dot product to be defined:
auto concept Dottable<typename T, typename U = T>
{
    typename result_type = T;
    result_type dot(const T&t, const U& u);
};

auto concept OneNormApplicable<typename V>
{
    typename result_type;
    result_type one_norm(const V&);
};

auto concept TwoNormApplicable<typename V>
{
    typename result_type;
    result_type two_norm(const V&);
};

auto concept InfinityNormApplicable<typename V>
{
    typename result_type;
    result_type inf_norm(const V&);
};

#endif  // __GXX_CONCEPTS__

} // namespace math

#endif // LA_CONCEPTS_INCLUDE


#ifndef IMAGE_REPROJECTION_SURFACE_MODEL_HPP
#define IMAGE_REPROJECTION_SURFACE_MODEL_HPP

#include <string>

#include <nodelet/nodelet.h>
#include <topic_tools/shape_shifter.h>

#include <boost/shared_ptr.hpp>

#include <opencv2/core/core.hpp>

namespace image_reprojection {

/*
  base class of surface models which calculate intersections vs 3D rays.
  child class may throw std::exception from its overrode functions.
  also, child class must be threadsafe.
*/
class SurfaceModel : public nodelet::Nodelet {
public:
  SurfaceModel() {}

  virtual ~SurfaceModel() {}

  /*
    calculate intersection between 3D ray in surface coordinate and surface.
    input ray directions have not to be normalized.
    mask is input and output; as input it should indicate valid input rays.
    as output it should indicate rays which intersects the surface.
  */
  void intersection(const cv::Vec3f &src_origin, const cv::Mat &src_direction, cv::Mat &dst,
                    cv::Mat &mask) const {
    /*
      required conditions
    */

    // source points must be an array of 3D points
    CV_Assert(src_direction.type() == CV_32FC3);
    // mask must be an array of bytes
    CV_Assert(mask.type() == CV_8UC1);
    // sizes of source points and mask must be same
    CV_Assert(src_direction.size() == mask.size());

    /*
      intersection calculation
    */

    onIntersection(src_origin, src_direction, dst, mask);

    /*
      ensured conditions
    */

    // intersection points must be an array of 3D points
    CV_Assert(dst.type() == CV_32FC3);
    // mask must be an array of bytes
    CV_Assert(mask.type() == CV_8UC1);
    // sizes of source, intersection points and mask must be same
    CV_Assert(src_direction.size() == dst.size());
    CV_Assert(dst.size() == mask.size());
  }

  /*
     checks 3D rays from ray origin and surface intersect at expected points within errors.
     ray origin and expected intersection points are assumed to be described in surface coordinate.
     mask is input and output; as input it should indicate valid input intersections.
     as output it should indicate intersections which satisfy error conditions.
   */
  void intersectsAt(const cv::Vec3f &src_origin, const cv::Mat &expected, cv::Mat &mask,
                    const double abs_error) const {
    /*
      required conditions
    */

    // allowable error must be zero or positive
    CV_Assert(abs_error >= 0.);

    /*
      calculation of actual intersection
    */

    cv::Mat actual;
    intersection(src_origin, expected - cv::Mat(expected.size(), CV_32FC3).setTo(src_origin),
                 actual, mask);

    /*
      update mask according to error on intersection points
     */

    mask.forEach<uchar>([actual, expected, abs_error](uchar &m, const int *const pos) {
      if (m != 0) {
        const cv::Vec3f &a = *actual.ptr<cv::Vec3f>(pos[0], pos[1]);
        const cv::Vec3f &e = *expected.ptr<cv::Vec3f>(pos[0], pos[1]);
        m = (cv::norm(a, e) <= abs_error) ? 1 : 0;
      }
    });
  }

  /*
    update this model
  */
  virtual void update(const topic_tools::ShapeShifter &surface) = 0;

  /*
    return surface frame id
  */
  virtual std::string getFrameId() const = 0;

private:
  /*
    following vitual functions must be implemented in a child class
  */
  virtual void onInit() = 0; // from nodelet::Nodelet

  virtual void onIntersection(const cv::Vec3f &src_origin, const cv::Mat &src_direction,
                              cv::Mat &dst, cv::Mat &mask) const = 0;
};

typedef boost::shared_ptr<SurfaceModel> SurfaceModelPtr;
typedef boost::shared_ptr<const SurfaceModel> SurfaceModelConstPtr;

} // namespace image_reprojection

#endif /* IMAGE_REPROJECTION_SURFACE_MODEL_HPP */


/*
 *  \file GLHelpers.cpp
 *
 * $Id$
 */

#include <SceneGraph/GLHelpers.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <vector>

#undef Success
#include <Eigen/Core>
#include <Eigen/LU>

namespace SceneGraph
{

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///  If a GL error has occured, this function outputs "msg" and the
//   programme exits. To avoid exiting @see WarnForGLErrors.
void _CheckForGLErrors( const char *sFile, const int nLine )
{
    GLenum glError = glGetError();
    if( glError != GL_NO_ERROR ) {
        fprintf( stderr, "ERROR: %s -- %s line %d\n",
                (char*)gluErrorString(glError), sFile, nLine );
        //FIXME: On my box, glError is not totally free of error.
        // there's probably something wrong with one of the glEnable(...) in GLWindow.cpp
        // so I temporarily comment out the exit() call here.
//        exit( -1 );
    }
}

//////////////////////////////////////////////////////////////////////////////
// Extract current camera pose from opengl in the Robotics Coordinate frame convention
Eigen::Matrix4d GLGetCameraPose()
{
    Eigen::Matrix<double,4,4,Eigen::ColMajor> M; // for opengl
    glGetDoublev( GL_MODELVIEW_MATRIX, M.data() );

    Eigen::Matrix3d gl2v;
    gl2v << 0,0,-1,  1,0,0,  0,-1,0;

    //    Eigen::Vector3d xyz = -M.block<3,3>(0,0).transpose()*M.block<3,1>(0,3);
    //    Vector3d pqr = R2Cart( gl2v*M.block<3,3>(0,0) );

    Eigen::Matrix4d T;

    T.block<3,3>(0,0) = gl2v*M.block<3,3>(0,0);
    T.block<3,1>(0,3) = -M.block<3,3>(0,0).transpose()*M.block<3,1>(0,3);
    T.block<1,4>(3,0) << 0, 0, 0, 1;

    return T;
}

///////////////////////////////////////////////////////////////////////////////
/// Convert opengl projection matrix into computer vision K matrix
Eigen::Matrix3d GLGetProjectionMatrix()
{
    Eigen::Matrix<double,4,4,Eigen::ColMajor> P; // for opengl
    glGetDoublev( GL_PROJECTION_MATRIX, P.data() );
    GLint vViewport[4];
    glGetIntegerv( GL_VIEWPORT, vViewport );

    //    fovy = 2*atan( h/2/f );
    //    f = tan(fovy/2)/(h/2)

    Eigen::Matrix3d K;
    K(0,0) = P(0,0); // P00 = 1/tan(fov/2) ==> fx = P00
    K(0,1) = 0.0; // sx
    K(0,2) = vViewport[2]/2.0; // cx
    K(1,0) = 0.0;
    K(1,1) = P(1,1); // fy
    K(1,2) = vViewport[3]/2.0; // cy
    K(2,0) = 0.0;
    K(2,1) = 0.0;
    K(2,2) = 1.0;

    //    cout << P(0,0) * vViewport[2]/(double)vViewport[3] << endl;
    //    cout << "K:\n" << K <<  endl << endl;

    return K;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// If a GL error has occured, this function outputs "msg" and  automatically sets
//  the gl error state back to normal.
void WarnForGLErrors( const char * msg )
{
    GLenum glError = (GLenum)glGetError();
    if( glError != GL_NO_ERROR ) {
        if( msg ) {
            fprintf( stderr, "WARNING: %s\n", msg );
        }
        fprintf( stderr, "ERROR: %s\n", (char *) gluErrorString(glError) );
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// Given a particular OpenGL Format, return the appropriate number of image channels.
unsigned int NumChannels( unsigned int nFormat )
{
    switch( nFormat ){
        case GL_LUMINANCE:
        case GL_RED:
        case GL_BLUE:
        case GL_GREEN:
        case GL_ALPHA:
            return 1;
        case GL_RGB:
            return 3;
        case GL_RGBA:
            return 4;
        default:
            fprintf( stderr, "NumChannels() - unknown format\n" );
            return 0;
    }
    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// Given an OpenGL Type, return the associated number of bits used.
unsigned int BitsPerChannel( unsigned int nType )
{
    switch( nType ){
        case GL_UNSIGNED_BYTE:
        case GL_BYTE:
            return 8;
        case GL_2_BYTES:
        case GL_SHORT:
        case GL_UNSIGNED_SHORT:
            return 16;
        case GL_4_BYTES:
        case GL_FLOAT:
        case GL_UNSIGNED_INT:
        case GL_INT:
            return 32;
        case GL_DOUBLE:
            return 64;
        case GL_3_BYTES:
            return 24;
        default:
            fprintf( stderr, "\nBitsPerChannel() - unknown image Type");
            return 0;
    }
    return 0;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Change to orthographic projection (for image drawing, etc)
void PushOrtho( const unsigned int nWidth, const unsigned int nHeight )
{
    // load ortho to the size of the window
    glPushMatrix();
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0, nWidth, nHeight, 0, -1, 1); // left, right, top, bottom, near, far
    glMatrixMode( GL_MODELVIEW );
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// Set projection matrix
void PopOrtho()
{
    glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 );
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// Set projection matrix
void OrthoQuad(
        const int nTexWidth,    //< Input:
        const int nTexHeight,   //< Input:
        const int nTop,      //< Input:
        const int nLeft,     //< Input:
        const int nBottom,   //< Input:
        const int nRight     //< Input:
        )
{
    /*
       glBegin( GL_QUADS );
       glTexCoord2f(    0.0,    0.0  ); glVertex3f( nLeft,  nBottom, 1 );
       glTexCoord2f(    0.0, nTexHeight ); glVertex3f( nLeft,  nTop, 1 );
       glTexCoord2f( nTexWidth, nTexHeight ); glVertex3f( nRight, nTop, 1 );
       glTexCoord2f( nTexWidth,    0.0  ); glVertex3f( nRight, nBottom, 1 );
       glEnd();
     */

    glNormal3f( -1,0,0 );
    glBegin( GL_QUADS );
    glTexCoord2f(       0.0,       0.0  ); glVertex3f(  nLeft, nBottom, 0 );
    glTexCoord2f( nTexWidth,       0.0  ); glVertex3f( nRight, nBottom, 0 );
    glTexCoord2f( nTexWidth, nTexHeight ); glVertex3f( nRight,    nTop, 0 );
    glTexCoord2f(       0.0, nTexHeight ); glVertex3f(  nLeft,    nTop, 0 );
    glEnd();

}

////////////////////////////////////////////////////////////////////////////////////////////////////
void DrawBorderAsWindowPercentage(
        const float fTop,
        const float fLeft,
        const float fBottom,
        const float fRight
        )
{
    GLint vViewport[4];
    glGetIntegerv( GL_VIEWPORT, vViewport );

    PushOrtho( vViewport[2], vViewport[3] );
    glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
    glBegin( GL_QUADS );
    glVertex3f(  fLeft*vViewport[2], fBottom*vViewport[3], 0 );
    glVertex3f( fRight*vViewport[2], fBottom*vViewport[3], 0 );
    glVertex3f( fRight*vViewport[2],    fTop*vViewport[3], 0 );
    glVertex3f(  fLeft*vViewport[2],    fTop*vViewport[3], 0 );
    glEnd();
    glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

    PopOrtho();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
void DrawTextureAsWindowPercentage(
        const unsigned int nTexId,      //< Input:
        const unsigned int nTexWidth, //< Input:
        const unsigned int nTexHeight,//< Input:
        const float fTop,
        const float fLeft,
        const float fBottom,
        const float fRight
        )
{
    glDisable( GL_LIGHTING );
    glColor4f( 1,1,1,1 ); // only scoop up texture colors

    GLint vViewport[4];
    glGetIntegerv( GL_VIEWPORT, vViewport );

    glEnable(GL_TEXTURE_RECTANGLE_ARB);
    glBindTexture( GL_TEXTURE_RECTANGLE_ARB, nTexId );

    PushOrtho( vViewport[2], vViewport[3] );
    OrthoQuad( nTexWidth, nTexHeight,
            fTop*vViewport[3], fLeft*vViewport[2],
            fBottom*vViewport[3], fRight*vViewport[2] );

    PopOrtho();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
void DrawTexture(
        const unsigned int nTexId,      //< Input:
        const unsigned int nTexWidth, //< Input:
        const unsigned int nTexHeight,//< Input:
        const unsigned int nTop,        //< Input:
        const unsigned int nLeft,       //< Input:
        const unsigned int nBottom,     //< Input:
        const unsigned int nRight       //< Input:
        )
{
    // NB DECAL ignores lighting
    //    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
    glDisable( GL_LIGHTING );
    glColor4f( 1,1,1,1 ); // only scoop up texture colors

    GLint vViewport[4];
    glGetIntegerv( GL_VIEWPORT, vViewport );

    glEnable(GL_TEXTURE_RECTANGLE_ARB);
    glBindTexture( GL_TEXTURE_RECTANGLE_ARB, nTexId );

    PushOrtho( vViewport[2], vViewport[3] );
    OrthoQuad( nTexWidth, nTexHeight, nTop, nLeft, nBottom, nRight );
    PopOrtho();
}

//////////////////////////////////////////////////////////////////////////////
// read from opengl buffer into our own vector
void ReadPixels(
        std::vector<unsigned char>& vPixels,
        int nWidth,
        int nHeight,
        bool bFlip /// default to true
        )
{
    int nFormat = GL_RGBA;
    int nType = GL_UNSIGNED_BYTE;

    unsigned int nBpp = GLBytesPerPixel( nFormat, nType );
    if( vPixels.size() < nWidth*nBpp*nHeight ){
        vPixels.resize( nWidth*nBpp*nHeight );
    }
    char* pPixelData = (char*)&vPixels[0];

    glReadPixels( 0, 0, nWidth, nHeight, nFormat, nType, pPixelData );

    if( bFlip ){
        int inc = nBpp*nWidth;
        char* pSwap = (char*)malloc( inc );
        char* pTopDown = pPixelData;
        char* pBottomUp = &pPixelData[ nWidth*nBpp*nHeight ];
        for( ; pTopDown < pBottomUp; pTopDown += inc, pBottomUp -= inc ){
            memcpy( pSwap, pTopDown, inc );
            memcpy( pTopDown, pBottomUp, inc );
            memcpy( pBottomUp, pSwap, inc );
        }
        free( pSwap );
    }
}

//////////////////////////////////////////////////////////////////////////////
// read from opengl buffer into our own vector
void ReadDepthPixels(
        std::vector<float>& vPixels,
        int nWidth,
        int nHeight,
        bool bFlip // defaults to true
        )
{
    int nFormat = GL_RGBA;
    int nType = GL_UNSIGNED_BYTE;

    unsigned int nBpp = GLBytesPerPixel( nFormat, nType );
    if( vPixels.size() < nWidth*nBpp*nHeight ){
        vPixels.resize( nWidth*nBpp*nHeight );
    }
    char* pPixelData = (char*)&vPixels[0];

    glReadPixels( 0, 0, nWidth, nHeight, nFormat, nType, pPixelData );

    if( bFlip ){
        int inc = nBpp*nWidth;
        char* pSwap = (char*)malloc( inc );
        char* pTopDown = pPixelData;
        char* pBottomUp = &pPixelData[ nWidth*nBpp*nHeight ];
        for( ; pTopDown < pBottomUp; pTopDown += inc, pBottomUp -= inc ){
            memcpy( pSwap, pTopDown, inc );
            memcpy( pTopDown, pBottomUp, inc );
            memcpy( pBottomUp, pSwap, inc );
        }
        free( pSwap );
    }
}

//////////////////////////////////////////////////////////////////////////////
void CheckFBOStatus()
{
    //Does the GPU support current FBO configuration?
    GLenum status;
    status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
    switch(status) {
        case GL_FRAMEBUFFER_COMPLETE_EXT:
            //    std::cerr << "The framebuffer is complete and valid for rendering.\n";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
            std::cerr << "One or more attachment points are not framebuffer attachment complete.\n";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
            std::cerr << "There are no attachments.\n";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
            std::cerr << "Attachments are of different size. All attachments must have the same width and height.\n";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
            std::cerr << "The color attachments have different format. All color attachments must have the same format.\n";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
            std::cerr << "An attachment point referenced by glDrawBuffers() doesn’t have an attachment.\n";
            break;
        case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
            std::cerr << "The attachment point referenced by glReadBuffers() doesn’t have an attachment.\n";
            break;
        case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
            std::cerr << "This particular FBO configuration is not supported by the implementation.\n";
            break;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// set perspective view.  same as gluperspective().
void Perspective( double fovy, double aspect, double zNear, double zFar)
{
    double xmin, xmax, ymin, ymax;
    ymax = zNear * tan(fovy * M_PI / 360.0);
    ymin = -ymax;
    xmin = ymin * aspect;
    xmax = ymax * aspect;
    glFrustum(xmin, xmax, ymin, ymax, zNear, zFar);
}

////////////////////////////////////////////////////////////////////////////////////////////////////
/// Reshape viewport whenever window changes size.
void ReshapeViewport( int w, int h )
{
    // Viewport
    glViewport(0, 0, w, h );

    // Projection
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    GLfloat ratio = float(w) / float(h);
    Perspective( 60.0, 1.0*ratio, 1.0, 1000.0 );

    // Model view
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// local little helper
Eigen::Vector4d Vec4( double a, double b, double c, double d )
{
    Eigen::Vector4d tmp;
    tmp << a, b, c, d;
    return tmp;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
void DrawCamera(
        int nTexWidth,
        int nTexHeight,
        int nTexId,
        const Eigen::Matrix4d& dModelViewMatrix,
        const Eigen::Matrix4d& dProjectionMatrix
        )
{
    // OK
    Eigen::Matrix4d M = dProjectionMatrix.inverse();
    Eigen::Matrix4d T = dModelViewMatrix.inverse();

    Eigen::Vector4d lbn = T*M*Vec4( -1,-1,-1, 1 );  lbn/=lbn[3];
    Eigen::Vector4d rbn = T*M*Vec4(  1,-1,-1, 1 );  rbn/=rbn[3];
    Eigen::Vector4d ltn = T*M*Vec4( -1, 1,-1, 1 );  ltn/=ltn[3];
    Eigen::Vector4d rtn = T*M*Vec4(  1, 1,-1, 1 );  rtn/=rtn[3];

    Eigen::Vector4d lbf = T*M*Vec4( -1,-1, 1, 1 );  lbf/=lbf[3];
    Eigen::Vector4d rbf = T*M*Vec4(  1,-1, 1, 1 );  rbf/=rbf[3];
    Eigen::Vector4d ltf = T*M*Vec4( -1, 1, 1, 1 );  ltf/=ltf[3];
    Eigen::Vector4d rtf = T*M*Vec4(  1, 1, 1, 1 );  rtf/=rtf[3];

    //    glColor4f( 1,1,1,1 );

    /// Draw texture
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
    glEnable( GL_TEXTURE_RECTANGLE_ARB );
    glBindTexture( GL_TEXTURE_RECTANGLE_ARB, nTexId );

    glEnable( GL_DEPTH_TEST );
    glDisable( GL_LIGHTING );
    glDisable( GL_LIGHT0 );
    glDisable( GL_CULL_FACE );

    glBegin( GL_QUADS );
    glNormal3f( -1,0,0 );
    glTexCoord2f(       0.0,       0.0  ); glVertex3dv( lbn.data() );
    glTexCoord2f( nTexWidth,       0.0  ); glVertex3dv( rbn.data() );
    glTexCoord2f( nTexWidth, nTexHeight ); glVertex3dv( rtn.data() );
    glTexCoord2f(       0.0, nTexHeight ); glVertex3dv( ltn.data() );
    glEnd();

    glBindTexture( GL_TEXTURE_RECTANGLE_ARB, 0 );
    glDisable(GL_TEXTURE_RECTANGLE_ARB);
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );

    //    glDisable( GL_DEPTH_TEST );
    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
    glEnable( GL_BLEND );

    glColor4f( 1, 1, 1, 0.5 );

    // draw frustrum
    glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
    glBegin( GL_QUADS );

    // near
    glVertex3dv( lbn.data() );
    glVertex3dv( rbn.data() );
    glVertex3dv( rtn.data() );
    glVertex3dv( ltn.data() );

    // far
    glVertex3dv( rbf.data() );
    glVertex3dv( lbf.data() );
    glVertex3dv( ltf.data() );
    glVertex3dv( rtf.data() );

    // left
    glVertex3dv( lbf.data() );
    glVertex3dv( lbn.data() );
    glVertex3dv( ltn.data() );
    glVertex3dv( ltf.data() );

    // right
    glVertex3dv( rbn.data() );
    glVertex3dv( rbf.data() );
    glVertex3dv( rtf.data() );
    glVertex3dv( rtn.data() );

    // top
    glVertex3dv( ltn.data() );
    glVertex3dv( rtn.data() );
    glVertex3dv( rtf.data() );
    glVertex3dv( ltf.data() );

    // bottom
    glVertex3dv( lbn.data() );
    glVertex3dv( rbn.data() );
    glVertex3dv( rbf.data() );
    glVertex3dv( lbf.data() );

    glEnd();
    glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );

    //    glEnable( GL_LIGHTING );
    //    lEnable( GL_LIGHT0 );

    glColor4f( 1,1,1,0.1 );
    glBegin( GL_QUADS );

    // near
    glVertex3dv( lbn.data() );
    glVertex3dv( rbn.data() );
    glVertex3dv( rtn.data() );
    glVertex3dv( ltn.data() );

    // far
    glVertex3dv( rbf.data() );
    glVertex3dv( lbf.data() );
    glVertex3dv( ltf.data() );
    glVertex3dv( rtf.data() );

    // left
    glVertex3dv( lbf.data() );
    glVertex3dv( lbn.data() );
    glVertex3dv( ltn.data() );
    glVertex3dv( ltf.data() );

    // right
    glVertex3dv( rbn.data() );
    glVertex3dv( rbf.data() );
    glVertex3dv( rtf.data() );
    glVertex3dv( rtn.data() );

    // top
    glVertex3dv( ltn.data() );
    glVertex3dv( rtn.data() );
    glVertex3dv( rtf.data() );
    glVertex3dv( ltf.data() );

    // bottom
    glVertex3dv( lbn.data() );
    glVertex3dv( rbn.data() );
    glVertex3dv( rbf.data() );
    glVertex3dv( lbf.data() );

    glEnd();
    glDisable( GL_BLEND );
    glEnable( GL_DEPTH_TEST );
    glEnable( GL_CULL_FACE );

}

} // SceneGraph


//==============================================================================
//         Copyright 2003 - 2012   LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2012   LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#define NT2_UNIT_MODULE "nt2 reduction toolbox - all/simd Mode"

//////////////////////////////////////////////////////////////////////////////
// Test behavior of reduction components in simd mode
//////////////////////////////////////////////////////////////////////////////
#include <boost/simd/include/functions/load.hpp>
#include <boost/simd/sdk/simd/native.hpp>
#include <nt2/include/functions/load.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/dispatch/functor/meta/call.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <boost/simd/include/constants/real.hpp>
#include <boost/simd/include/constants/infinites.hpp>
#include <boost/simd/include/functions/max.hpp>
#include <boost/simd/reduction/include/functions/all.hpp>

NT2_TEST_CASE_TPL ( all_real__1_0,  BOOST_SIMD_REAL_TYPES)
{
  using boost::simd::all;
  using boost::simd::tag::all_;
  using boost::simd::load;
  using boost::simd::native;
  using boost::simd::meta::cardinal_of;
  typedef BOOST_SIMD_DEFAULT_EXTENSION  ext_t;
  typedef typename boost::dispatch::meta::upgrade<T>::type   u_t;
  typedef native<T,ext_t>                        n_t;
  typedef n_t                                     vT;
  typedef typename boost::dispatch::meta::as_integer<T>::type iT;
  typedef native<iT,ext_t>                       ivT;
  typedef typename boost::dispatch::meta::call<all_(vT)>::type r_t;
  typedef typename boost::dispatch::meta::call<all_(T)>::type sr_t;
  typedef typename boost::dispatch::meta::scalar_of<r_t>::type ssr_t;

  // random verifications
  static const boost::simd::uint32_t NR = BOOST_SIMD_NB_RANDOM_TEST;
  {
    BOOST_SIMD_CREATE_BUF(tab_a0,T, NR, boost::simd::Valmin<T>(), boost::simd::Valmax<T>());
    double ulp0, ulpd ; ulpd=ulp0=0.0;
    for(uint32_t j = 0; j < NR/cardinal_of<n_t>::value; j++)
      {
        vT a0 = load<vT>(&tab_a0[0],j);
        r_t v = boost::simd::all(a0);
        bool z = true;
        for(int i = 0; i< cardinal_of<n_t>::value; i++)
          {
            z = z && (a0[j]);
          }
        NT2_TEST_EQUAL( v, z);
        }
      }

  }
} // end of test for floating_


// Copyright (c) 2018-2020 The PIVX developers
// Copyright (c) 2021 The ISLAMIC DIGITAL COIN developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "test_islamic_digital_coin.h"

#include "budget/budgetmanager.h"
#include "tinyformat.h"
#include "utilmoneystr.h"
#include "validation.h"
#include "utilstrencodings.h"

#include <boost/test/unit_test.hpp>

class BudgetManagerTest : CBudgetManager
{
public:
    void ForceAddFinalizedBudget(CFinalizedBudget& finalizedBudget)
    {
        LOCK(cs_budgets);
        const uint256& nHash = finalizedBudget.GetHash();
        mapFinalizedBudgets.emplace(nHash, finalizedBudget);
        mapFeeTxToBudget.emplace(finalizedBudget.GetFeeTXHash(), nHash);
    }

    bool IsBlockValueValid(int nHeight, CAmount& nExpectedValue, CAmount nMinted, bool fSporkActive = true)
    {
        // suppose masternodeSync is complete
        if (fSporkActive) {
            // add current payee amount to the expected block value
            CAmount expectedPayAmount;
            if (GetExpectedPayeeAmount(nHeight, expectedPayAmount)) {
                nExpectedValue += expectedPayAmount;
            }
        }
        return nMinted <= nExpectedValue;
    }
};

BOOST_FIXTURE_TEST_SUITE(budget_tests, TestingSetup)

void CheckBudgetValue(int nHeight, std::string strNetwork, CAmount nExpectedValue)
{
    CBudgetManager budget;
    CAmount nBudget = g_budgetman.GetTotalBudget(nHeight);
    std::string strError = strprintf("Budget is not as expected for %s. Result: %s, Expected: %s", strNetwork, FormatMoney(nBudget), FormatMoney(nExpectedValue));
    BOOST_CHECK_MESSAGE(nBudget == nExpectedValue, strError);
}

BOOST_AUTO_TEST_CASE(budget_value)
{
    SelectParams(CBaseChainParams::TESTNET);
    int nHeightTest = 0;
    CheckBudgetValue(nHeightTest-1, "testnet", 7200*COIN);
    CheckBudgetValue(nHeightTest, "testnet", 144*COIN);

    SelectParams(CBaseChainParams::MAIN);
    nHeightTest = 0;
    CheckBudgetValue(nHeightTest, "mainnet", 43200*COIN);
}

BOOST_AUTO_TEST_CASE(block_value)
{
    BudgetManagerTest t_budgetman;
    SelectParams(CBaseChainParams::TESTNET);
    std::string strError;

    // regular block
    int nHeight = 100;
    CAmount nExpected = GetBlockValue(nHeight);
    BOOST_CHECK(t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected-1));
    BOOST_CHECK(t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected));
    BOOST_CHECK(!t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected+1));

    // superblock - create the finalized budget with a proposal, and vote on it
    nHeight = 144;
    nExpected = GetBlockValue(nHeight);
    const CTxIn mnVin(GetRandHash(), 0);
    const CScript payee = GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))));
    const CAmount propAmt = 100 * COIN;
    const uint256& propHash = GetRandHash(), finTxId = GetRandHash();
    const CTxBudgetPayment txBudgetPayment(propHash, payee, propAmt);
    CFinalizedBudget fin("main (test)", 144, {txBudgetPayment}, finTxId);
    const CFinalizedBudgetVote fvote(mnVin, fin.GetHash());
    BOOST_CHECK(fin.AddOrUpdateVote(fvote, strError));
    t_budgetman.ForceAddFinalizedBudget(fin);

    // check superblock's block-value
    BOOST_CHECK(t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected));
    BOOST_CHECK(t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected+propAmt-1));
    BOOST_CHECK(t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected+propAmt));
    BOOST_CHECK(!t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected+propAmt+1));

    // check with spork disabled
    nExpected = GetBlockValue(nHeight);
    BOOST_CHECK(t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected-1, false));
    BOOST_CHECK(t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected, false));
    BOOST_CHECK(!t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected+1, false));
    BOOST_CHECK(!t_budgetman.IsBlockValueValid(nHeight, nExpected, nExpected+propAmt, false));
}

BOOST_AUTO_TEST_SUITE_END()


#include"stdafx.h"
#include <boost/math/distributions/fisher_f.hpp>
#include <boost/math/distributions/students_t.hpp>
#include<time.h>