#include <psemek/test/test.hpp>

#include <psemek/util/big_int.hpp>
#include <psemek/random/generator.hpp>
#include <psemek/random/uniform.hpp>

using namespace psemek;
using namespace psemek::util;

namespace
{

	void check_digits(big_int const & bi, std::vector<big_int::digit> const & digits_expected)
	{
		span<big_int::digit const> digits_actual = bi.digits();
		expect_equal(digits_actual.size(), digits_expected.size());
		for (std::size_t i = 0; i < digits_actual.size(); ++i)
			expect_equal(digits_actual[i], digits_expected[i]);
	}

}

test_case(util_big__int_init_small)
{
	{
		big_int x;
		expect(!x.negative());
		check_digits(x, {});
	}

	{
		big_int x(1);
		expect(!x.negative());
		check_digits(x, {1});
	}

	{
		big_int x(173);
		expect(!x.negative());
		check_digits(x, {173});
	}

	{
		big_int x(1u << 31);
		expect(!x.negative());
		check_digits(x, {1u << 31});
	}

	{
		big_int x(1ull << 32);
		expect(!x.negative());
		check_digits(x, {0, 1});
	}

	{
		big_int x(1ull << 63);
		expect(!x.negative());
		check_digits(x, {0, 1u << 31});
	}

	{
		big_int x(static_cast<std::uint64_t>(-1));
		expect(!x.negative());
		check_digits(x, {-1, -1});
	}

	{
		big_int x(-1);
		expect(x.negative());
		check_digits(x, {1});
	}

	{
		big_int x(-279);
		expect(x.negative());
		check_digits(x, {279});
	}

	{
		big_int x((-1) << 31);
		expect(x.negative());
		check_digits(x, {1u << 31});
	}

	{
		big_int x((-1ll) << 32);
		expect(x.negative());
		check_digits(x, {0, 1u});
	}

	{
		big_int x((-1ll) << 63);
		expect(x.negative());
		check_digits(x, {0, 1u << 31});
	}

	{
		big_int x(((-1ll) << 62) + ((-1ll) << 15));
		expect(x.negative());
		check_digits(x, {1u << 15, 1u << 30});
	}
}

test_case(util_big__int_init_from__digits)
{
	random::generator rng;

	{
		big_int x = big_int::from_digits(std::vector<int>{});
		expect(!x.negative());
		check_digits(x, {});
	}

	{
		big_int x = big_int::from_digits(std::vector{5724});
		expect(!x.negative());
		check_digits(x, {5724});
	}

	{
		big_int x = big_int::from_digits(std::vector{1234, 5678});
		expect(!x.negative());
		check_digits(x, {1234, 5678});
	}

	{
		big_int x = big_int::from_digits(std::vector{1234, 5678, 0, 0});
		expect(!x.negative());
		check_digits(x, {1234, 5678});
	}

	{
		std::vector<big_int::digit> digits(64);
		for (auto & digit : digits)
			digit = random::uniform<big_int::digit>(rng);
		if (digits.back() == 0)
			digits.back() = 1;

		big_int x = big_int::from_digits(digits);
		expect(!x.negative());
		check_digits(x, digits);
	}

	{
		big_int x = big_int::from_digits(std::vector<int>{}, true);
		expect(!x.negative());
		check_digits(x, {});
	}

	{
		big_int x = big_int::from_digits(std::vector{5724}, true);
		expect(x.negative());
		check_digits(x, {5724});
	}

	{
		big_int x = big_int::from_digits(std::vector{1234, 5678}, true);
		expect(x.negative());
		check_digits(x, {1234, 5678});
	}

	{
		big_int x = big_int::from_digits(std::vector{1234, 5678, 0, 0}, true);
		expect(x.negative());
		check_digits(x, {1234, 5678});
	}

	{
		std::vector<big_int::digit> digits(64);
		for (auto & digit : digits)
			digit = random::uniform<big_int::digit>(rng);
		if (digits.back() == 0)
			digits.back() = 1;

		big_int x = big_int::from_digits(digits, true);
		expect(x.negative());
		check_digits(x, digits);
	}
}
