/*******************************************************************************
***  This file is part of Pecunia.                                           ***
***                                                                          ***
***  Copyright (C) 2017, 2018, 2019, 2020, 2021, 2023                        ***
***  John Schneiderman <Licencing _AT_ Schneiderman _DOT_ me>                ***
***                                                                          ***
***  This program is free software: you can redistribute it and/or modify it ***
***  under the terms of the GNU Lesser General Public License as published   ***
***  by the Free Software Foundation, either version 3 of the License, or    ***
***  (at your option) any later version.                                     ***
***                                                                          ***
***  This program is distributed in the hope that it will be useful, but     ***
***  WITHOUT ANY WARRANTY; without even the implied warranty of              ***
***  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                    ***
***  See the GNU Lesser General Public License for more details.             ***
***                                                                          ***
***  You should have received a copy of the GNU Lesser General Public License***
***  along with this program. If not, see <http://www.gnu.org/licenses/>.    ***
*******************************************************************************/
#include <QTest>

#include <cstdint>

#include <pecunia/FloatingPoint.h>
using pecunia::FloatingPoint;
#include <pecunia/Rounders.h>
using pecunia::rounders::reals::awayZero;
using pecunia::rounders::reals::down;
using pecunia::rounders::reals::even;
using pecunia::rounders::reals::odd;
using pecunia::rounders::reals::towardsZero;
using pecunia::rounders::reals::up;


namespace pecunia
{
namespace rounders
{
namespace reals
{
namespace tests
{

struct RoundersUT : QObject
{
	Q_OBJECT

private slots:
	//{ Up

	void up_OneSixPositive_ShouldBeRoundToTwo()
	{
		const FloatingPoint value{up(1.6, 0)};
		const FloatingPoint expected{2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_OneFivePositive_ShouldBeRoundToTwo()
	{
		const FloatingPoint value{up(1.5, 0)};
		const FloatingPoint expected{2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_OneFourPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{up(1.4, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_ZeroSixPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{up(0.6, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_ZeroFivePositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{up(0.5, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_ZeroFourPositive_ShouldBeRoundToZero()
	{
		const FloatingPoint value{up(0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_ZeroFourNegative_ShouldBeZero()
	{
		const FloatingPoint value{up(-0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_ZeroFiveNegative_ShouldBeZero()
	{
		const FloatingPoint value{up(-0.5, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_ZeroSixNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{up(-0.6, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_OneFourNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{up(-1.4, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_OneFiveNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{up(-1.5, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_OneSixNegative_ShouldBeNegativeTwo()
	{
		const FloatingPoint value{up(-1.6, 0)};
		const FloatingPoint expected{-2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_twentythreeFivePositive_ShouldBeTwentyfour()
	{
		const FloatingPoint value{up(23.5, 0)};
		const FloatingPoint expected{24.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void up_twentythreeFiveNegative_ShouldBeTwentythreeNegative()
	{
		const FloatingPoint value{up(-23.5, 0)};
		const FloatingPoint expected{-23.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}

	//}

	//{ Down

	void down_OneSixPositive_ShouldBeRoundToTwo()
	{
		const FloatingPoint value{down(1.6, 0)};
		const FloatingPoint expected{2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_OneFivePositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{down(1.5, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_OneFourPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{down(1.4, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_ZeroSixPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{down(0.6, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_ZeroFivePositive_ShouldBeRoundToZero()
	{
		const FloatingPoint value{down(0.5, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_ZeroFourPositive_ShouldBeRoundToZero()
	{
		const FloatingPoint value{down(0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_ZeroFourNegative_ShouldBeZero()
	{
		const FloatingPoint value{down(-0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_ZeroFiveNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{down(-0.5, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_ZeroSixNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{down(-0.6, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_OneFourNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{down(-1.4, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_OneFiveNegative_ShouldBeNegativeTwo()
	{
		const FloatingPoint value{down(-1.5, 0)};
		const FloatingPoint expected{-2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_OneSixNegative_ShouldBeNegativeTwo()
	{
		const FloatingPoint value{down(-1.6, 0)};
		const FloatingPoint expected{-2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_OneZeroZeroSixNegativeTwoDigits_ShouldBeNegativeOneZeroOne()
	{
		const FloatingPoint value{down(-1.006, 2)};
		const FloatingPoint expected{-1.01};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_twentythreeFivePositive_ShouldBeTwentythree()
	{
		const FloatingPoint value{down(23.5, 0)};
		const FloatingPoint expected{23.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void down_twentythreeFiveNegative_ShouldBeTwentyfourNegative()
	{
		const FloatingPoint value{down(-23.5, 0)};
		const FloatingPoint expected{-24.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}

	//}

	//{ Towards Zero

	void towardsZero_OneSixPositive_ShouldBeRoundToTwo()
	{
		const FloatingPoint value{towardsZero(1.6, 0)};
		const FloatingPoint expected{2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_OneFivePositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{towardsZero(1.5, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_OneFourPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{towardsZero(1.4, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_ZeroSixPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{towardsZero(0.6, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_ZeroFivePositive_ShouldBeRoundToZero()
	{
		const FloatingPoint value{towardsZero(0.5, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_ZeroFourPositive_ShouldBeRoundToZero()
	{
		const FloatingPoint value{towardsZero(0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_ZeroFourNegative_ShouldBeZero()
	{
		const FloatingPoint value{towardsZero(-0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_ZeroFiveNegative_ShouldBeZero()
	{
		const FloatingPoint value{towardsZero(-0.5, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_ZeroSixNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{towardsZero(-0.6, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_OneFourNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{towardsZero(-1.4, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_OneFiveNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{towardsZero(-1.5, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_OneSixNegative_ShouldBeNegativeTwo()
	{
		const FloatingPoint value{towardsZero(-1.6, 0)};
		const FloatingPoint expected{-2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_OneZeroZeroSixNegativeTwoDigits_ShouldBeNegativeOneZeroOne()
	{
		const FloatingPoint value{towardsZero(-1.006, 2)};
		const FloatingPoint expected{-1.01};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_twentythreeFivePositive_ShouldBeTwentythree()
	{
		const FloatingPoint value{towardsZero(23.5, 0)};
		const FloatingPoint expected{23.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void towardsZero_twentythreeFiveNegative_ShouldBeTwentythreeNegative()
	{
		const FloatingPoint value{towardsZero(-23.5, 0)};
		const FloatingPoint expected{-23.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}

	//}

	//{ Away Zero

	void awayZero_OneSixPositive_ShouldBeRoundToTwo()
	{
		const FloatingPoint value{awayZero(1.6, 0)};
		const FloatingPoint expected{2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_OneFivePositive_ShouldBeRoundToTwo()
	{
		const FloatingPoint value{awayZero(1.5, 0)};
		const FloatingPoint expected{2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_OneFourPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{awayZero(1.4, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_ZeroSixPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{awayZero(0.6, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_ZeroFivePositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{awayZero(0.5, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_ZeroFourPositive_ShouldBeRoundToZero()
	{
		const FloatingPoint value{awayZero(0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_ZeroFourNegative_ShouldBeZero()
	{
		const FloatingPoint value{awayZero(-0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_ZeroFiveNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{awayZero(-0.5, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_ZeroSixNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{awayZero(-0.6, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_OneFourNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{awayZero(-1.4, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_OneFiveNegative_ShouldBeNegativeTwo()
	{
		const FloatingPoint value{awayZero(-1.5, 0)};
		const FloatingPoint expected{-2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_OneSixNegative_ShouldBeNegativeTwo()
	{
		const FloatingPoint value{awayZero(-1.6, 0)};
		const FloatingPoint expected{-2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_OneZeroZeroSixNegativeTwoDigits_ShouldBeNegativeOneZeroOne()
	{
		const FloatingPoint value{awayZero(-1.006, 2)};
		const FloatingPoint expected{-1.01};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_twentythreeFivePositive_ShouldBeTwentyfour()
	{
		const FloatingPoint value{awayZero(23.5, 0)};
		const FloatingPoint expected{24.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void awayZero_twentythreeFiveNegative_ShouldBeTwentythreeNegative()
	{
		const FloatingPoint value{awayZero(-23.5, 0)};
		const FloatingPoint expected{-24.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}

	//}

	//{ Even

	void even_OneSixPositive_ShouldBeRoundToTwo()
	{
		const FloatingPoint value{even(1.6, 0)};
		const FloatingPoint expected{2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_OneFivePositive_ShouldBeRoundToTwo()
	{
		const FloatingPoint value{even(1.5, 0)};
		const FloatingPoint expected{2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_OneFourPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{even(1.4, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_ZeroSixPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{even(0.6, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_ZeroFivePositive_ShouldBeRoundToZero()
	{
		const FloatingPoint value{even(0.5, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_ZeroFourPositive_ShouldBeRoundToZero()
	{
		const FloatingPoint value{even(0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_ZeroFourNegative_ShouldBeZero()
	{
		const FloatingPoint value{even(-0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_ZeroFiveNegative_ShouldBeZero()
	{
		const FloatingPoint value{even(-0.5, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_ZeroSixNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{even(-0.6, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_OneFourNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{even(-1.4, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_OneFiveNegative_ShouldBeNegativeTwo()
	{
		const FloatingPoint value{even(-1.5, 0)};
		const FloatingPoint expected{-2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_OneSixNegative_ShouldBeNegativeTwo()
	{
		const FloatingPoint value{even(-1.6, 0)};
		const FloatingPoint expected{-2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_OneZeroZeroSixNegativeTwoDigits_ShouldBeNegativeOneZeroOne()
	{
		const FloatingPoint value{even(-1.006, 2)};
		const FloatingPoint expected{-1.01};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_twentyfourFivePositive_ShouldBeTwentyfour()
	{
		const FloatingPoint value{even(24.5, 0)};
		const FloatingPoint expected{24.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_twentythreeFivePositive_ShouldBeTwentyfour()
	{
		const FloatingPoint value{even(23.5, 0)};
		const FloatingPoint expected{24.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_twentythreeFiveNegative_ShouldBeTwentyfourNegative()
	{
		const FloatingPoint value{even(-23.5, 0)};
		const FloatingPoint expected{-24.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void even_twentyfourFiveNegative_ShouldBeTwentyfourNegative()
	{
		const FloatingPoint value{even(-24.5, 0)};
		const FloatingPoint expected{-24.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}

	//}

	//{ Odd

	void odd_OneSixPositive_ShouldBeRoundToTwo()
	{
		const FloatingPoint value{odd(1.6, 0)};
		const FloatingPoint expected{2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_OneFivePositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{odd(1.5, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_OneFourPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{odd(1.4, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_ZeroSixPositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{odd(0.6, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_ZeroFivePositive_ShouldBeRoundToOne()
	{
		const FloatingPoint value{odd(0.5, 0)};
		const FloatingPoint expected{1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_ZeroFourPositive_ShouldBeRoundToZero()
	{
		const FloatingPoint value{odd(0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_ZeroFourNegative_ShouldBeZero()
	{
		const FloatingPoint value{odd(-0.4, 0)};
		const FloatingPoint expected{0.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_ZeroFiveNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{odd(-0.5, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_ZeroSixNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{odd(-0.6, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_OneFourNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{odd(-1.4, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_OneFiveNegative_ShouldBeNegativeOne()
	{
		const FloatingPoint value{odd(-1.5, 0)};
		const FloatingPoint expected{-1.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_OneSixNegative_ShouldBeNegativeTwo()
	{
		const FloatingPoint value{odd(-1.6, 0)};
		const FloatingPoint expected{-2.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_OneZeroZeroSixNegativeTwoDigits_ShouldBeNegativeOneZeroOne()
	{
		const FloatingPoint value{odd(-1.006, 2)};
		const FloatingPoint expected{-1.01};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_twentythreeFivePositive_ShouldBeTwentythree()
	{
		const FloatingPoint value{odd(23.5, 0)};
		const FloatingPoint expected{23.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_twentytwoFivePositive_ShouldBeTwentythree()
	{
		const FloatingPoint value{odd(22.5, 0)};
		const FloatingPoint expected{23.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_twentythreeFiveNegative_ShouldBeTwentythreeNegative()
	{
		const FloatingPoint value{odd(-23.5, 0)};
		const FloatingPoint expected{-23.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}
	void odd_twentytwoFiveNegative_ShouldBeTwentythreeNegative()
	{
		const FloatingPoint value{odd(-22.5, 0)};
		const FloatingPoint expected{-23.0};
		QCOMPARE(value.underlying(), expected.underlying());
	}

	//}
};

}}}}

QTEST_MAIN(pecunia::rounders::reals::tests::RoundersUT)
#include "rounders-floating-point-unit-tests.moc"
