package math

import (
	"errors"
	"math"
	"testing"

	"github.com/shopspring/decimal"
)

func TestCalculateFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	if expectedOutput, actualResult := 0.01, CalculateFee(originalInput, fee); expectedOutput != actualResult {
		t.Errorf(
			"Expected '%f'. Actual '%f'.", expectedOutput, actualResult)
	}
}

func TestCalculateAmountWithFee(t *testing.T) {
	t.Parallel()
	originalInput := float64(1)
	fee := float64(1)
	if actualResult, expectedOutput := CalculateAmountWithFee(originalInput, fee), 1.01; expectedOutput != actualResult {
		t.Errorf(
			"Expected '%f'. Actual '%f'.", expectedOutput, actualResult)
	}
}

func TestCalculatePercentageGainOrLoss(t *testing.T) {
	t.Parallel()
	originalInput := float64(9300)
	secondInput := float64(9000)
	expectedOutput := 3.3333333333333335
	actualResult := CalculatePercentageGainOrLoss(originalInput, secondInput)
	if expectedOutput != actualResult {
		t.Errorf(
			"Expected '%f'. Actual '%f'.", expectedOutput, actualResult)
	}
}

func TestCalculatePercentageDifference(t *testing.T) {
	t.Parallel()
	originalInput := float64(10)
	secondAmount := float64(5)
	expectedOutput := 66.66666666666666
	actualResult := CalculatePercentageDifference(originalInput, secondAmount)
	if expectedOutput != actualResult {
		t.Errorf(
			"Expected '%f'. Actual '%f'.", expectedOutput, actualResult)
	}
}

func TestCalculateNetProfit(t *testing.T) {
	t.Parallel()
	amount := float64(5)
	priceThen := float64(1)
	priceNow := float64(10)
	costs := float64(1)
	actualResult := CalculateNetProfit(amount, priceThen, priceNow, costs)
	if expectedOutput := float64(44); expectedOutput != actualResult {
		t.Errorf(
			"Expected '%f'. Actual '%f'.", expectedOutput, actualResult)
	}
}

func TestRoundFloat(t *testing.T) {
	t.Parallel()
	// mapping of input vs expected result : map[precision]map[testedValue]expectedOutput
	testTableValues := map[int]map[float64]float64{
		0: {
			2.23456789:  2,
			-2.23456789: -2,
		},
		1: {
			2.23456789:  2.2,
			-2.23456789: -2.2,
		},
		2: {
			2.23456789:  2.23,
			-2.23456789: -2.23,
		},
		4: {
			2.23456789:  2.2346,
			-2.23456789: -2.2346,
		},
		8: {
			2.23456781:  2.23456781,
			-2.23456781: -2.23456781,
		},
	}

	for precision, values := range testTableValues {
		for testInput, expectedOutput := range values {
			actualOutput := RoundFloat(testInput, precision)
			if actualOutput != expectedOutput {
				t.Errorf("RoundFloat Expected '%v'. Actual '%v' on precission %d",
					expectedOutput, actualOutput, precision)
			}
		}
	}
}

func TestSortinoRatio(t *testing.T) {
	t.Parallel()
	rfr := 0.001
	figures := []float64{0.10, 0.04, 0.15, -0.05, 0.20, -0.02, 0.08, -0.06, 0.13, 0.23}
	avg, err := ArithmeticMean(figures)
	if err != nil {
		t.Error(err)
	}
	_, err = SortinoRatio(nil, rfr, avg)
	if !errors.Is(err, errZeroValue) {
		t.Errorf("expected: %v, received %v", errZeroValue, err)
	}

	var r float64
	r, err = SortinoRatio(figures, rfr, avg)
	if err != nil {
		t.Error(err)
	}
	if r != 3.0377875479459906 {
		t.Errorf("expected 3.0377875479459906, received %v", r)
	}
	avg, err = FinancialGeometricMean(figures)
	if err != nil {
		t.Error(err)
	}
	r, err = SortinoRatio(figures, rfr, avg)
	if err != nil {
		t.Error(err)
	}
	if r != 2.8712802265603243 {
		t.Errorf("expected 2.525203164136098, received %v", r)
	}

	// this follows and matches the example calculation from
	// https://www.wallstreetmojo.com/sortino-ratio/
	example := []float64{
		0.1,
		0.12,
		0.07,
		-0.03,
		0.08,
		-0.04,
		0.15,
		0.2,
		0.12,
		0.06,
		-0.03,
		0.02,
	}
	avg, err = ArithmeticMean(example)
	if err != nil {
		t.Error(err)
	}
	r, err = SortinoRatio(example, 0.06, avg)
	if err != nil {
		t.Error(err)
	}
	if rr := math.Round(r*10) / 10; rr != 0.2 {
		t.Errorf("expected 0.2, received %v", rr)
	}
}

func TestInformationRatio(t *testing.T) {
	t.Parallel()
	figures := []float64{0.0665, 0.0283, 0.0911, 0.0008, -0.0203, -0.0978, 0.0164, -0.0537, 0.078, 0.0032, 0.0249, 0}
	comparisonFigures := []float64{0.0216, 0.0048, 0.036, 0.0303, 0.0043, -0.0694, 0.0179, -0.0918, 0.0787, 0.0297, 0.003, 0}
	avg, err := ArithmeticMean(figures)
	if err != nil {
		t.Error(err)
	}
	if avg != 0.01145 {
		t.Error(avg)
	}
	var avgComparison float64
	avgComparison, err = ArithmeticMean(comparisonFigures)
	if err != nil {
		t.Error(err)
	}
	if avgComparison != 0.005425 {
		t.Error(avgComparison)
	}

	var eachDiff []float64
	for i := range figures {
		eachDiff = append(eachDiff, figures[i]-comparisonFigures[i])
	}
	stdDev, err := PopulationStandardDeviation(eachDiff)
	if err != nil {
		t.Error(err)
	}
	if stdDev != 0.028992588851865803 {
		t.Error(stdDev)
	}
	information := (avg - avgComparison) / stdDev
	if information != 0.20781172839666107 {
		t.Errorf("expected %v received %v", 0.20781172839666107, information)
	}
	var information2 float64
	information2, err = InformationRatio(figures, comparisonFigures, avg, avgComparison)
	if err != nil {
		t.Error(err)
	}
	if information != information2 {
		t.Error(information2)
	}

	_, err = InformationRatio(figures, []float64{1}, avg, avgComparison)
	if !errors.Is(err, errInformationBadLength) {
		t.Errorf("expected: %v, received %v", errInformationBadLength, err)
	}
}

func TestCalmarRatio(t *testing.T) {
	t.Parallel()
	_, err := CalmarRatio(0, 0, 0, 0)
	if !errors.Is(err, errCalmarHighest) {
		t.Errorf("expected: %v, received %v", errCalmarHighest, err)
	}
	var ratio float64
	ratio, err = CalmarRatio(50000, 15000, 0.2, 0.1)
	if err != nil {
		t.Error(err)
	}
	if ratio != 0.14285714285714288 {
		t.Error(ratio)
	}
}

func TestCAGR(t *testing.T) {
	t.Parallel()
	_, err := CompoundAnnualGrowthRate(
		0,
		0,
		0,
		0)
	if !errors.Is(err, errCAGRNoIntervals) {
		t.Error(err)
	}
	_, err = CompoundAnnualGrowthRate(
		0,
		0,
		0,
		1)
	if !errors.Is(err, errCAGRZeroOpenValue) {
		t.Error(err)
	}

	var cagr float64
	cagr, err = CompoundAnnualGrowthRate(
		100,
		147,
		1,
		1)
	if err != nil {
		t.Error(err)
	}
	if cagr != 47 {
		t.Error("expected 47%")
	}
	cagr, err = CompoundAnnualGrowthRate(
		100,
		147,
		365,
		365)
	if err != nil {
		t.Error(err)
	}
	if cagr != 47 {
		t.Error("expected 47%")
	}

	cagr, err = CompoundAnnualGrowthRate(
		100,
		200,
		1,
		20)
	if err != nil {
		t.Error(err)
	}
	if cagr != 3.5264923841377582 {
		t.Error("expected 3.53%")
	}
}

func TestCalculateSharpeRatio(t *testing.T) {
	t.Parallel()
	result, err := SharpeRatio(nil, 0, 0)
	if !errors.Is(err, errZeroValue) {
		t.Error(err)
	}
	if result != 0 {
		t.Error("expected 0")
	}

	result, err = SharpeRatio([]float64{0.026}, 0.017, 0.026)
	if err != nil {
		t.Error(err)
	}
	if result != 0 {
		t.Error("expected 0")
	}

	// this follows and matches the example calculation (without rounding) from
	// https://www.educba.com/sharpe-ratio-formula/
	returns := []float64{
		-0.0005,
		-0.0065,
		-0.0113,
		0.0031,
		-0.0112,
		0.0056,
		0.0156,
		0.0048,
		0.0012,
		0.0038,
		-0.0008,
		0.0032,
		0,
		-0.0128,
		-0.0058,
		0.003,
		0.0042,
		0.0055,
		0.0009,
	}
	var avg float64
	avg, err = ArithmeticMean(returns)
	if err != nil {
		t.Error(err)
	}
	result, err = SharpeRatio(returns, -0.0017, avg)
	if err != nil {
		t.Error(err)
	}
	result = math.Round(result*100) / 100
	if result != 0.26 {
		t.Errorf("expected 0.26, received %v", result)
	}
}

func TestStandardDeviation2(t *testing.T) {
	t.Parallel()
	r := []float64{9, 2, 5, 4, 12, 7}
	mean, err := ArithmeticMean(r)
	if err != nil {
		t.Error(err)
	}
	superMean := []float64{}
	for i := range r {
		result := math.Pow(r[i]-mean, 2)
		superMean = append(superMean, result)
	}
	superMeany := (superMean[0] + superMean[1] + superMean[2] + superMean[3] + superMean[4] + superMean[5]) / 5
	manualCalculation := math.Sqrt(superMeany)
	var codeCalcu float64
	codeCalcu, err = SampleStandardDeviation(r)
	if err != nil {
		t.Error(err)
	}
	if manualCalculation != codeCalcu && codeCalcu != 3.619 {
		t.Error("expected 3.619")
	}
}

func TestGeometricAverage(t *testing.T) {
	t.Parallel()
	values := []float64{1, 2, 3, 4, 5, 6, 7, 8}
	_, err := GeometricMean(nil)
	if !errors.Is(err, errZeroValue) {
		t.Error(err)
	}
	var mean float64
	mean, err = GeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if mean != 3.764350599503129 {
		t.Errorf("expected %v, received %v", 3.95, mean)
	}

	values = []float64{15, 12, 13, 19, 10}
	mean, err = GeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if mean != 13.477020583645698 {
		t.Errorf("expected %v, received %v", 13.50, mean)
	}

	values = []float64{-1, 12, 13, 19, 10}
	mean, err = GeometricMean(values)
	if !errors.Is(err, errGeometricNegative) {
		t.Error(err)
	}
	if mean != 0 {
		t.Errorf("expected %v, received %v", 0, mean)
	}
}

func TestFinancialGeometricAverage(t *testing.T) {
	t.Parallel()
	values := []float64{1, 2, 3, 4, 5, 6, 7, 8}
	_, err := FinancialGeometricMean(nil)
	if !errors.Is(err, errZeroValue) {
		t.Error(err)
	}

	var mean float64
	mean, err = FinancialGeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if mean != 3.9541639996482028 {
		t.Errorf("expected %v, received %v", 3.95, mean)
	}

	values = []float64{15, 12, 13, 19, 10}
	mean, err = FinancialGeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if mean != 13.49849123325646 {
		t.Errorf("expected %v, received %v", 13.50, mean)
	}

	values = []float64{-1, 12, 13, 19, 10}
	mean, err = FinancialGeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if mean != 0 {
		t.Errorf("expected %v, received %v", 0, mean)
	}

	values = []float64{-2, 12, 13, 19, 10}
	_, err = FinancialGeometricMean(values)
	if !errors.Is(err, errNegativeValueOutOfRange) {
		t.Error(err)
	}
}

func TestArithmeticAverage(t *testing.T) {
	values := []float64{1, 2, 3, 4, 5, 6, 7, 8}
	_, err := ArithmeticMean(nil)
	if !errors.Is(err, errZeroValue) {
		t.Error(err)
	}
	var avg float64
	avg, err = ArithmeticMean(values)
	if err != nil {
		t.Error(err)
	}
	if avg != 4.5 {
		t.Error("expected 4.5")
	}
}

func TestDecimalSortinoRatio(t *testing.T) {
	t.Parallel()
	rfr := decimal.NewFromFloat(0.001)
	figures := []decimal.Decimal{
		decimal.NewFromFloat(0.10),
		decimal.NewFromFloat(0.04),
		decimal.NewFromFloat(0.15),
		decimal.NewFromFloat(-0.05),
		decimal.NewFromFloat(0.20),
		decimal.NewFromFloat(-0.02),
		decimal.NewFromFloat(0.08),
		decimal.NewFromFloat(-0.06),
		decimal.NewFromFloat(0.13),
		decimal.NewFromFloat(0.23),
	}
	avg, err := DecimalArithmeticMean(figures)
	if err != nil {
		t.Error(err)
	}
	_, err = DecimalSortinoRatio(nil, rfr, avg)
	if !errors.Is(err, errZeroValue) {
		t.Errorf("expected: %v, received %v", errZeroValue, err)
	}

	var r decimal.Decimal
	r, err = DecimalSortinoRatio(figures, rfr, avg)
	if err != nil && !errors.Is(err, ErrInexactConversion) {
		t.Error(err)
	}
	rf, exact := r.Float64()
	if !exact && rf != 3.0377875479459906 {
		t.Errorf("expected 3.0377875479459906, received %v", r)
	} else if rf != 3.0377875479459907 {
		t.Errorf("expected 3.0377875479459907, received %v", r)
	}

	avg, err = DecimalFinancialGeometricMean(figures)
	if err != nil {
		t.Error(err)
	}

	r, err = DecimalSortinoRatio(figures, rfr, avg)
	if err != nil && !errors.Is(err, ErrInexactConversion) {
		t.Error(err)
	}
	if !r.Equal(decimal.NewFromFloat(2.8712802265603243)) {
		t.Errorf("expected 2.525203164136098, received %v", r)
	}

	// this follows and matches the example calculation from
	// https://www.wallstreetmojo.com/sortino-ratio/
	example := []decimal.Decimal{
		decimal.NewFromFloat(0.1),
		decimal.NewFromFloat(0.12),
		decimal.NewFromFloat(0.07),
		decimal.NewFromFloat(-0.03),
		decimal.NewFromFloat(0.08),
		decimal.NewFromFloat(-0.04),
		decimal.NewFromFloat(0.15),
		decimal.NewFromFloat(0.2),
		decimal.NewFromFloat(0.12),
		decimal.NewFromFloat(0.06),
		decimal.NewFromFloat(-0.03),
		decimal.NewFromFloat(0.02),
	}
	avg, err = DecimalArithmeticMean(example)
	if err != nil {
		t.Error(err)
	}
	r, err = DecimalSortinoRatio(example, decimal.NewFromFloat(0.06), avg)
	if err != nil && !errors.Is(err, ErrInexactConversion) {
		t.Error(err)
	}
	if rr := r.Round(1); !rr.Equal(decimal.NewFromFloat(0.2)) {
		t.Errorf("expected 0.2, received %v", rr)
	}
}

func TestDecimalInformationRatio(t *testing.T) {
	t.Parallel()
	figures := []decimal.Decimal{
		decimal.NewFromFloat(0.0665),
		decimal.NewFromFloat(0.0283),
		decimal.NewFromFloat(0.0911),
		decimal.NewFromFloat(0.0008),
		decimal.NewFromFloat(-0.0203),
		decimal.NewFromFloat(-0.0978),
		decimal.NewFromFloat(0.0164),
		decimal.NewFromFloat(-0.0537),
		decimal.NewFromFloat(0.078),
		decimal.NewFromFloat(0.0032),
		decimal.NewFromFloat(0.0249),
		decimal.Zero,
	}
	comparisonFigures := []decimal.Decimal{
		decimal.NewFromFloat(0.0216),
		decimal.NewFromFloat(0.0048),
		decimal.NewFromFloat(0.036),
		decimal.NewFromFloat(0.0303),
		decimal.NewFromFloat(0.0043),
		decimal.NewFromFloat(-0.0694),
		decimal.NewFromFloat(0.0179),
		decimal.NewFromFloat(-0.0918),
		decimal.NewFromFloat(0.0787),
		decimal.NewFromFloat(0.0297),
		decimal.NewFromFloat(0.003),
		decimal.Zero,
	}
	avg, err := DecimalArithmeticMean(figures)
	if err != nil {
		t.Error(err)
	}
	if !avg.Equal(decimal.NewFromFloat(0.01145)) {
		t.Error(avg)
	}
	var avgComparison decimal.Decimal
	avgComparison, err = DecimalArithmeticMean(comparisonFigures)
	if err != nil {
		t.Error(err)
	}
	if !avgComparison.Equal(decimal.NewFromFloat(0.005425)) {
		t.Error(avgComparison)
	}

	var eachDiff []decimal.Decimal
	for i := range figures {
		eachDiff = append(eachDiff, figures[i].Sub(comparisonFigures[i]))
	}
	stdDev, err := DecimalPopulationStandardDeviation(eachDiff)
	if err != nil && !errors.Is(err, ErrInexactConversion) {
		t.Error(err)
	}
	if !stdDev.Equal(decimal.NewFromFloat(0.028992588851865227)) {
		t.Error(stdDev)
	}
	information := avg.Sub(avgComparison).Div(stdDev)
	if !information.Equal(decimal.NewFromFloat(0.2078117283966652)) {
		t.Errorf("expected %v received %v", 0.2078117283966652, information)
	}
	var information2 decimal.Decimal
	information2, err = DecimalInformationRatio(figures, comparisonFigures, avg, avgComparison)
	if err != nil {
		t.Error(err)
	}
	if !information.Equal(information2) {
		t.Error(information2)
	}

	_, err = DecimalInformationRatio(figures, []decimal.Decimal{decimal.NewFromInt(1)}, avg, avgComparison)
	if !errors.Is(err, errInformationBadLength) {
		t.Errorf("expected: %v, received %v", errInformationBadLength, err)
	}
}

func TestDecimalCalmarRatio(t *testing.T) {
	t.Parallel()
	_, err := DecimalCalmarRatio(decimal.Zero, decimal.Zero, decimal.Zero, decimal.Zero)
	if !errors.Is(err, errCalmarHighest) {
		t.Errorf("expected: %v, received %v", errCalmarHighest, err)
	}
	var ratio decimal.Decimal
	ratio, err = DecimalCalmarRatio(
		decimal.NewFromInt(50000),
		decimal.NewFromInt(15000),
		decimal.NewFromFloat(0.2),
		decimal.NewFromFloat(0.1))
	if err != nil {
		t.Error(err)
	}
	if !ratio.Equal(decimal.NewFromFloat(0.1428571428571429)) {
		t.Error(ratio)
	}
}

func TestDecimalCalculateSharpeRatio(t *testing.T) {
	t.Parallel()
	result, err := DecimalSharpeRatio(nil, decimal.Zero, decimal.Zero)
	if !errors.Is(err, errZeroValue) {
		t.Error(err)
	}
	if !result.IsZero() {
		t.Error("expected 0")
	}

	result, err = DecimalSharpeRatio([]decimal.Decimal{decimal.NewFromFloat(0.026)}, decimal.NewFromFloat(0.017), decimal.NewFromFloat(0.026))
	if err != nil {
		t.Error(err)
	}
	if !result.IsZero() {
		t.Error("expected 0")
	}

	// this follows and matches the example calculation (without rounding) from
	// https://www.educba.com/sharpe-ratio-formula/
	returns := []decimal.Decimal{
		decimal.NewFromFloat(-0.0005),
		decimal.NewFromFloat(-0.0065),
		decimal.NewFromFloat(-0.0113),
		decimal.NewFromFloat(0.0031),
		decimal.NewFromFloat(-0.0112),
		decimal.NewFromFloat(0.0056),
		decimal.NewFromFloat(0.0156),
		decimal.NewFromFloat(0.0048),
		decimal.NewFromFloat(0.0012),
		decimal.NewFromFloat(0.0038),
		decimal.NewFromFloat(-0.0008),
		decimal.NewFromFloat(0.0032),
		decimal.Zero,
		decimal.NewFromFloat(-0.0128),
		decimal.NewFromFloat(-0.0058),
		decimal.NewFromFloat(0.003),
		decimal.NewFromFloat(0.0042),
		decimal.NewFromFloat(0.0055),
		decimal.NewFromFloat(0.0009),
	}
	var avg decimal.Decimal
	avg, err = DecimalArithmeticMean(returns)
	if err != nil {
		t.Error(err)
	}
	result, err = DecimalSharpeRatio(returns, decimal.NewFromFloat(-0.0017), avg)
	if err != nil {
		t.Error(err)
	}
	result = result.Round(2)
	if !result.Equal(decimal.NewFromFloat(0.26)) {
		t.Errorf("expected 0.26, received %v", result)
	}
}

func TestDecimalStandardDeviation2(t *testing.T) {
	t.Parallel()
	r := []decimal.Decimal{
		decimal.NewFromInt(9),
		decimal.NewFromInt(2),
		decimal.NewFromInt(5),
		decimal.NewFromInt(4),
		decimal.NewFromInt(12),
		decimal.NewFromInt(7),
	}
	mean, err := DecimalArithmeticMean(r)
	if err != nil {
		t.Error(err)
	}
	var superMean []decimal.Decimal
	for i := range r {
		result := r[i].Sub(mean).Pow(decimal.NewFromInt(2))
		superMean = append(superMean, result)
	}
	superMeany := superMean[0].Add(superMean[1].Add(superMean[2].Add(superMean[3].Add(superMean[4].Add(superMean[5]))))).Div(decimal.NewFromInt(5))
	fSuperMeany, _ := superMeany.Float64()
	manualCalculation := decimal.NewFromFloat(math.Sqrt(fSuperMeany))
	var codeCalcu decimal.Decimal
	codeCalcu, err = DecimalSampleStandardDeviation(r)
	if err != nil {
		t.Error(err)
	}
	if !manualCalculation.Equal(codeCalcu) && codeCalcu.Equal(decimal.NewFromFloat(3.619)) {
		t.Error("expected 3.619")
	}
}

func TestDecimalGeometricAverage(t *testing.T) {
	t.Parallel()
	values := []decimal.Decimal{
		decimal.NewFromInt(1),
		decimal.NewFromInt(2),
		decimal.NewFromInt(3),
		decimal.NewFromInt(4),
		decimal.NewFromInt(5),
		decimal.NewFromInt(6),
		decimal.NewFromInt(7),
		decimal.NewFromInt(8),
	}
	_, err := DecimalGeometricMean(nil)
	if !errors.Is(err, errZeroValue) {
		t.Error(err)
	}
	var mean decimal.Decimal
	mean, err = DecimalGeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if !mean.Equal(decimal.NewFromFloat(3.764350599503129)) {
		t.Errorf("expected %v, received %v", 3.95, mean)
	}

	values = []decimal.Decimal{
		decimal.NewFromInt(15),
		decimal.NewFromInt(12),
		decimal.NewFromInt(13),
		decimal.NewFromInt(19),
		decimal.NewFromInt(10),
	}
	mean, err = DecimalGeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if !mean.Equal(decimal.NewFromFloat(13.477020583645698)) {
		t.Errorf("expected %v, received %v", 13.50, mean)
	}

	values = []decimal.Decimal{
		decimal.NewFromInt(-1),
		decimal.NewFromInt(12),
		decimal.NewFromInt(13),
		decimal.NewFromInt(19),
		decimal.NewFromInt(10),
	}
	mean, err = DecimalGeometricMean(values)
	if !errors.Is(err, errGeometricNegative) {
		t.Error(err)
	}
	if !mean.Equal(decimal.Zero) {
		t.Errorf("expected %v, received %v", 0, mean)
	}
}

func TestDecimalFinancialGeometricAverage(t *testing.T) {
	t.Parallel()
	values := []decimal.Decimal{
		decimal.NewFromInt(1),
		decimal.NewFromInt(2),
		decimal.NewFromInt(3),
		decimal.NewFromInt(4),
		decimal.NewFromInt(5),
		decimal.NewFromInt(6),
		decimal.NewFromInt(7),
		decimal.NewFromInt(8),
	}
	_, err := DecimalFinancialGeometricMean(nil)
	if !errors.Is(err, errZeroValue) {
		t.Error(err)
	}

	var mean decimal.Decimal
	mean, err = DecimalFinancialGeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if !mean.Equal(decimal.NewFromFloat(3.9541639996482028)) {
		t.Errorf("expected %v, received %v", 3.95, mean)
	}

	values = []decimal.Decimal{
		decimal.NewFromInt(15),
		decimal.NewFromInt(12),
		decimal.NewFromInt(13),
		decimal.NewFromInt(19),
		decimal.NewFromInt(10),
	}
	mean, err = DecimalFinancialGeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if !mean.Equal(decimal.NewFromFloat(13.49849123325646)) {
		t.Errorf("expected %v, received %v", 13.50, mean)
	}

	values = []decimal.Decimal{
		decimal.NewFromInt(-1),
		decimal.NewFromInt(12),
		decimal.NewFromInt(13),
		decimal.NewFromInt(19),
		decimal.NewFromInt(10),
	}
	mean, err = DecimalFinancialGeometricMean(values)
	if err != nil {
		t.Error(err)
	}
	if !mean.Equal(decimal.Zero) {
		t.Errorf("expected %v, received %v", 0, mean)
	}

	values = []decimal.Decimal{
		decimal.NewFromInt(-2),
		decimal.NewFromInt(12),
		decimal.NewFromInt(13),
		decimal.NewFromInt(19),
		decimal.NewFromInt(10),
	}
	_, err = DecimalFinancialGeometricMean(values)
	if !errors.Is(err, errNegativeValueOutOfRange) {
		t.Error(err)
	}
}

func TestDecimalArithmeticAverage(t *testing.T) {
	values := []decimal.Decimal{
		decimal.NewFromInt(1),
		decimal.NewFromInt(2),
		decimal.NewFromInt(3),
		decimal.NewFromInt(4),
		decimal.NewFromInt(5),
		decimal.NewFromInt(6),
		decimal.NewFromInt(7),
		decimal.NewFromInt(8),
	}
	_, err := DecimalArithmeticMean(nil)
	if !errors.Is(err, errZeroValue) {
		t.Error(err)
	}
	var avg decimal.Decimal
	avg, err = DecimalArithmeticMean(values)
	if err != nil {
		t.Error(err)
	}
	if !avg.Equal(decimal.NewFromFloat(4.5)) {
		t.Error("expected 4.5")
	}
}
