/*
 * GraphBLAS Template Library (GBTL), Version 3.0
 *
 * Copyright 2020 Carnegie Mellon University, Battelle Memorial Institute, and
 * Authors.
 *
 * THIS MATERIAL WAS PREPARED AS AN ACCOUNT OF WORK SPONSORED BY AN AGENCY OF
 * THE UNITED STATES GOVERNMENT.  NEITHER THE UNITED STATES GOVERNMENT NOR THE
 * UNITED STATES DEPARTMENT OF ENERGY, NOR THE UNITED STATES DEPARTMENT OF
 * DEFENSE, NOR CARNEGIE MELLON UNIVERSITY, NOR BATTELLE, NOR ANY OF THEIR
 * EMPLOYEES, NOR ANY JURISDICTION OR ORGANIZATION THAT HAS COOPERATED IN THE
 * DEVELOPMENT OF THESE MATERIALS, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
 * ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS,
 * OR USEFULNESS OR ANY INFORMATION, APPARATUS, PRODUCT, SOFTWARE, OR PROCESS
 * DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED
 * RIGHTS.
 *
 * Released under a BSD-style license, please see LICENSE file or contact
 * permission@sei.cmu.edu for full terms.
 *
 * [DISTRIBUTION STATEMENT A] This material has been approved for public release
 * and unlimited distribution.  Please see Copyright notice for non-US
 * Government use and distribution.
 *
 * This Software includes and/or makes use of the following Third-Party Software
 * subject to its own license:
 *
 * 1. Boost Unit Test Framework
 * (https://www.boost.org/doc/libs/1_45_0/libs/test/doc/html/utf.html)
 * Copyright 2001 Boost software license, Gennadiy Rozental.
 *
 * DM20-0442
 */

#include <functional>
#include <iostream>
#include <vector>

#include <graphblas/graphblas.hpp>

using namespace grb;

#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE algebra_semiring_test_suite

#include <boost/test/included/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE)

//****************************************************************************
BOOST_AUTO_TEST_CASE(arithmetic_semiring_test)
{
    BOOST_CHECK_EQUAL(ArithmeticSemiring<double>().zero(), 0.0);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<double>().add(-2., 1.), -1.0);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<double>().mult(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<float>().zero(), 0.0f);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<float>().add(-2.f, 1.f), -1.0f);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<float>().mult(-2.f, 1.f), -2.0f);

    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint64_t>().zero(), 0UL);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint64_t>().add(2UL, 1UL), 3UL);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint64_t>().mult(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint32_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint32_t>().add(2U, 1U), 3U);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint32_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint16_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint16_t>().add(2U, 1U), 3U);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint16_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint8_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint8_t>().add(2U, 1U), 3U);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<uint8_t>().mult(2U, 1U), 2U);

    BOOST_CHECK_EQUAL(ArithmeticSemiring<int64_t>().zero(), 0L);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int64_t>().add(-2L, 1L), -1L);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int64_t>().mult(-2L, 1L), -2L);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int32_t>().zero(), 0);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int32_t>().add(-2, 1), -1);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int32_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int16_t>().zero(), 0);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int16_t>().add(-2, 1), -1);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int16_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int8_t>().zero(), 0);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int8_t>().add(-2, 1), -1);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<int8_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(ArithmeticSemiring<bool>().zero(), false);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<bool>().add(false, true), true);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<bool>().mult(false, true), false);
    BOOST_CHECK_EQUAL(ArithmeticSemiring<bool>().mult(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(min_plus_semiring_test)
{
    BOOST_CHECK_EQUAL(MinPlusSemiring<double>().zero(),
                      std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MinPlusSemiring<double>().add(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(MinPlusSemiring<double>().add(2., 1.), 1.0);
    BOOST_CHECK_EQUAL(MinPlusSemiring<double>().mult(-2., 1.), -1.0);

    BOOST_CHECK_EQUAL(MinPlusSemiring<float>().zero(),
                      std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MinPlusSemiring<float>().add(-2.f, 1.f), -2.0f);
    BOOST_CHECK_EQUAL(MinPlusSemiring<float>().add(2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(MinPlusSemiring<float>().mult(-2.f, 1.f), -1.0f);

    BOOST_CHECK_EQUAL(MinPlusSemiring<uint64_t>().zero(),
                      std::numeric_limits<uint64_t>::max());
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint64_t>().add(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint64_t>().add(2UL, 3UL), 2UL);
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint64_t>().mult(2UL, 1UL), 3UL);

    BOOST_CHECK_EQUAL(MinPlusSemiring<uint32_t>().zero(),
                      std::numeric_limits<uint32_t>::max());
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint32_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint32_t>().add(2U, 3U), 2U);
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint32_t>().mult(2U, 1U), 3U);

    BOOST_CHECK_EQUAL(MinPlusSemiring<uint16_t>().zero(),
                      std::numeric_limits<uint16_t>::max());
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint16_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint16_t>().add(2U, 3U), 2U);
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint16_t>().mult(2U, 1U), 3U);

    BOOST_CHECK_EQUAL(MinPlusSemiring<uint8_t>().zero(),
                      std::numeric_limits<uint8_t>::max());
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint8_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint8_t>().add(2U, 3U), 2U);
    BOOST_CHECK_EQUAL(MinPlusSemiring<uint8_t>().mult(2U, 1U), 3U);

    BOOST_CHECK_EQUAL(MinPlusSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::max());
    BOOST_CHECK_EQUAL(MinPlusSemiring<int64_t>().add(-2L, 1L), -2L);
    BOOST_CHECK_EQUAL(MinPlusSemiring<int64_t>().add(2L, -1L), -1L);
    BOOST_CHECK_EQUAL(MinPlusSemiring<int64_t>().mult(-2L, 1L), -1L);

    BOOST_CHECK_EQUAL(MinPlusSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::max());
    BOOST_CHECK_EQUAL(MinPlusSemiring<int32_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinPlusSemiring<int32_t>().add(2, -1), -1);
    BOOST_CHECK_EQUAL(MinPlusSemiring<int32_t>().mult(-2, 1), -1);

    BOOST_CHECK_EQUAL(MinPlusSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::max());
    BOOST_CHECK_EQUAL(MinPlusSemiring<int16_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinPlusSemiring<int16_t>().add(2, -1), -1);
    BOOST_CHECK_EQUAL(MinPlusSemiring<int16_t>().mult(-2, 1), -1);

    BOOST_CHECK_EQUAL(MinPlusSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::max());
    BOOST_CHECK_EQUAL(MinPlusSemiring<int8_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinPlusSemiring<int8_t>().add(2, -1), -1);
    BOOST_CHECK_EQUAL(MinPlusSemiring<int8_t>().mult(-2, 1), -1);

    BOOST_CHECK_EQUAL(MinPlusSemiring<bool>().zero(), true);
    BOOST_CHECK_EQUAL(MinPlusSemiring<bool>().add(false, true), false);
    BOOST_CHECK_EQUAL(MinPlusSemiring<bool>().add(true, true), true);
    BOOST_CHECK_EQUAL(MinPlusSemiring<bool>().mult(false, false), false);
    BOOST_CHECK_EQUAL(MinPlusSemiring<bool>().mult(true, false), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(max_plus_semiring_test)
{
    BOOST_CHECK_EQUAL(MaxPlusSemiring<double>().zero(),
                      -std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<double>().add(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<double>().add(2., 1.), 2.0);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<double>().mult(-2., 1.), -1.0);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<float>().zero(),
                      -std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<float>().add(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<float>().add(2.f, 1.f), 2.0f);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<float>().mult(-2.f, 1.f), -1.0f);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint64_t>().zero(),
                      std::numeric_limits<uint64_t>::min());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint64_t>().add(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint64_t>().add(2UL, 3UL), 3UL);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint64_t>().mult(2UL, 1UL), 3UL);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint32_t>().zero(),
                      std::numeric_limits<uint32_t>::min());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint32_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint32_t>().add(2U, 3U), 3U);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint32_t>().mult(2U, 1U), 3U);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint16_t>().zero(),
                      std::numeric_limits<uint16_t>::min());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint16_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint16_t>().add(2U, 3U), 3U);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint16_t>().mult(2U, 1U), 3U);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint8_t>().zero(),
                      std::numeric_limits<uint8_t>::min());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint8_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint8_t>().add(2U, 3U), 3U);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<uint8_t>().mult(2U, 1U), 3U);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::min());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int64_t>().add(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int64_t>().add(2L, -1L), 2L);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int64_t>().mult(-2L, 1L), -1L);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::min());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int32_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int32_t>().add(2, -1), 2);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int32_t>().mult(-2, 1), -1);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::min());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int16_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int16_t>().add(2, -1), 2);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int16_t>().mult(-2, 1), -1);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::min());
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int8_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int8_t>().add(2, -1), 2);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<int8_t>().mult(-2, 1), -1);

    BOOST_CHECK_EQUAL(MaxPlusSemiring<bool>().zero(), false);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<bool>().add(false, true), true);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<bool>().add(true, true), true);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<bool>().mult(false, false), false);
    BOOST_CHECK_EQUAL(MaxPlusSemiring<bool>().mult(true, false), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(min_times_semiring_test)
{
    BOOST_CHECK_EQUAL(MinTimesSemiring<double>().zero(),
                      std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MinTimesSemiring<double>().add(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(MinTimesSemiring<double>().mult(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(MinTimesSemiring<float>().zero(),
                      std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MinTimesSemiring<float>().add(-2.f, 1.f), -2.0f);
    BOOST_CHECK_EQUAL(MinTimesSemiring<float>().mult(-2.f, 1.f), -2.0f);

    BOOST_CHECK_EQUAL(MinTimesSemiring<uint64_t>().zero(),
                      std::numeric_limits<uint64_t>::max());
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint64_t>().add(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint64_t>().mult(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint32_t>().zero(),
                      std::numeric_limits<uint32_t>::max());
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint32_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint32_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint16_t>().zero(),
                      std::numeric_limits<uint16_t>::max());
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint16_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint16_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint8_t>().zero(),
                      std::numeric_limits<uint8_t>::max());
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint8_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinTimesSemiring<uint8_t>().mult(2U, 1U), 2U);

    BOOST_CHECK_EQUAL(MinTimesSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::max());
    BOOST_CHECK_EQUAL(MinTimesSemiring<int64_t>().add(-2L, 1L), -2L);
    BOOST_CHECK_EQUAL(MinTimesSemiring<int64_t>().mult(-2L, 1L), -2L);
    BOOST_CHECK_EQUAL(MinTimesSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::max());
    BOOST_CHECK_EQUAL(MinTimesSemiring<int32_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinTimesSemiring<int32_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinTimesSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::max());
    BOOST_CHECK_EQUAL(MinTimesSemiring<int16_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinTimesSemiring<int16_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinTimesSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::max());
    BOOST_CHECK_EQUAL(MinTimesSemiring<int8_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinTimesSemiring<int8_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(MinTimesSemiring<bool>().zero(), true);
    BOOST_CHECK_EQUAL(MinTimesSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(MinTimesSemiring<bool>().add(false, true), false);
    BOOST_CHECK_EQUAL(MinTimesSemiring<bool>().mult(false, true), false);
    BOOST_CHECK_EQUAL(MinTimesSemiring<bool>().mult(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(max_times_semiring_test)
{
    BOOST_CHECK_EQUAL(MaxTimesSemiring<double>().zero(),
                      -std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MaxTimesSemiring<double>().add(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<double>().mult(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<float>().zero(),
                      -std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MaxTimesSemiring<float>().add(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<float>().mult(-2.f, 1.f), -2.0f);

    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint64_t>().zero(), 0UL);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint64_t>().add(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint64_t>().mult(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint32_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint32_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint32_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint16_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint16_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint16_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint8_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint8_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<uint8_t>().mult(2U, 1U), 2U);

    BOOST_CHECK_EQUAL(MaxTimesSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::min());
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int64_t>().add(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int64_t>().mult(-2L, 1L), -2L);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::min());
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int32_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int32_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::min());
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int16_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int16_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::min());
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int8_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<int8_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(MaxTimesSemiring<bool>().zero(), false);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<bool>().add(false, true), true);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<bool>().mult(false, true), false);
    BOOST_CHECK_EQUAL(MaxTimesSemiring<bool>().mult(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(min_max_semiring_test)
{
    BOOST_CHECK_EQUAL(MinMaxSemiring<double>().zero(),
                      std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MinMaxSemiring<double>().add(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(MinMaxSemiring<double>().mult(-2., 1.),  1.0);
    BOOST_CHECK_EQUAL(MinMaxSemiring<float>().zero(),
                      std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MinMaxSemiring<float>().add(-2.f, 1.f), -2.0f);
    BOOST_CHECK_EQUAL(MinMaxSemiring<float>().mult(-2.f, 1.f),  1.0f);

    BOOST_CHECK_EQUAL(MinMaxSemiring<uint64_t>().zero(),
                      std::numeric_limits<uint64_t>::max());
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint64_t>().add(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint64_t>().mult(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint32_t>().zero(),
                      std::numeric_limits<uint32_t>::max());
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint32_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint32_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint16_t>().zero(),
                      std::numeric_limits<uint16_t>::max());
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint16_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint16_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint8_t>().zero(),
                      std::numeric_limits<uint8_t>::max());
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint8_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinMaxSemiring<uint8_t>().mult(2U, 1U), 2U);

    BOOST_CHECK_EQUAL(MinMaxSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::max());
    BOOST_CHECK_EQUAL(MinMaxSemiring<int64_t>().add(-2L, 1L),-2L);
    BOOST_CHECK_EQUAL(MinMaxSemiring<int64_t>().mult(-2L, 1L),  1L);
    BOOST_CHECK_EQUAL(MinMaxSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::max());
    BOOST_CHECK_EQUAL(MinMaxSemiring<int32_t>().add(-2, 1),-2);
    BOOST_CHECK_EQUAL(MinMaxSemiring<int32_t>().mult(-2, 1),  1);
    BOOST_CHECK_EQUAL(MinMaxSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::max());
    BOOST_CHECK_EQUAL(MinMaxSemiring<int16_t>().add(-2, 1),-2);
    BOOST_CHECK_EQUAL(MinMaxSemiring<int16_t>().mult(-2, 1),  1);
    BOOST_CHECK_EQUAL(MinMaxSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::max());
    BOOST_CHECK_EQUAL(MinMaxSemiring<int8_t>().add(-2, 1),-2);
    BOOST_CHECK_EQUAL(MinMaxSemiring<int8_t>().mult(-2, 1),  1);

    BOOST_CHECK_EQUAL(MinMaxSemiring<bool>().zero(), true);
    BOOST_CHECK_EQUAL(MinMaxSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(MinMaxSemiring<bool>().add(false, true), false);
    BOOST_CHECK_EQUAL(MinMaxSemiring<bool>().mult(false, true), true);
    BOOST_CHECK_EQUAL(MinMaxSemiring<bool>().mult(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(max_min_semiring_test)
{
    BOOST_CHECK_EQUAL(MaxMinSemiring<double>().zero(),
                      -std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MaxMinSemiring<double>().add(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(MaxMinSemiring<double>().mult(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(MaxMinSemiring<float>().zero(),
                      -std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MaxMinSemiring<float>().add(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(MaxMinSemiring<float>().mult(-2.f, 1.f), -2.0f);

    BOOST_CHECK_EQUAL(MaxMinSemiring<uint64_t>().zero(), 0UL);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint64_t>().add(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint64_t>().mult(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint32_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint32_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint32_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint16_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint16_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint16_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint8_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint8_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxMinSemiring<uint8_t>().mult(2U, 1U), 1U);

    BOOST_CHECK_EQUAL(MaxMinSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::min());
    BOOST_CHECK_EQUAL(MaxMinSemiring<int64_t>().add(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(MaxMinSemiring<int64_t>().mult(-2L, 1L), -2L);
    BOOST_CHECK_EQUAL(MaxMinSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::min());
    BOOST_CHECK_EQUAL(MaxMinSemiring<int32_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxMinSemiring<int32_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(MaxMinSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::min());
    BOOST_CHECK_EQUAL(MaxMinSemiring<int16_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxMinSemiring<int16_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(MaxMinSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::min());
    BOOST_CHECK_EQUAL(MaxMinSemiring<int8_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxMinSemiring<int8_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(MaxMinSemiring<bool>().zero(), false);
    BOOST_CHECK_EQUAL(MaxMinSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(MaxMinSemiring<bool>().add(false, true), true);
    BOOST_CHECK_EQUAL(MaxMinSemiring<bool>().mult(false, true), false);
    BOOST_CHECK_EQUAL(MaxMinSemiring<bool>().mult(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(plus_min_semiring_test)
{
    BOOST_CHECK_EQUAL(PlusMinSemiring<double>().zero(), 0.);
    BOOST_CHECK_EQUAL(PlusMinSemiring<double>().add(-2., 1.), -1.0);
    BOOST_CHECK_EQUAL(PlusMinSemiring<double>().add(2., 1.), 3.0);
    BOOST_CHECK_EQUAL(PlusMinSemiring<double>().mult(-2., 1.), -2.0);

    BOOST_CHECK_EQUAL(PlusMinSemiring<float>().zero(), 0.f);
    BOOST_CHECK_EQUAL(PlusMinSemiring<float>().add(-2.f, 1.f), -1.0f);
    BOOST_CHECK_EQUAL(PlusMinSemiring<float>().add(2.f, 1.f), 3.0f);
    BOOST_CHECK_EQUAL(PlusMinSemiring<float>().mult(-2.f, 1.f), -2.0f);

    BOOST_CHECK_EQUAL(PlusMinSemiring<uint64_t>().zero(), 0UL);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint64_t>().add(2UL, 1UL), 3UL);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint64_t>().add(2UL, 3UL), 5UL);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint64_t>().mult(2UL, 1UL), 1UL);

    BOOST_CHECK_EQUAL(PlusMinSemiring<uint32_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint32_t>().add(2U, 1U), 3U);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint32_t>().add(2U, 3U), 5U);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint32_t>().mult(2U, 1U), 1U);

    BOOST_CHECK_EQUAL(PlusMinSemiring<uint16_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint16_t>().add(2U, 1U), 3U);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint16_t>().add(2U, 3U), 5U);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint16_t>().mult(2U, 1U), 1U);

    BOOST_CHECK_EQUAL(PlusMinSemiring<uint8_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint8_t>().add(2U, 1U), 3U);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint8_t>().add(2U, 3U), 5U);
    BOOST_CHECK_EQUAL(PlusMinSemiring<uint8_t>().mult(2U, 1U), 1U);

    BOOST_CHECK_EQUAL(PlusMinSemiring<int64_t>().zero(), 0L);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int64_t>().add(-2L, 1L), -1L);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int64_t>().add(2L, -1L), 1L);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int64_t>().mult(-2L, 1L), -2L);

    BOOST_CHECK_EQUAL(PlusMinSemiring<int32_t>().zero(), 0);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int32_t>().add(-2, 1), -1);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int32_t>().add(2, -1), 1);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int32_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(PlusMinSemiring<int16_t>().zero(), 0);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int16_t>().add(-2, 1), -1);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int16_t>().add(2, -1), 1);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int16_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(PlusMinSemiring<int8_t>().zero(), 0);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int8_t>().add(-2, 1), -1);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int8_t>().add(2, -1), 1);
    BOOST_CHECK_EQUAL(PlusMinSemiring<int8_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(PlusMinSemiring<bool>().zero(), false);
    BOOST_CHECK_EQUAL(PlusMinSemiring<bool>().add(false, true), true);
    BOOST_CHECK_EQUAL(PlusMinSemiring<bool>().add(true, true), true);
    BOOST_CHECK_EQUAL(PlusMinSemiring<bool>().mult(false, false), false);
    BOOST_CHECK_EQUAL(PlusMinSemiring<bool>().mult(true, false), false);
    BOOST_CHECK_EQUAL(PlusMinSemiring<bool>().mult(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(logical_semiring_test)
{
    BOOST_CHECK_EQUAL(LogicalSemiring<double>().zero(), 0.0);
    BOOST_CHECK_EQUAL(LogicalSemiring<double>().add(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(LogicalSemiring<double>().add(0., 1.), 1.0);
    BOOST_CHECK_EQUAL(LogicalSemiring<double>().add(-2., 0.), 1.0);
    BOOST_CHECK_EQUAL(LogicalSemiring<double>().add(0., 0.), 0.0);
    BOOST_CHECK_EQUAL(LogicalSemiring<double>().mult(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(LogicalSemiring<double>().mult(0., 1.), 0.0);
    BOOST_CHECK_EQUAL(LogicalSemiring<double>().mult(-2., 0.), 0.0);
    BOOST_CHECK_EQUAL(LogicalSemiring<double>().mult(0., 0.), 0.0);

    BOOST_CHECK_EQUAL(LogicalSemiring<float>().zero(), 0.0f);
    BOOST_CHECK_EQUAL(LogicalSemiring<float>().add(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(LogicalSemiring<float>().add(0.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(LogicalSemiring<float>().add(-2.f, 0.f), 1.0f);
    BOOST_CHECK_EQUAL(LogicalSemiring<float>().add(0.f, 0.f), 0.0f);
    BOOST_CHECK_EQUAL(LogicalSemiring<float>().mult(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(LogicalSemiring<float>().mult(0.f, 1.f), 0.0f);
    BOOST_CHECK_EQUAL(LogicalSemiring<float>().mult(-2.f, 0.f), 0.0f);
    BOOST_CHECK_EQUAL(LogicalSemiring<float>().mult(0.f, 0.f), 0.0f);

    BOOST_CHECK_EQUAL(LogicalSemiring<uint64_t>().zero(), 0UL);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint64_t>().add(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint64_t>().add(2UL, 0UL), 1UL);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint64_t>().add(0UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint64_t>().add(0UL, 0UL), 0UL);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint64_t>().mult(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint64_t>().mult(2UL, 0UL), 0UL);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint64_t>().mult(0UL, 1UL), 0UL);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint64_t>().mult(0UL, 0UL), 0UL);

    BOOST_CHECK_EQUAL(LogicalSemiring<uint32_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint32_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint32_t>().add(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint32_t>().add(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint32_t>().add(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint32_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint32_t>().mult(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint32_t>().mult(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint32_t>().mult(0U, 0U), 0U);

    BOOST_CHECK_EQUAL(LogicalSemiring<uint16_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint16_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint16_t>().add(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint16_t>().add(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint16_t>().add(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint16_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint16_t>().mult(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint16_t>().mult(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint16_t>().mult(0U, 0U), 0U);

    BOOST_CHECK_EQUAL(LogicalSemiring<uint8_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint8_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint8_t>().add(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint8_t>().add(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint8_t>().add(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint8_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint8_t>().mult(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint8_t>().mult(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(LogicalSemiring<uint8_t>().mult(0U, 0U), 0U);

    BOOST_CHECK_EQUAL(LogicalSemiring<int64_t>().zero(), 0L);
    BOOST_CHECK_EQUAL(LogicalSemiring<int64_t>().add(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(LogicalSemiring<int64_t>().add(-2L, 0L), 1L);
    BOOST_CHECK_EQUAL(LogicalSemiring<int64_t>().add(0L, 1L), 1L);
    BOOST_CHECK_EQUAL(LogicalSemiring<int64_t>().add(0L, 0L), 0L);
    BOOST_CHECK_EQUAL(LogicalSemiring<int64_t>().mult(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(LogicalSemiring<int64_t>().mult(-2L, 0L), 0L);
    BOOST_CHECK_EQUAL(LogicalSemiring<int64_t>().mult(0L, 1L), 0L);
    BOOST_CHECK_EQUAL(LogicalSemiring<int64_t>().mult(0L, 0L), 0L);

    BOOST_CHECK_EQUAL(LogicalSemiring<int32_t>().zero(), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int32_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int32_t>().add(-2, 0), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int32_t>().add(0, 1), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int32_t>().add(0, 0), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int32_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int32_t>().mult(-2, 0), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int32_t>().mult(0, 1), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int32_t>().mult(0, 0), 0);

    BOOST_CHECK_EQUAL(LogicalSemiring<int16_t>().zero(), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int16_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int16_t>().add(-2, 0), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int16_t>().add(0, 1), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int16_t>().add(0, 0), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int16_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int16_t>().mult(-2, 0), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int16_t>().mult(0, 1), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int16_t>().mult(0, 0), 0);

    BOOST_CHECK_EQUAL(LogicalSemiring<int8_t>().zero(), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int8_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int8_t>().add(-2, 0), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int8_t>().add(0, 1), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int8_t>().add(0, 0), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int8_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(LogicalSemiring<int8_t>().mult(-2, 0), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int8_t>().mult(0, 1), 0);
    BOOST_CHECK_EQUAL(LogicalSemiring<int8_t>().mult(0, 0), 0);

    BOOST_CHECK_EQUAL(LogicalSemiring<bool>().zero(), false);
    BOOST_CHECK_EQUAL(LogicalSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(LogicalSemiring<bool>().add(false, true), true);
    BOOST_CHECK_EQUAL(LogicalSemiring<bool>().add(true, false), true);
    BOOST_CHECK_EQUAL(LogicalSemiring<bool>().add(true, true), true);
    BOOST_CHECK_EQUAL(LogicalSemiring<bool>().mult(false, false), false);
    BOOST_CHECK_EQUAL(LogicalSemiring<bool>().mult(false, true), false);
    BOOST_CHECK_EQUAL(LogicalSemiring<bool>().mult(true, false), false);
    BOOST_CHECK_EQUAL(LogicalSemiring<bool>().mult(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(and_or_semiring_test)
{
    BOOST_CHECK_EQUAL(AndOrSemiring<double>().zero(), 1.0);
    BOOST_CHECK_EQUAL(AndOrSemiring<double>().mult(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(AndOrSemiring<double>().mult(0., 1.), 1.0);
    BOOST_CHECK_EQUAL(AndOrSemiring<double>().mult(-2., 0.), 1.0);
    BOOST_CHECK_EQUAL(AndOrSemiring<double>().mult(0., 0.), 0.0);
    BOOST_CHECK_EQUAL(AndOrSemiring<double>().add(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(AndOrSemiring<double>().add(0., 1.), 0.0);
    BOOST_CHECK_EQUAL(AndOrSemiring<double>().add(-2., 0.), 0.0);
    BOOST_CHECK_EQUAL(AndOrSemiring<double>().add(0., 0.), 0.0);

    BOOST_CHECK_EQUAL(AndOrSemiring<float>().zero(), 1.0f);
    BOOST_CHECK_EQUAL(AndOrSemiring<float>().mult(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(AndOrSemiring<float>().mult(0.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(AndOrSemiring<float>().mult(-2.f, 0.f), 1.0f);
    BOOST_CHECK_EQUAL(AndOrSemiring<float>().mult(0.f, 0.f), 0.0f);
    BOOST_CHECK_EQUAL(AndOrSemiring<float>().add(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(AndOrSemiring<float>().add(0.f, 1.f), 0.0f);
    BOOST_CHECK_EQUAL(AndOrSemiring<float>().add(-2.f, 0.f), 0.0f);
    BOOST_CHECK_EQUAL(AndOrSemiring<float>().add(0.f, 0.f), 0.0f);

    BOOST_CHECK_EQUAL(AndOrSemiring<uint64_t>().zero(), 1UL);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint64_t>().mult(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint64_t>().mult(2UL, 0UL), 1UL);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint64_t>().mult(0UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint64_t>().mult(0UL, 0UL), 0UL);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint64_t>().add(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint64_t>().add(2UL, 0UL), 0UL);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint64_t>().add(0UL, 1UL), 0UL);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint64_t>().add(0UL, 0UL), 0UL);

    BOOST_CHECK_EQUAL(AndOrSemiring<uint32_t>().zero(), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint32_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint32_t>().mult(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint32_t>().mult(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint32_t>().mult(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint32_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint32_t>().add(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint32_t>().add(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint32_t>().add(0U, 0U), 0U);

    BOOST_CHECK_EQUAL(AndOrSemiring<uint16_t>().zero(), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint16_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint16_t>().mult(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint16_t>().mult(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint16_t>().mult(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint16_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint16_t>().add(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint16_t>().add(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint16_t>().add(0U, 0U), 0U);

    BOOST_CHECK_EQUAL(AndOrSemiring<uint8_t>().zero(), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint8_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint8_t>().mult(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint8_t>().mult(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint8_t>().mult(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint8_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint8_t>().add(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint8_t>().add(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(AndOrSemiring<uint8_t>().add(0U, 0U), 0U);

    BOOST_CHECK_EQUAL(AndOrSemiring<int64_t>().zero(), 1L);
    BOOST_CHECK_EQUAL(AndOrSemiring<int64_t>().mult(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(AndOrSemiring<int64_t>().mult(-2L, 0L), 1L);
    BOOST_CHECK_EQUAL(AndOrSemiring<int64_t>().mult(0L, 1L), 1L);
    BOOST_CHECK_EQUAL(AndOrSemiring<int64_t>().mult(0L, 0L), 0L);
    BOOST_CHECK_EQUAL(AndOrSemiring<int64_t>().add(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(AndOrSemiring<int64_t>().add(-2L, 0L), 0L);
    BOOST_CHECK_EQUAL(AndOrSemiring<int64_t>().add(0L, 1L), 0L);
    BOOST_CHECK_EQUAL(AndOrSemiring<int64_t>().add(0L, 0L), 0L);

    BOOST_CHECK_EQUAL(AndOrSemiring<int32_t>().zero(), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int32_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int32_t>().mult(-2, 0), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int32_t>().mult(0, 1), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int32_t>().mult(0, 0), 0);
    BOOST_CHECK_EQUAL(AndOrSemiring<int32_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int32_t>().add(-2, 0), 0);
    BOOST_CHECK_EQUAL(AndOrSemiring<int32_t>().add(0, 1), 0);
    BOOST_CHECK_EQUAL(AndOrSemiring<int32_t>().add(0, 0), 0);

    BOOST_CHECK_EQUAL(AndOrSemiring<int16_t>().zero(), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int16_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int16_t>().mult(-2, 0), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int16_t>().mult(0, 1), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int16_t>().mult(0, 0), 0);
    BOOST_CHECK_EQUAL(AndOrSemiring<int16_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int16_t>().add(-2, 0), 0);
    BOOST_CHECK_EQUAL(AndOrSemiring<int16_t>().add(0, 1), 0);
    BOOST_CHECK_EQUAL(AndOrSemiring<int16_t>().add(0, 0), 0);

    BOOST_CHECK_EQUAL(AndOrSemiring<int8_t>().zero(), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int8_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int8_t>().mult(-2, 0), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int8_t>().mult(0, 1), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int8_t>().mult(0, 0), 0);
    BOOST_CHECK_EQUAL(AndOrSemiring<int8_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(AndOrSemiring<int8_t>().add(-2, 0), 0);
    BOOST_CHECK_EQUAL(AndOrSemiring<int8_t>().add(0, 1), 0);
    BOOST_CHECK_EQUAL(AndOrSemiring<int8_t>().add(0, 0), 0);

    BOOST_CHECK_EQUAL(AndOrSemiring<bool>().zero(), true);
    BOOST_CHECK_EQUAL(AndOrSemiring<bool>().mult(false, false), false);
    BOOST_CHECK_EQUAL(AndOrSemiring<bool>().mult(false, true), true);
    BOOST_CHECK_EQUAL(AndOrSemiring<bool>().mult(true, false), true);
    BOOST_CHECK_EQUAL(AndOrSemiring<bool>().mult(true, true), true);
    BOOST_CHECK_EQUAL(AndOrSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(AndOrSemiring<bool>().add(false, true), false);
    BOOST_CHECK_EQUAL(AndOrSemiring<bool>().add(true, false), false);
    BOOST_CHECK_EQUAL(AndOrSemiring<bool>().add(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(xor_and_semiring_test)
{
    BOOST_CHECK_EQUAL(XorAndSemiring<double>().zero(), 0.0);
    BOOST_CHECK_EQUAL(XorAndSemiring<double>().add(-2., 1.), 0.0);
    BOOST_CHECK_EQUAL(XorAndSemiring<double>().add(0., 1.), 1.0);
    BOOST_CHECK_EQUAL(XorAndSemiring<double>().add(-2., 0.), 1.0);
    BOOST_CHECK_EQUAL(XorAndSemiring<double>().add(0., 0.), 0.0);
    BOOST_CHECK_EQUAL(XorAndSemiring<double>().mult(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(XorAndSemiring<double>().mult(0., 1.), 0.0);
    BOOST_CHECK_EQUAL(XorAndSemiring<double>().mult(-2., 0.), 0.0);
    BOOST_CHECK_EQUAL(XorAndSemiring<double>().mult(0., 0.), 0.0);

    BOOST_CHECK_EQUAL(XorAndSemiring<float>().zero(), 0.0f);
    BOOST_CHECK_EQUAL(XorAndSemiring<float>().add(-2.f, 1.f), 0.0f);
    BOOST_CHECK_EQUAL(XorAndSemiring<float>().add(0.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(XorAndSemiring<float>().add(-2.f, 0.f), 1.0f);
    BOOST_CHECK_EQUAL(XorAndSemiring<float>().add(0.f, 0.f), 0.0f);
    BOOST_CHECK_EQUAL(XorAndSemiring<float>().mult(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(XorAndSemiring<float>().mult(0.f, 1.f), 0.0f);
    BOOST_CHECK_EQUAL(XorAndSemiring<float>().mult(-2.f, 0.f), 0.0f);
    BOOST_CHECK_EQUAL(XorAndSemiring<float>().mult(0.f, 0.f), 0.0f);

    BOOST_CHECK_EQUAL(XorAndSemiring<uint64_t>().zero(), 0UL);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint64_t>().add(2UL, 1UL), 0UL);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint64_t>().add(2UL, 0UL), 1UL);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint64_t>().add(0UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint64_t>().add(0UL, 0UL), 0UL);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint64_t>().mult(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint64_t>().mult(2UL, 0UL), 0UL);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint64_t>().mult(0UL, 1UL), 0UL);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint64_t>().mult(0UL, 0UL), 0UL);

    BOOST_CHECK_EQUAL(XorAndSemiring<uint32_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint32_t>().add(2U, 1U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint32_t>().add(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint32_t>().add(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint32_t>().add(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint32_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint32_t>().mult(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint32_t>().mult(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint32_t>().mult(0U, 0U), 0U);

    BOOST_CHECK_EQUAL(XorAndSemiring<uint16_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint16_t>().add(2U, 1U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint16_t>().add(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint16_t>().add(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint16_t>().add(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint16_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint16_t>().mult(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint16_t>().mult(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint16_t>().mult(0U, 0U), 0U);

    BOOST_CHECK_EQUAL(XorAndSemiring<uint8_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint8_t>().add(2U, 1U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint8_t>().add(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint8_t>().add(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint8_t>().add(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint8_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint8_t>().mult(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint8_t>().mult(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(XorAndSemiring<uint8_t>().mult(0U, 0U), 0U);

    BOOST_CHECK_EQUAL(XorAndSemiring<int64_t>().zero(), 0L);
    BOOST_CHECK_EQUAL(XorAndSemiring<int64_t>().add(-2L, 1L), 0L);
    BOOST_CHECK_EQUAL(XorAndSemiring<int64_t>().add(-2L, 0L), 1L);
    BOOST_CHECK_EQUAL(XorAndSemiring<int64_t>().add(0L, 1L), 1L);
    BOOST_CHECK_EQUAL(XorAndSemiring<int64_t>().add(0L, 0L), 0L);
    BOOST_CHECK_EQUAL(XorAndSemiring<int64_t>().mult(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(XorAndSemiring<int64_t>().mult(-2L, 0L), 0L);
    BOOST_CHECK_EQUAL(XorAndSemiring<int64_t>().mult(0L, 1L), 0L);
    BOOST_CHECK_EQUAL(XorAndSemiring<int64_t>().mult(0L, 0L), 0L);

    BOOST_CHECK_EQUAL(XorAndSemiring<int32_t>().zero(), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int32_t>().add(-2, 1), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int32_t>().add(-2, 0), 1);
    BOOST_CHECK_EQUAL(XorAndSemiring<int32_t>().add(0, 1), 1);
    BOOST_CHECK_EQUAL(XorAndSemiring<int32_t>().add(0, 0), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int32_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(XorAndSemiring<int32_t>().mult(-2, 0), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int32_t>().mult(0, 1), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int32_t>().mult(0, 0), 0);

    BOOST_CHECK_EQUAL(XorAndSemiring<int16_t>().zero(), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int16_t>().add(-2, 1), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int16_t>().add(-2, 0), 1);
    BOOST_CHECK_EQUAL(XorAndSemiring<int16_t>().add(0, 1), 1);
    BOOST_CHECK_EQUAL(XorAndSemiring<int16_t>().add(0, 0), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int16_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(XorAndSemiring<int16_t>().mult(-2, 0), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int16_t>().mult(0, 1), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int16_t>().mult(0, 0), 0);

    BOOST_CHECK_EQUAL(XorAndSemiring<int8_t>().zero(), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int8_t>().add(-2, 1), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int8_t>().add(-2, 0), 1);
    BOOST_CHECK_EQUAL(XorAndSemiring<int8_t>().add(0, 1), 1);
    BOOST_CHECK_EQUAL(XorAndSemiring<int8_t>().add(0, 0), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int8_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(XorAndSemiring<int8_t>().mult(-2, 0), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int8_t>().mult(0, 1), 0);
    BOOST_CHECK_EQUAL(XorAndSemiring<int8_t>().mult(0, 0), 0);

    BOOST_CHECK_EQUAL(XorAndSemiring<bool>().zero(), false);
    BOOST_CHECK_EQUAL(XorAndSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(XorAndSemiring<bool>().add(false, true), true);
    BOOST_CHECK_EQUAL(XorAndSemiring<bool>().add(true, false), true);
    BOOST_CHECK_EQUAL(XorAndSemiring<bool>().add(true, true), false);
    BOOST_CHECK_EQUAL(XorAndSemiring<bool>().mult(false, false), false);
    BOOST_CHECK_EQUAL(XorAndSemiring<bool>().mult(false, true), false);
    BOOST_CHECK_EQUAL(XorAndSemiring<bool>().mult(true, false), false);
    BOOST_CHECK_EQUAL(XorAndSemiring<bool>().mult(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(xnor_or_semiring_test)
{
    BOOST_CHECK_EQUAL(XnorOrSemiring<double>().zero(), 1.0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<double>().mult(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<double>().mult(0., 1.), 1.0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<double>().mult(-2., 0.), 1.0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<double>().mult(0., 0.), 0.0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<double>().add(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<double>().add(0., 1.), 0.0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<double>().add(-2., 0.), 0.0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<double>().add(0., 0.), 1.0);

    BOOST_CHECK_EQUAL(XnorOrSemiring<float>().zero(), 1.0f);
    BOOST_CHECK_EQUAL(XnorOrSemiring<float>().mult(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(XnorOrSemiring<float>().mult(0.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(XnorOrSemiring<float>().mult(-2.f, 0.f), 1.0f);
    BOOST_CHECK_EQUAL(XnorOrSemiring<float>().mult(0.f, 0.f), 0.0f);
    BOOST_CHECK_EQUAL(XnorOrSemiring<float>().add(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(XnorOrSemiring<float>().add(0.f, 1.f), 0.0f);
    BOOST_CHECK_EQUAL(XnorOrSemiring<float>().add(-2.f, 0.f), 0.0f);
    BOOST_CHECK_EQUAL(XnorOrSemiring<float>().add(0.f, 0.f), 1.0f);

    BOOST_CHECK_EQUAL(XnorOrSemiring<uint64_t>().zero(), 1UL);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint64_t>().mult(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint64_t>().mult(2UL, 0UL), 1UL);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint64_t>().mult(0UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint64_t>().mult(0UL, 0UL), 0UL);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint64_t>().add(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint64_t>().add(2UL, 0UL), 0UL);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint64_t>().add(0UL, 1UL), 0UL);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint64_t>().add(0UL, 0UL), 1UL);

    BOOST_CHECK_EQUAL(XnorOrSemiring<uint32_t>().zero(), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint32_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint32_t>().mult(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint32_t>().mult(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint32_t>().mult(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint32_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint32_t>().add(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint32_t>().add(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint32_t>().add(0U, 0U), 1U);

    BOOST_CHECK_EQUAL(XnorOrSemiring<uint16_t>().zero(), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint16_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint16_t>().mult(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint16_t>().mult(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint16_t>().mult(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint16_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint16_t>().add(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint16_t>().add(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint16_t>().add(0U, 0U), 1U);

    BOOST_CHECK_EQUAL(XnorOrSemiring<uint8_t>().zero(), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint8_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint8_t>().mult(2U, 0U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint8_t>().mult(0U, 1U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint8_t>().mult(0U, 0U), 0U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint8_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint8_t>().add(2U, 0U), 0U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint8_t>().add(0U, 1U), 0U);
    BOOST_CHECK_EQUAL(XnorOrSemiring<uint8_t>().add(0U, 0U), 1U);

    BOOST_CHECK_EQUAL(XnorOrSemiring<int64_t>().zero(), 1L);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int64_t>().mult(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int64_t>().mult(-2L, 0L), 1L);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int64_t>().mult(0L, 1L), 1L);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int64_t>().mult(0L, 0L), 0L);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int64_t>().add(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int64_t>().add(-2L, 0L), 0L);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int64_t>().add(0L, 1L), 0L);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int64_t>().add(0L, 0L), 1L);

    BOOST_CHECK_EQUAL(XnorOrSemiring<int32_t>().zero(), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int32_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int32_t>().mult(-2, 0), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int32_t>().mult(0, 1), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int32_t>().mult(0, 0), 0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int32_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int32_t>().add(-2, 0), 0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int32_t>().add(0, 1), 0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int32_t>().add(0, 0), 1);

    BOOST_CHECK_EQUAL(XnorOrSemiring<int16_t>().zero(), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int16_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int16_t>().mult(-2, 0), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int16_t>().mult(0, 1), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int16_t>().mult(0, 0), 0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int16_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int16_t>().add(-2, 0), 0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int16_t>().add(0, 1), 0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int16_t>().add(0, 0), 1);

    BOOST_CHECK_EQUAL(XnorOrSemiring<int8_t>().zero(), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int8_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int8_t>().mult(-2, 0), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int8_t>().mult(0, 1), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int8_t>().mult(0, 0), 0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int8_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int8_t>().add(-2, 0), 0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int8_t>().add(0, 1), 0);
    BOOST_CHECK_EQUAL(XnorOrSemiring<int8_t>().add(0, 0), 1);

    BOOST_CHECK_EQUAL(XnorOrSemiring<bool>().zero(), true);
    BOOST_CHECK_EQUAL(XnorOrSemiring<bool>().mult(false, false), false);
    BOOST_CHECK_EQUAL(XnorOrSemiring<bool>().mult(false, true), true);
    BOOST_CHECK_EQUAL(XnorOrSemiring<bool>().mult(true, false), true);
    BOOST_CHECK_EQUAL(XnorOrSemiring<bool>().mult(true, true), true);
    BOOST_CHECK_EQUAL(XnorOrSemiring<bool>().add(false, false), true);
    BOOST_CHECK_EQUAL(XnorOrSemiring<bool>().add(false, true), false);
    BOOST_CHECK_EQUAL(XnorOrSemiring<bool>().add(true, false), false);
    BOOST_CHECK_EQUAL(XnorOrSemiring<bool>().add(true, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(min_first_test)
{
    BOOST_CHECK_EQUAL(MinFirstSemiring<double>().zero(),
                      std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MinFirstSemiring<double>().add(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(MinFirstSemiring<double>().add(2., 1.), 1.0);
    BOOST_CHECK_EQUAL(MinFirstSemiring<double>().mult(-2., 1.), -2.0);

    BOOST_CHECK_EQUAL(MinFirstSemiring<float>().zero(),
                      std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MinFirstSemiring<float>().add(-2.f, 1.f), -2.0f);
    BOOST_CHECK_EQUAL(MinFirstSemiring<float>().add(2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(MinFirstSemiring<float>().mult(-2.f, 1.f), -2.0f);

    BOOST_CHECK_EQUAL(MinFirstSemiring<uint64_t>().zero(),
                      std::numeric_limits<uint64_t>::max());
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint64_t>().add(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint64_t>().add(2UL, 3UL), 2UL);
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint64_t>().mult(2UL, 1UL), 2UL);

    BOOST_CHECK_EQUAL(MinFirstSemiring<uint32_t>().zero(),
                      std::numeric_limits<uint32_t>::max());
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint32_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint32_t>().add(2U, 3U), 2U);
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint32_t>().mult(2U, 1U), 2U);

    BOOST_CHECK_EQUAL(MinFirstSemiring<uint16_t>().zero(),
                      std::numeric_limits<uint16_t>::max());
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint16_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint16_t>().add(2U, 3U), 2U);
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint16_t>().mult(2U, 1U), 2U);

    BOOST_CHECK_EQUAL(MinFirstSemiring<uint8_t>().zero(),
                      std::numeric_limits<uint8_t>::max());
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint8_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint8_t>().add(2U, 3U), 2U);
    BOOST_CHECK_EQUAL(MinFirstSemiring<uint8_t>().mult(2U, 1U), 2U);

    BOOST_CHECK_EQUAL(MinFirstSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::max());
    BOOST_CHECK_EQUAL(MinFirstSemiring<int64_t>().add(-2L, 1L), -2L);
    BOOST_CHECK_EQUAL(MinFirstSemiring<int64_t>().add(2L, -1L), -1L);
    BOOST_CHECK_EQUAL(MinFirstSemiring<int64_t>().mult(-2L, 1L), -2L);

    BOOST_CHECK_EQUAL(MinFirstSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::max());
    BOOST_CHECK_EQUAL(MinFirstSemiring<int32_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinFirstSemiring<int32_t>().add(2, -1), -1);
    BOOST_CHECK_EQUAL(MinFirstSemiring<int32_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(MinFirstSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::max());
    BOOST_CHECK_EQUAL(MinFirstSemiring<int16_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinFirstSemiring<int16_t>().add(2, -1), -1);
    BOOST_CHECK_EQUAL(MinFirstSemiring<int16_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(MinFirstSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::max());
    BOOST_CHECK_EQUAL(MinFirstSemiring<int8_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinFirstSemiring<int8_t>().add(2, -1), -1);
    BOOST_CHECK_EQUAL(MinFirstSemiring<int8_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(MinFirstSemiring<bool>().zero(), true);
    BOOST_CHECK_EQUAL(MinFirstSemiring<bool>().add(false, true), false);
    BOOST_CHECK_EQUAL(MinFirstSemiring<bool>().add(true, true), true);
    BOOST_CHECK_EQUAL(MinFirstSemiring<bool>().mult(true, false), true);
    BOOST_CHECK_EQUAL(MinFirstSemiring<bool>().mult(false, true), false);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(min_second_test)
{
    BOOST_CHECK_EQUAL(MinSecondSemiring<double>().zero(),
                      std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MinSecondSemiring<double>().add(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(MinSecondSemiring<double>().add(2., 1.), 1.0);
    BOOST_CHECK_EQUAL(MinSecondSemiring<double>().mult(-2., 1.), 1.0);

    BOOST_CHECK_EQUAL(MinSecondSemiring<float>().zero(),
                      std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MinSecondSemiring<float>().add(-2.f, 1.f), -2.0f);
    BOOST_CHECK_EQUAL(MinSecondSemiring<float>().add(2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(MinSecondSemiring<float>().mult(-2.f, 1.f), 1.0f);

    BOOST_CHECK_EQUAL(MinSecondSemiring<uint64_t>().zero(),
                      std::numeric_limits<uint64_t>::max());
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint64_t>().add(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint64_t>().add(2UL, 3UL), 2UL);
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint64_t>().mult(2UL, 1UL), 1UL);

    BOOST_CHECK_EQUAL(MinSecondSemiring<uint32_t>().zero(),
                      std::numeric_limits<uint32_t>::max());
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint32_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint32_t>().add(2U, 3U), 2U);
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint32_t>().mult(2U, 1U), 1U);

    BOOST_CHECK_EQUAL(MinSecondSemiring<uint16_t>().zero(),
                      std::numeric_limits<uint16_t>::max());
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint16_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint16_t>().add(2U, 3U), 2U);
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint16_t>().mult(2U, 1U), 1U);

    BOOST_CHECK_EQUAL(MinSecondSemiring<uint8_t>().zero(),
                      std::numeric_limits<uint8_t>::max());
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint8_t>().add(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint8_t>().add(2U, 3U), 2U);
    BOOST_CHECK_EQUAL(MinSecondSemiring<uint8_t>().mult(2U, 1U), 1U);

    BOOST_CHECK_EQUAL(MinSecondSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::max());
    BOOST_CHECK_EQUAL(MinSecondSemiring<int64_t>().add(-2L, 1L), -2L);
    BOOST_CHECK_EQUAL(MinSecondSemiring<int64_t>().add(2L, -1L), -1L);
    BOOST_CHECK_EQUAL(MinSecondSemiring<int64_t>().mult(-2L, 1L), 1L);

    BOOST_CHECK_EQUAL(MinSecondSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::max());
    BOOST_CHECK_EQUAL(MinSecondSemiring<int32_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinSecondSemiring<int32_t>().add(2, -1), -1);
    BOOST_CHECK_EQUAL(MinSecondSemiring<int32_t>().mult(-2, 1), 1);

    BOOST_CHECK_EQUAL(MinSecondSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::max());
    BOOST_CHECK_EQUAL(MinSecondSemiring<int16_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinSecondSemiring<int16_t>().add(2, -1), -1);
    BOOST_CHECK_EQUAL(MinSecondSemiring<int16_t>().mult(-2, 1), 1);

    BOOST_CHECK_EQUAL(MinSecondSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::max());
    BOOST_CHECK_EQUAL(MinSecondSemiring<int8_t>().add(-2, 1), -2);
    BOOST_CHECK_EQUAL(MinSecondSemiring<int8_t>().add(2, -1), -1);
    BOOST_CHECK_EQUAL(MinSecondSemiring<int8_t>().mult(-2, 1), 1);

    BOOST_CHECK_EQUAL(MinSecondSemiring<bool>().zero(), true);
    BOOST_CHECK_EQUAL(MinSecondSemiring<bool>().add(false, true), false);
    BOOST_CHECK_EQUAL(MinSecondSemiring<bool>().add(true, true), true);
    BOOST_CHECK_EQUAL(MinSecondSemiring<bool>().mult(true, false), false);
    BOOST_CHECK_EQUAL(MinSecondSemiring<bool>().mult(false, true), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(max_first_test)
{
    BOOST_CHECK_EQUAL(MaxFirstSemiring<double>().zero(),
                      -std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MaxFirstSemiring<double>().add(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<double>().mult(-2., 1.), -2.0);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<float>().zero(),
                      -std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MaxFirstSemiring<float>().add(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<float>().mult(-2.f, 1.f), -2.0f);

    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint64_t>().zero(), 0UL);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint64_t>().add(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint64_t>().mult(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint32_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint32_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint32_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint16_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint16_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint16_t>().mult(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint8_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint8_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<uint8_t>().mult(2U, 1U), 2U);

    BOOST_CHECK_EQUAL(MaxFirstSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::min());
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int64_t>().add(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int64_t>().mult(-2L, 1L), -2L);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::min());
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int32_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int32_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::min());
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int16_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int16_t>().mult(-2, 1), -2);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::min());
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int8_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<int8_t>().mult(-2, 1), -2);

    BOOST_CHECK_EQUAL(MaxFirstSemiring<bool>().zero(), false);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<bool>().add(false, true), true);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<bool>().mult(false, true), false);
    BOOST_CHECK_EQUAL(MaxFirstSemiring<bool>().mult(true, false), true);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(max_second_test)
{
    BOOST_CHECK_EQUAL(MaxSecondSemiring<double>().zero(),
                      -std::numeric_limits<double>::infinity());
    BOOST_CHECK_EQUAL(MaxSecondSemiring<double>().add(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<double>().mult(-2., 1.), 1.0);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<float>().zero(),
                      -std::numeric_limits<float>::infinity());
    BOOST_CHECK_EQUAL(MaxSecondSemiring<float>().add(-2.f, 1.f), 1.0f);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<float>().mult(-2.f, 1.f), 1.0f);

    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint64_t>().zero(), 0UL);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint64_t>().add(2UL, 1UL), 2UL);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint64_t>().mult(2UL, 1UL), 1UL);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint32_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint32_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint32_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint16_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint16_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint16_t>().mult(2U, 1U), 1U);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint8_t>().zero(), 0U);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint8_t>().add(2U, 1U), 2U);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<uint8_t>().mult(2U, 1U), 1U);

    BOOST_CHECK_EQUAL(MaxSecondSemiring<int64_t>().zero(),
                      std::numeric_limits<int64_t>::min());
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int64_t>().add(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int64_t>().mult(-2L, 1L), 1L);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int32_t>().zero(),
                      std::numeric_limits<int32_t>::min());
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int32_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int32_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int16_t>().zero(),
                      std::numeric_limits<int16_t>::min());
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int16_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int16_t>().mult(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int8_t>().zero(),
                      std::numeric_limits<int8_t>::min());
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int8_t>().add(-2, 1), 1);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<int8_t>().mult(-2, 1), 1);

    BOOST_CHECK_EQUAL(MaxSecondSemiring<bool>().zero(), false);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<bool>().add(false, false), false);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<bool>().add(false, true), true);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<bool>().mult(false, true), true);
    BOOST_CHECK_EQUAL(MaxSecondSemiring<bool>().mult(true, false), false);
}

BOOST_AUTO_TEST_SUITE_END()


/***********************************************************************
This file is part of the librjmcmc project source files.

Copyright : Institut Geographique National (2008-2012)
Contributors : Mathieu Brédif, Olivier Tournaire, Didier Boldo
email : librjmcmc@ign.fr

This software is a generic C++ library for stochastic optimization.

This software is governed by the CeCILL license under French law and
abiding by the rules of distribution of free software. You can use,
modify and/or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info".

As a counterpart to the access to the source code and rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty and the software's author, the holder of the
economic rights, and the successive licensors have only limited liability.

In this respect, the user's attention is drawn to the risks associated
with loading, using, modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean that it is complicated to manipulate, and that also
therefore means that it is reserved for developers and experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and, more generally, to use and operate it in the
same conditions as regards security.

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.

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

#ifndef UNIFORM_DISTRIBUTION_HPP
#define UNIFORM_DISTRIBUTION_HPP

#include <boost/random/uniform_smallint.hpp>
// boost::math::uniform is not used as it is linked to real values rather than discrete integral values

namespace rjmcmc {

    // 1_(n\in[a,b])/(b-a+1)
    class uniform_distribution {
    public:
        typedef double real_type;
        typedef int    int_type;
        typedef boost::uniform_smallint<int_type> rand_distribution_type;

        uniform_distribution(int_type a, int_type b)
            : m_rand(a,b)
            , m_pdf(real_type(1)/(b-a+1)) {}

        // new/old
        real_type pdf_ratio(int_type n0, int_type n1) const
        {
            assert(pdf(n0)>0);
            return (m_variate.distribution().min() <= n1 && n1 <= m_variate.distribution().max() );
        }

        real_type pdf(int_type n) const
        {
            return m_pdf * (m_variate.distribution().min() <= n && n <= m_variate.distribution().max() );
        }

        template<typename Engine>
        inline int_type operator()(Engine& e) const { return m_rand(e); }

    private:
        mutable rand_distribution_type m_rand;
        real_type m_pdf;
    };

}; // namespace rjmcmc

#endif // UNIFORM_DISTRIBUTION_HPP


#pragma once

#include <Eigen/Core>
#include <array>

namespace Discregrid
{

    enum class NearestEntity
    {
        VN0,
        VN1,
        VN2,
        EN0,
        EN1,
        EN2,
        FN
    };

    float point_triangle_sqdistance(Eigen::Vector3f const &point,
                                    std::array<Eigen::Vector3f const *, 3> const &triangle,
                                    Eigen::Vector3f *nearest_point = nullptr,
                                    NearestEntity *ne = nullptr);

}


/**
 * @file upwindquadrature_main.cc
 * @brief Convergence study for 3 methods to solve CD BVP
 * @author Philippe Peter
 * @date July 2020
 * @copyright Developed at SAM, ETH Zurich
 */
#include <lf/fe/fe.h>
#include <lf/mesh/hybrid2d/hybrid2d.h>
#include <lf/mesh/mesh.h>
#include <lf/mesh/utils/utils.h>
#include <lf/refinement/refinement.h>
#include <lf/uscalfe/uscalfe.h>

#include <Eigen/Core>
#include <cmath>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>

#include "cd_tools.h"
#include "standard_fem.h"
#include "supg.h"
#include "upwind.h"

int main() {
  // parameter functions:
  // velocity field
  const Eigen::Vector2d v(2.0, 3.0);
  const auto velocity = [&v](const Eigen::Vector2d& x) { return v; };
  // diffusion coefficient
  const double eps = 1.0;
  const auto epsilon = [&eps](const Eigen::Vector2d& x) { return eps; };
  // exact solution:
  const auto u_exact = [&eps](const Eigen::Vector2d& x) {
    return x(0) * x(1) * x(1) - x(1) * x(1) * std::exp(2 * (x(0) - 1) / eps) -
           x(0) * std::exp(3 * (x(1) - 1) / eps) +
           std::exp(2 * (x(0) - 1) / eps + 3 * (x(1) - 1) / eps);
  };
  // gradient of the exact solution
  const auto u_grad_exact = [&eps](const Eigen::Vector2d& x) {
    Eigen::Vector2d res;
    res(0) = x(1) * x(1) -
             x(1) * x(1) * 2.0 / eps * std::exp(2 * (x(0) - 1) / eps) -
             std::exp(3 * (x(1) - 1) / eps) +
             2.0 / eps * std::exp(2 * (x(0) - 1) / eps + 3 * (x(1) - 1) / eps);
    res(1) = 2 * x(0) * x(1) - 2 * x(1) * std::exp(2 * (x(0) - 1) / eps) -
             x(0) * 3.0 / eps * std::exp(3 * (x(1) - 1) / eps) +
             3.0 / eps * std::exp(2 * (x(0) - 1) / eps + 3 * (x(1) - 1) / eps);
    return res;
  };
  // Laplacian of the exact solution
  const auto u_laplace_exact = [&eps](const Eigen::Vector2d& x) {
    double uxx =
        -4.0 / (eps * eps) * x(1) * x(1) * std::exp(2 * (x(0) - 1) / eps) +
        4.0 / (eps * eps) *
            std::exp(2 * (x(0) - 1) / eps + 3 * (x(1) - 1) / eps);
    double uyy = 2.0 * x(0) - 2.0 * std::exp(2 * (x(0) - 1) / eps) -
                 x(0) * 9.0 / (eps * eps) * std::exp(3 * (x(1) - 1) / eps) +
                 9.0 / (eps * eps) *
                     std::exp(2 * (x(0) - 1) / eps + 3 * (x(1) - 1) / eps);
    return uxx + uyy;
  };
  // source function
  const auto f = [&eps, &v, &u_grad_exact,
                  &u_laplace_exact](const Eigen::Vector2d& x) {
    return -eps * u_laplace_exact(x) + v.transpose() * u_grad_exact(x);
  };

  // boundary conditions
  const auto g = [&u_exact](const Eigen::Vector2d& x) { return u_exact(x); };

  // Construct mesh hierarchy:
  std::unique_ptr<lf::mesh::MeshFactory> top_mesh_factory_ptr =
      std::make_unique<lf::mesh::hybrid2d::MeshFactory>(2);
  lf::mesh::utils::TPTriagMeshBuilder builder(std::move(top_mesh_factory_ptr));
  builder.setBottomLeftCorner(Eigen::Vector2d{0.0, 0.0})
      .setTopRightCorner(Eigen::Vector2d{1.0, 1.0})
      .setNumXCells(2)
      .setNumYCells(2);
  std::shared_ptr<lf::mesh::Mesh> top_mesh = builder.Build();

  std::shared_ptr<lf::refinement::MeshHierarchy> multi_mesh_p =
      lf::refinement::GenerateMeshHierarchyByUniformRefinemnt(top_mesh, 6);
  lf::refinement::MeshHierarchy& multi_mesh{*multi_mesh_p};
  multi_mesh.PrintInfo(std::cout);

  // get number of levels:
  unsigned L = multi_mesh.NumLevels();

  // Output file
  std::string file_name = "results_errors.txt";
  std::ofstream file;
  file.open(file_name);
  file << "h, $L^2$-Error u (FEM), $L^2$-Error u (Upwind), $L^2$-Error u "
          "(SUPG) \n";

  // Perform computations on all levels and compute  L2-errors
  for (unsigned l = 0; l < L; ++l) {
    // extract mesh and construct FE space.
    std::shared_ptr<const lf::mesh::Mesh> mesh_p{multi_mesh.getMesh(l)};
    auto fe_space =
        std::make_shared<lf::uscalfe::FeSpaceLagrangeO1<double>>(mesh_p);

    // compute solutions using Standard FE, Upwind, SUPG method
    Eigen::VectorXd sol_standard = ConvectionDiffusion::SolveCDBVPStandardFem(
        fe_space, epsilon, velocity, f, g);
    lf::fe::MeshFunctionFE sol_standard_mf(fe_space, sol_standard);

    Eigen::VectorXd sol_stable = ConvectionDiffusion::SolveCDBVPUpwind(
        fe_space, epsilon, velocity, f, g);
    lf::fe::MeshFunctionFE sol_upwind_mf(fe_space, sol_stable);

    Eigen::VectorXd sol_supg =
        ConvectionDiffusion::SolveCDBVPSupg(fe_space, epsilon, velocity, f, g);
    lf::fe::MeshFunctionFE sol_supg_mf(fe_space, sol_supg);

    // Wrap exact solution into mesh function for error computations
    auto mf_solution = lf::mesh::utils::MeshFunctionGlobal(u_exact);

    // Calculate L2 errors:
    double L2err_standard = std::sqrt(lf::fe::IntegrateMeshFunction(
        *mesh_p, lf::mesh::utils::squaredNorm(sol_standard_mf - mf_solution),
        10));
    double L2err_upwind = std::sqrt(lf::fe::IntegrateMeshFunction(
        *mesh_p, lf::mesh::utils::squaredNorm(sol_upwind_mf - mf_solution),
        10));
    double L2err_supg = std::sqrt(lf::fe::IntegrateMeshFunction(
        *mesh_p, lf::mesh::utils::squaredNorm(sol_supg_mf - mf_solution), 10));

    // output
    std::cout << "Level " << l
              << "(h= " << ConvectionDiffusion::MeshWidth(mesh_p)
              << "): " << L2err_standard << ", " << L2err_upwind << ", "
              << L2err_supg << std::endl;
    file << ConvectionDiffusion::MeshWidth(mesh_p) << ", " << L2err_standard
         << ", " << L2err_upwind << ", " << L2err_supg << std::endl;
  }
  file.close();

  // Plot
  std::system("python3 " CURRENT_SOURCE_DIR
              "/plot_convergence.py " CURRENT_BINARY_DIR);

  return 0;
}


#include "advent.hpp"

#include <fstream>
#include <gsl/gsl_util>
#include <iostream>
#include <scn/scn.h>
#include <Eigen/Dense>

using board = Eigen::Array<int, -1, -1>;

auto day04(int argc, char** argv) -> int
{
    if (argc < 2) {
        fmt::print("Please provide an input file.\n");
        return 1;
    }

    std::ifstream infile(argv[1]); // NOLINT
    std::string line;

    // the first line represents the list of input numbers
    std::getline(infile, line);
    std::vector<int> numbers;
    auto res = scn::scan_list(line, numbers, ',');
    ENSURE(res);

    // the remainder of the file contains the board configurations
    int rows = 0;
    std::vector<std::vector<int>> values;
    std::vector<board> boards;

    while(std::getline(infile, line)) {
        if (line.empty()) {
            if (rows > 0) {
                boards.emplace_back(rows, values.front().size());
                auto& b = boards.back();
                for (size_t i = 0; i < values.size(); ++i) {
                    EXPECT(values[i].size() == b.rows());
                    b.row(gsl::narrow<Eigen::Index>(i)) = Eigen::Map<Eigen::Array<int, -1, 1>>(values[i].data(), values[i].size());
                }
                rows = 0;
                values.clear();
                //std::cout << b << "\n\n";
            }
            continue;
        }
        std::vector<int> vec;
        auto res = scn::scan_list(line, vec, ' ');
        ENSURE(res);
        values.push_back(vec);
        ++rows;
    }

    // part 1
    std::vector<bool> bingoed(boards.size(), false);
    for (auto n : numbers) {
        size_t idx{0};
        for (auto& b : boards) {
            for (int i = 0; i < b.cols(); ++i) {
                for (int j = 0; j < b.rows(); ++j) {
                    if (bingoed[idx]) {
                        continue;
                    }

                    if (n == b(i, j)) {
                        b(i, j) = -1;
                    }

                    if (
                            (i == j && (b.matrix().diagonal().array() < 0).all()) // bingo on the main diagonal
                            || ((i == b.rows() - j - 1 || j == b.rows() - i - 1) && (b.matrix().transpose().diagonal().array() < 0).all()) // bingo on the other diagonal
                            || (b.col(i) < 0).all() // column bingo
                            || (b.row(j) < 0).all() // row bingo
                       ) {
                        auto sum = (b < 0).select(0, b).sum();
                        auto score = sum * n;
                        fmt::print("bingo at board {}! n: {}, sum: {}, score: {}\n", idx, n, sum, score);
                        std::cout << b << "\n\n";
                        bingoed[idx] = true;
                    }
                }
            }
            ++idx;
        }
    }
    return 0;
}


#include "Srobotconfig.h"
#include <Eigen/Dense>
#include <iostream>
#include <vector>

using namespace std;

namespace SRobot
{
    SCARA Scara(250,250);
	//初始化TransMatrix
	double mTransMatrix[16]={0};
    double mangle[4]={0};
    double mpose[6]={0};

	//只使用一种姿态
	bool mConfig = 1;

    Matrix3d skew(Vector3d w)
    {
        Matrix3d so3;
        so3 << 0, -w(2), w(1),
            w(2), 0, -w(0),
            -w(1), w(0), 0;
        return so3;
    }

    Matrix4d R2SE3(Vector3d v, Vector3d w, int i, double theta)
    {
        Matrix4d SE3 = Matrix4d::Identity();
        if (i == 2)
        {
            SE3(2,3) = -theta;
        }
        else
        {
            theta = PI * theta / 180;
            Matrix3d so3 = skew(w);
            Matrix3d SO3 = Matrix3d::Identity() + so3 * sin(theta) + so3 * so3 * (1 - cos(theta));  //Rodrigues Formula
            Vector3d p = (Matrix3d::Identity() - SO3) * skew(w) * v + w * w.transpose() * v * theta;
            SE3.block<3,3>(0,0) = SO3;
            SE3.block<3,1>(0,3) = p;
        }
        return SE3;
    }

    Matrix4d GetInverse(Matrix4d T)
    {
        Matrix4d T_inv = Matrix4d::Identity();
        T_inv.block<3,3>(0,0) = T.block<3,3>(0,0).transpose();
        T_inv.block<3,1>(0,3) = -T.block<3,3>(0,0).transpose() * T.block<3,1>(0,3);
        return T_inv;
    }

    void Subproblem1 (Vector3d r, Vector3d p, Vector3d q, Vector3d w, double &theta)
    {
        Vector3d u,v,u_dot,v_dot;
        u = p - r;
        v = q - r;

        u_dot = u - w * w.transpose() * u;
        v_dot = v - w * w.transpose() * v;

        theta = atan2(w.transpose()*(u_dot.cross(v_dot)),u_dot.transpose()*v_dot) * 180 / PI;
    }

//     //SCRAR没有用 写一下当练习
    void Subproblem2 (Vector3d r, Vector3d p, Vector3d q, Vector3d w1, Vector3d w2, vector<double> &theta1, vector<double> &theta2 )
    {
        Vector3d u,v;
        u = p - r;
        v = q - r;

        double w12 = w1.transpose()*w2;
        double w2u = w2.transpose()*u;
        double w1v = w1.transpose()*v;
        double alpha = (w12*w2u - w1v) / (w12*w12 -1);
        double beta = (w12*w1v-w2u) / (w12*w12 -1);

        double u_norm_2 = u.transpose()*u;
        Vector3d w1_cross_w2 = w1.cross(w2);
        double Gamma_2 = (u_norm_2 - alpha*alpha -beta*beta -2*alpha*beta*w12) / (w1_cross_w2).dot(w1_cross_w2);

        if(Gamma_2>=0){
            double Gamma = sqrt(Gamma_2);
            Vector3d z1 = alpha * w1 + beta * w2 + Gamma * w1_cross_w2;
            Vector3d z2 = alpha * w1 + beta * w2 - Gamma * w1_cross_w2;
            Vector3d c1 = z1 + r;
            Vector3d c2 = z2 + r;

            double theta1_temp, theta2_temp;
            Subproblem1(r, q, c1, w1, theta1_temp);
            theta1.push_back(-theta1_temp);
            Subproblem1(r, q, c2, w1, theta1_temp);
            theta1.push_back(-theta1_temp);

            Subproblem1(r, p, c1, w2, theta2_temp);
            theta2.push_back(theta2_temp);
            Subproblem1(r, p, c2, w2, theta2_temp);
            theta2.push_back(theta2_temp);
        }
    }
   void Subproblem3 (Vector3d r, Vector3d p, Vector3d q, Vector3d w, double delta, vector<double> &theta)
    {
        Vector3d u,v,u_dot,v_dot;
        u = p - r;
        v = q - r;
        u_dot = u - w * w.transpose() * u;
        v_dot = v - w * w.transpose() * v;
        double delta_dot_2 = delta*delta - (w.dot(p-q))*(w.dot(p-q));   //maybe bug

        double theta0 = atan2(w.dot(u_dot.cross(v_dot)),u_dot.dot(v_dot));

        double Phi = (u_dot.dot(u_dot) + v_dot.dot(v_dot) -delta_dot_2)/sqrt(4*u_dot.dot(u_dot)*v_dot.dot(v_dot));
        if (abs(Phi) <= 1){
            theta.push_back((theta0+acos(Phi))/PI*180);
            theta.push_back((theta0-acos(Phi))/PI*180);
        }
    }

	void SetRobotEndPos(double x, double y, double z, double yaw, double pitch, double roll)
	{
        mpose[0] = x;
        mpose[1] = y;
        mpose[2] = z;
        mpose[3] = yaw/180*PI;
        mpose[4] = pitch/180*PI;
        mpose[5] = roll/180*PI;

        Eigen::AngleAxisd yawAngle(AngleAxisd(mpose[3],Vector3d::UnitZ()));
        Eigen::AngleAxisd pitchAngle(AngleAxisd(mpose[4],Vector3d::UnitY()));
        Eigen::AngleAxisd rollAngle(AngleAxisd(mpose[5],Vector3d::UnitZ()));
        Matrix3d rotate;
        rotate = yawAngle*pitchAngle*rollAngle;
        Matrix4d T = Matrix4d::Identity();
        T.block<3,3>(0,0) = rotate;
        T(0,3) = x;
        T(1,3) = y;
        T(2,3) = z;

        for (int i=0; i<4; i++){
            for(int j=0; j<4; j++){
                mTransMatrix[4*i+j] = T(i,j);
            }
        }
	}

	void GetJointAngles(double &angle1, double &angle2, double &angle3, double &angle4)
	{
        robotBackward(mTransMatrix, mConfig, mangle);
        angle1 = mangle[0];
        angle2 = mangle[1];
        angle3 = mangle[2];
        angle4 = mangle[3];
	}

	void SetRobotJoint(double angle1, double angle2, double angle3, double angle4)
	{
        mangle[0] = angle1;
        mangle[1] = angle2;
        mangle[2] = angle3;
        mangle[3] = angle4;
	}

	void GetJointEndPos(double &x, double &y, double &z, double &yaw, double &pitch, double &roll)
	{
        Matrix4d T;
        Matrix3d R;
        Vector3d Euler;
        robotForward(mangle, mTransMatrix, mConfig);
        for (int i=0; i<4; i++){
            for(int j=0; j<4; j++){
                T(i,j) = mTransMatrix[4*i+j];
            }
        }
        x = T(0,3);
        y = T(1,3);
        z = T(2,3);
        R = T.block<3,3>(0,0);
        Euler = R.eulerAngles(2,1,2);
        yaw = Euler(0)/PI*180;
        pitch = Euler(1)/PI*180;
        roll = Euler(2)/PI*180;
	}

	/********************************************************************
	ABSTRACT:	机器人逆运动学

	INPUTS:		T[16]:	位姿矩阵，其中长度距离为米

				config：姿态，六轴机器人对应有8种姿态（即对应的逆运动学8个解），
				Scara机器人有2个解，Delta机器人有一个解，
				为了安全，实验室中我们只计算一种即可。config用来作为选解的标志数。

	OUTPUTS:    theta[6] 6个关节角, 单位为弧度

	RETURN:		<none>
	***********************************************************************/
	void robotBackward(const double* TransVector, bool mconfig, double* theta)
	{
        //get angle3
		theta[2] = -TransVector[11];

        Matrix4d G, T;
        for (int i=0; i<4; i++){
            for(int j=0; j<4; j++){
                T(i,j) = TransVector[4*i+j];
            }
        }
        G = T*GetInverse(Scara.G0);
        //Subproblem3 to get angle 2
        Matrix4d T3 = Matrix4d::Identity();
        T3(2,3) = theta[2];
        Vector4d q3(500,0,0,1);
        Vector4d q1(0,0,0,1);

        double delta = (G*q3).norm();
        Vector4d p = T3*q3;
        Vector4d q2(250,0,0,1);
        vector<double> angle2;
        Subproblem3(q2.head(3),p.head(3),q1.head(3),Scara.w.col(1),delta,angle2);
        theta[1] = angle2[mconfig];
        //Subproblem1 to get angle 1
        Matrix4d T2 = R2SE3(Scara.v.col(1),Scara.w.col(1), 1, theta[1]);
        Vector4d q = G*q3;
        p =T2*T3*q3;
        Subproblem1(q1.head(3),p.head(3),q.head(3),Scara.w.col(0),theta[0]);
        //Subproblem1 to get angle 4
        Matrix4d T1 = R2SE3(Scara.v.col(0),Scara.w.col(0), 0, theta[0]);
        q = GetInverse(T3)*GetInverse(T2)*GetInverse(T1)*G*q1;
        p = q1;
        Subproblem1(q3.head(3),p.head(3),q.head(3),Scara.w.col(3),theta[3]);
	}

	/********************************************************************
	ABSTRACT:	机器人正运动学

	INPUTS:		q[6]: 6个关节角, 单位为弧度

	OUTPUTS:	config用来作为选解的标志数。

				TransVector[16] : 刚体变换矩阵，也就是末端的位姿描述，其中长度距离为米

	RETURN:		<none>
	***********************************************************************/
	void robotForward(const double* q, double* TransVector, bool mconfig)
	{
        Matrix4d T=Matrix4d::Identity();
        cout<<endl;
		for(int i=0;i<4;i++){
            T = T * R2SE3(Scara.v.col(i),Scara.w.col(i),i,q[i]);
        }
        T = T*Scara.G0;
        for (int i=0; i<4; i++){
            for(int j=0; j<4; j++){
                TransVector[4*i+j] = T(i,j);
            }
        }
	}
}


#include <mex.h>
#include "armaMex.hpp"
#include <armadillo>
#include <boost/math/special_functions/expint.hpp>
#include <boost/math/special_functions/trigamma.hpp>
#define POSITIVE_EPS 0.0001
#define epsilon1 1e-30
#define epsilon2 1e-7

double Lentz_Algorithm(double const x)
{
    double f_prev = epsilon1, C_prev = epsilon1, D_prev = 0, delta = 2+epsilon2, D_curr, C_curr, f_curr;
    double j = 1.0, tmp1, tmp2;
    while (delta-1>=epsilon2 || 1-delta >= epsilon2)
    {
        j++;
        tmp1 = x+2*j-1;
        tmp2 = pow(j-1,2);
        D_curr = 1/(tmp1-tmp2*D_prev);
        C_curr = tmp1-tmp2/C_prev;
        delta = C_curr*D_curr;
        f_curr = f_prev*delta;
        f_prev = f_curr;
        C_prev = C_curr;
        D_prev = D_curr;
    }
    return 1/(x+1+f_curr);
}

void mexFunction(int nlhs,mxArray *plhs[],int nrhs, const mxArray *prhs[])
{
    /* Input Interface*/
    mat XDat = armaGetPr(prhs[0]);
    uword maxIter, s, p = XDat.n_cols,n = XDat.n_rows;
    double tol, eta, r;

    if (nrhs>1)
    {
        eta = armaGetDouble(prhs[1]);
        eta = eta/(p+eta);
    }
    else eta = 300.0/(p+300.0);//0.05; //4*sqrt(double(p)/(p^2-1));

    if (nrhs>2) maxIter = (uword)armaGetDouble(prhs[2]);
    else maxIter = 10000;
    if (nrhs>3) tol = armaGetDouble(prhs[3]);
    else tol = 0.01;

    if (nrhs>4) r = armaGetDouble(prhs[4]);
    else
    {
        r = 0.5; //(1-400.0/double(p))/(1+400.0/double(p));
        //if(r<0.05) r = 0.05;
    }
    if (nrhs>5) s = (uword)armaGetDouble(prhs[5]);
    else s = round((double)p/(1e-3*(p-1)+1)); //(pow((double)p,1.5)/(5e-2*(p-1)+sqrt(double(p))));

	mat nS = XDat.t()*XDat;

    /* Initialization */
    auto t_start = std::chrono::high_resolution_clock::now();
	vec dnS = nS.diag();
    uword pe = p*(p-1)/2;

    uvec idl(pe);
    uvec idu(pe);
    uvec idr(pe);
    uvec idc(pe);
    uword k = 0, i, j, jp, kappa;
    for (j = 0,jp = 0;j<p;j++,jp+=p)
    {
        for (i=j+1;i<p;i++)
        {
            idl(k) = jp+i;
            idu(k) = i*p+j;
            idr(k) = i;
            idc(k) = j;
            k++;
        }

    }

    double psr = (double)p/s;
    uvec idp(p), id0(s), idd;
    vec nd2pp = (double)n/2+linspace<vec>(p,1,p);

    mat ML(p,p,fill::eye);
    mat ML2(p,p,fill::eye);
    mat ML2pVL(p,p,fill::eye);
    mat VL(p,p,fill::zeros);
    mat LAMBDA(p,p,fill::zeros);

    vec h(pe,fill::zeros),zeta(pe),mL(pe,fill::zeros);
    zeta.fill(10.0);

    vec alpha(p,fill::ones);
    vec beta(p,fill::ones);

    double a = (double)pe/2, b = a/50;

    vec mLold = h/zeta;
    vec lambdaold(pe,fill::ones);

    mat VL2(p,p,fill::zeros);
    for (i=1;i<p;i++)
    {
        VL2(span(i,p-1),i) += i;
        VL2(i,span(i,p-1)) += i;
    }
	VL2 *= 0.01;

	vec d(pe,1);
	d.fill(0.5);

    vec mD, mD2, mD2pvD, vD, vL, lambda(pe), gmL, gvL, gmD, gvD, c5(p), c6, mLnew, lambdanew, alphatmp, betatmp, dtmp;//mL,
    mat c1, c2, c3, c4, LDL, c2pc3, K_tmp1, K_tmp2;
    double omega, difmL, diflambda, difmax;
    vec d1h(pe,fill::zeros), d1zeta(pe,fill::zeros), d1alpha(p,fill::zeros), d1beta(p,fill::zeros), d1d(pe,fill::zeros);
    double d2h = 0, d2zeta = 0, d2alpha = 0, d2beta = 0, d2d = 0, d1b = 0, d2b = 0;
    double tau = 6e2; //, tauzeta = tauh, taualpha = tauh, taubeta = tauh, taub = tauh, taud = tauh;
    double rho, gb, btmp;//1/eta;rho_ub, c7=(5*eta>0.25)?0.25:5*eta,
    vec gh, gzeta, galpha, gbeta, gd;
    arma_rng::set_seed(0);

    /* KL proximal variational inference */
    mexPrintf("Start Running BISN ...\n");
    for (kappa=1;kappa<=maxIter;kappa++)
    {
        mD = alpha/beta;
        mD2 = square(mD);
        vD = mD/beta;
        mD2pvD = mD2+vD;

        vL = 1/zeta;
        VL.elem(idl) = vL;
        mL =  h%vL;
        ML.elem(idl) = mL;
        ML2.elem(idl) = square(mL);
        ML2pVL.elem(idl) = ML2.elem(idl)+vL;

        omega = a/b;
        lambda = d;
        lambda.transform([](double val){return (val > 10) ? Lentz_Algorithm(val) : (- boost::math::expint(-val) * exp(val));});
        lambda = 1/(d%lambda)-1;
        LAMBDA.elem(idl) = omega*lambda;
        LAMBDA.elem(idu) = LAMBDA.elem(idl);

        if (kappa==1)
        {
            c1 = nS;
            c2 = -VL;
            c2.each_row() += sum(VL);
            c2 *= LAMBDA(1);
            //c2 = LAMBDA(1)*(repmat(sum(VL),p,1)-VL);
            c3 = LAMBDA;
            c4 = (VL+VL2)*mD2pvD(0);
			VL2.clear();
        }
        else
        {
            K_tmp1 = ML.rows(id0);
            K_tmp1.each_row() %= mD.t();
            LDL = K_tmp1*ML.t();
            c1 *= r;
            c1.rows(id0) += psr*(nS.rows(id0)+LDL%LAMBDA.rows(id0))*ML;

            c2 *= r;
            c2.rows(id0) += psr*LAMBDA.rows(id0)*VL;

            c3 *= r;
            c3.rows(id0) += psr*LAMBDA.rows(id0)*ML2;

            K_tmp1 = ML2pVL.rows(id0);
            K_tmp1.each_row() %= mD2pvD.t();
            K_tmp2 = ML2.rows(id0);
            K_tmp2.each_row() %= mD2.t();
            c4 *=r;
            c4.rows(id0) += psr*(K_tmp1*ML2pVL.t()-K_tmp2*ML2.t()+square(LDL));
        }

        c2pc3 = c2+c3;
        gmL = -c1.elem(idl)%mD.elem(idc)-ML.elem(idl)%(mD2pvD.elem(idc)%c2.elem(idl)+vD.elem(idc)%c3.elem(idl));
        gvL = dnS.elem(idr)%mD.elem(idc)+c2pc3.elem(idl)%mD2pvD.elem(idc);
        gh = gmL+mL%gvL - h;
        gvL.elem(find(gvL<0)).zeros();
        gzeta = gvL - zeta;
        d1h = (1-1/tau)*d1h+gh/tau;
        d2h = (1-1/tau)*d2h+mean(square(gh))/tau;
        d1zeta = (1-1/tau)*d1zeta + gzeta/tau;
        d2zeta = (1-1/tau)*d2zeta + mean(square(gzeta))/tau;

        gmD = (VL.t()*dnS+trans(sum(ML%c1))+trans(sum(VL%(c2pc3+c3)))%mD)/2;
        gvD = trans(sum(ML2pVL%c2pc3))/4;;
        c5 = alpha;
        c5.transform([](double val) { return boost::math::trigamma(val); });
        c6 = mD/(alpha%c5-1);
        alphatmp = nd2pp+c6/beta%gvD;
        alphatmp.elem(find(alphatmp<0)).zeros();
        betatmp = gmD+(1/beta+c5%c6)%gvD;
        betatmp.elem(find(betatmp<0)).zeros();
        galpha = alphatmp - alpha;
        gbeta = betatmp - beta;
        d1alpha = (1-1/tau)*d1alpha + galpha/tau;
        d2alpha = (1-1/tau)*d2alpha + mean(square(galpha))/tau;
        d1beta = (1-1/tau)*d1beta + gbeta/tau;
        d2beta = (1-1/tau)*d2beta + mean(square(gbeta))/tau;

        dtmp = omega/2*c4.elem(idl);
        dtmp.elem(find(dtmp<0)).zeros();
        gd = dtmp - d;
        d1d = (1-1/tau)*d1d + gd/tau;
        d2d = (1-1/tau)*d2d + mean(square(gd))/tau;

        btmp = sum(lambda%c4.elem(idl))/2;
        if (btmp<0) btmp = 0;
        gb = btmp - b;
        d1b = (1-1/tau)*d1b + gb/tau;
        d2b = (1-1/tau)*d2b + pow(gb,2)/tau;

        rho = (mean(square(d1h))+mean(square(d1zeta))+p/pe*(mean(square(d1alpha))+mean(square(d1beta)))+mean(square(d1d))+pow(d1b,2)/pe)/(d2h+d2zeta+p/pe*(d2alpha+d2beta)+d2d+d2b/pe);
        if (rho>eta) rho = eta;

        tau = (1-rho)*tau + 1;
        h += rho*gh;
        zeta += rho*gzeta;
        alpha += rho*galpha;
        beta += rho*gbeta;
        d += rho*gd;
        b += rho*gb;

        if (kappa%100 == 0)
        {
            mLnew = h/zeta;
            lambdanew = lambda;
            difmL = sqrt(mean(square(mLnew-mLold))/mean(square(mLold)));
            diflambda = max(abs(lambdanew-lambdaold));
            difmax = max(abs(mLnew-mLold));
            mexPrintf("#no. of iterations = %d, difmL = %f, difmax = %f, diflambda = %f\n",kappa,difmL,difmax,diflambda);
            mexEvalString("drawnow;");
            if (difmL<tol)
                break;
            else
            {
                mLold = mLnew;
                lambdaold = lambdanew;
            }
        }

        id0 = randperm(p, s);

        K_tmp1 = ML.rows(id0);
        K_tmp1.each_row() %= mD.t();
        LDL = K_tmp1*ML.t();
        c1.rows(id0) -= psr*(nS.rows(id0)+LDL%LAMBDA.rows(id0))*ML;
        c2.rows(id0) -= psr*LAMBDA.rows(id0)*VL;
        c3.rows(id0) -= psr*LAMBDA.rows(id0)*ML2;

        K_tmp1 = ML2pVL.rows(id0);
        K_tmp1.each_row() %= mD2pvD.t();
        K_tmp2 = ML2.rows(id0);
        K_tmp2.each_row() %= mD2.t();
        c4.rows(id0) -= psr*(K_tmp1*ML2pVL.t()-K_tmp2*ML2.t()+square(LDL));
    }

    ML.elem(idl) = h/zeta;
    VL.elem(idl) = 1/zeta;
    mD = alpha/beta;
    vD = mD/beta;
    auto t_end = std::chrono::high_resolution_clock::now();
    double ElapsedTime = std::chrono::duration<double, std::milli>(t_end-t_start).count() / 1e3;
    if (kappa < maxIter && difmL<tol) mexPrintf("BISN converges, elapsed time is %e seconds.\n",ElapsedTime);
    else mexPrintf("BISN reaches the maximum number of iterations, elapsed time is %e seconds.\n",ElapsedTime);

    /* Output Interface */
    if (nlhs>3)
    {
        plhs[0] = armaCreateMxMatrix(p,p,mxDOUBLE_CLASS,mxREAL);
        armaSetPr(plhs[0],ML);
        plhs[1] = armaCreateMxMatrix(p,p,mxDOUBLE_CLASS,mxREAL);
        armaSetPr(plhs[1],VL);
        plhs[2] = armaCreateMxMatrix(p,1,mxDOUBLE_CLASS,mxREAL);
        armaSetPr(plhs[2],mD);
        plhs[3] = armaCreateMxMatrix(p,1,mxDOUBLE_CLASS,mxREAL);
        armaSetPr(plhs[3],vD);
        if (nlhs>4)
        {
            omega = a/b;
            plhs[4] = mxCreateDoubleScalar(omega);
        }
        if (nlhs>5)
        {
            lambda = d;
            lambda.transform([](double val){return (val > 10) ? Lentz_Algorithm(val) : (- boost::math::expint(-val) * exp(val));});
            lambda = 1/(d%lambda)-1;
            plhs[5] = armaCreateMxMatrix(pe,1,mxDOUBLE_CLASS,mxREAL);
            armaSetPr(plhs[5],lambda);
        }
        if (nlhs>6) plhs[6] = mxCreateDoubleScalar(ElapsedTime);
    }
    else
    {
        mexErrMsgIdAndTxt("BINS:output","Expected at least four output arguments");
    }

}


// Copyright (c) 2015-2019 Daniel Cooke
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

#include "genotype.hpp"

#include <boost/math/special_functions/binomial.hpp>
#include <boost/numeric/conversion/cast.hpp>

#include "utils/maths.hpp"

namespace octopus {

// non-member methods

Genotype<Haplotype> remap(const Genotype<Haplotype>& genotype, const GenomicRegion& region)
{
    Genotype<Haplotype> result {genotype.ploidy()};
    for (const auto& haplotype : genotype) result.emplace(remap(haplotype, region));
    return result;
}

std::size_t num_genotypes(const unsigned num_elements, const unsigned ploidy)
{
    return boost::math::binomial_coefficient<double>(num_elements + ploidy - 1, num_elements - 1);
}

boost::optional<std::size_t> num_genotypes_noexcept(const unsigned num_elements, const unsigned ploidy) noexcept
{
    boost::optional<std::size_t> result {};
    try {
        result = num_genotypes(num_elements, ploidy);
    } catch (...) {}
    return result;
}

std::size_t max_num_elements(const std::size_t num_genotypes, const unsigned ploidy)
{
    if (num_genotypes == 0 || ploidy == 0) return 0;
    auto y = maths::factorial<std::size_t>(ploidy);
    if (y >= num_genotypes) return 1;
    const auto t = num_genotypes * y;
    unsigned j {1};
    for (; j < num_genotypes; ++j) {
        y /= j;
        y *= j + ploidy;
        if (y >= t) break;
    }
    return j + 1;
}

std::size_t element_cardinality_in_genotypes(const unsigned num_elements, const unsigned ploidy)
{
    return ploidy * (num_genotypes(num_elements, ploidy) / num_elements);
}

std::size_t num_max_zygosity_genotypes(const unsigned num_elements, const unsigned ploidy)
{
    namespace bmp = boost::math::policies;
    using policy = bmp::policy<bmp::overflow_error<bmp::throw_on_error>>;
    try {
        return boost::numeric_cast<std::size_t>(boost::math::binomial_coefficient<double>(num_elements, ploidy, policy {}));
    } catch (const boost::numeric::positive_overflow& e) {
        throw std::overflow_error {e.what()};
    }
}

boost::optional<std::size_t> num_max_zygosity_genotypes_noexcept(unsigned num_elements, unsigned ploidy) noexcept
{
    assert(num_elements >= ploidy);
    boost::optional<std::size_t> result {};
    try {
        result = num_max_zygosity_genotypes(num_elements, ploidy);
    } catch (...) {}
    return result;
}

} // namespace octopus


#include "NelderMead.hpp"

#include <iostream>
#include <Eigen/Dense>

class Example : public NelderMead<Eigen::VectorXd, double> {
    double f(const Eigen::VectorXd& ys){
        double res = 0.0;
        double m = 1.0;
        for(int i=0; i<ys.size(); i++){
            res += m*std::abs(ys[i]);
            m*=10000.0;
        }
        return res;
    }
};

int main(){
    std::vector<Eigen::VectorXd> xs;

    xs.push_back(Eigen::Vector2d(1, 101));
    xs.push_back(Eigen::Vector2d(2, 100));
    xs.push_back(Eigen::Vector2d(3, 101));

    Example q;
    q.setSimplex(xs);
    for(int i=0; i<200; i++){
        q.iterate();
        std::cout<<q.current().first<<"\n";
    }
}