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

#include <iostream>

#include <boost/core/demangle.hpp>
#include <boost/safe_numerics/checked_result_operations.hpp>
#include <boost/safe_numerics/checked_integer.hpp>

// note: T should be of type checked_result<R> for some integer type R
template<class T>
bool test_checked_multiply(
    T v1,
    T v2,
    char expected_result
){
    using namespace boost::safe_numerics;
    const T result = v1 * v2;
    std::cout
        << "testing  "
        << v1 << " * " << v2 << " -> " << result
        << std::endl;

    switch(expected_result){
    case '0':
    case '.':
        if(result.exception()){
            std::cout
                << "erroneously detected error in multiplication "
                << std::endl;
            v1 * v2;
            return false;
        }
        if(expected_result == '0'
        && result != T(0)
        ){
            std::cout
                << "failed to get expected zero result "
                << std::endl;
            v1 * v2;
            return false;
        }
        return true;
    case '-':
        if(safe_numerics_error::negative_overflow_error == result.m_e)
            return true;
        break;
    case '+':
        if(safe_numerics_error::positive_overflow_error == result.m_e)
            return true;
        break;
    case '!':
        if(safe_numerics_error::range_error == result.m_e)
            return true;
        break;
    }
    std::cout
        << "failed to detect error in multiplication "
        << std::hex << result << "(" << std::dec << result << ")"
        << " != "<< v1 << " * " << v2
        << std::endl;
    v1 * v2;
    return false;
}

#include "test_checked_multiply.hpp"

template<typename T, typename First, typename Second>
struct test_signed_pair {
    bool operator()() const {
        std::size_t i = First();
        std::size_t j = Second();
        std::cout << std::dec << i << ',' << j << ','
        << "testing " << boost::core::demangle(typeid(T).name()) << ' ';
        return test_checked_multiply(
            signed_values<T>[i],
            signed_values<T>[j],
            signed_multiplication_results[i][j]
        );
    };
};

template<typename T, typename First, typename Second>
struct test_unsigned_pair {
    bool operator()() const {
        std::size_t i = First();
        std::size_t j = Second();
        std::cout << std::dec << i << ',' << j << ','
        << "testing " << boost::core::demangle(typeid(T).name()) << ' ';
        return test_checked_multiply(
            unsigned_values<T>[i],
            unsigned_values<T>[j],
            unsigned_multiplication_results[i][j]
        );
    };
};

#include "check_symmetry.hpp"

#include <boost/mp11/algorithm.hpp>

int main(int , char *[]){
    static_assert(
        check_symmetry(signed_multiplication_results),
        "sanity check on test matrix - should be symmetrical"
    );
    static_assert(
        check_symmetry(unsigned_multiplication_results),
        "sanity check on test matrix - should be symmetrical"
    );

    using namespace boost::mp11;
    bool rval = true;

    mp_for_each<
        mp_product<
            test_signed_pair,
            signed_test_types,
            signed_value_indices,
            signed_value_indices
        >
    >([&](auto I){
        rval &= I();
    });

    std::cout << "*** testing unsigned values\n";

    mp_for_each<
        mp_product<
            test_unsigned_pair,
            unsigned_test_types,
            unsigned_value_indices, unsigned_value_indices
        >
    >([&](auto I){
        rval &= I();
    });

    std::cout << (rval ? "success!" : "failure") << std::endl;
    return rval ? 0 : 1;
}