--[[----------------------------------------------------------------------------

ADOBE CONFIDENTIAL
------------------
Copyright 2025 Adobe
All Rights Reserved.

NOTICE: All information contained herein is, and remains
the property of Adobe and its suppliers, if any. The intellectual
and technical concepts contained herein are proprietary to Adobe
and its suppliers and are protected by all applicable intellectual
property laws, including trade secret and copyright laws.
Dissemination of this information or reproduction of this material
is strictly forbidden unless prior written permission is obtained
from Adobe.

------------------------------------------------------------------------------]]

	-- WF.core.self_test
local WFRxTestCase = require 'WFRxTestCase'

	-- WF.core.utils
local WFMathUtils = require 'WFMathUtils'

--============================================================================--

local WFMathUtilsTest = {}

--------------------------------------------------------------------------------

local function roundToThirdDecimal( value )
	return math.floor( value * 1000 + 0.5 ) / 1000
end

--------------------------------------------------------------------------------

function WFMathUtilsTest.rxTestCalculateStandardDeviation( rxTest )

	-- Test case 1: Standard case
	local values1 = { 2, 4, 4, 4, 5, 5, 7, 9 }
	local mean1 = 5
	local expectedStandardDeviation1 = 2
	local calculatedStandardDeviation1 = WFMathUtils.calculateStandardDeviation( values1, mean1 )
	rxTest:expectEqual( calculatedStandardDeviation1, expectedStandardDeviation1 )

	-- Test case 2: All same values
	local values2 = { 5, 5, 5, 5, 5 }
	local mean2 = 5
	local expectedStandardDeviation2 = 0
	local calculatedStandardDeviation2 = WFMathUtils.calculateStandardDeviation( values2, mean2 )
	rxTest:expectEqual( calculatedStandardDeviation2, expectedStandardDeviation2 )

	-- Test case 3: Including negative numbers
	local values3 = { -3, -1, -1, 0, 1, 1, 3 }
	local mean3 = 0
	local expectedStandardDeviation3 = 1.773
	local calculatedStandardDeviation3 = WFMathUtils.calculateStandardDeviation( values3, mean3 )
	rxTest:expectEqual( roundToThirdDecimal( calculatedStandardDeviation3 ), expectedStandardDeviation3 )

	-- Test case 4: Single value
	local values4 = { 5 }
	local mean4 = 5
	local expectedStandardDeviation4 = 0
	local calculatedStandardDeviation4 = WFMathUtils.calculateStandardDeviation( values4, mean4 )
	rxTest:expectEqual( calculatedStandardDeviation4, expectedStandardDeviation4 )

	-- Test case 5: Empty list
	local values5 = {}
	local mean5 = 0
	local expectedStandardDeviation5 = 0
	local calculatedStandardDeviation5 = WFMathUtils.calculateStandardDeviation( values5, mean5 )
	rxTest:expectEqual( calculatedStandardDeviation5, expectedStandardDeviation5 )

end

--------------------------------------------------------------------------------

function WFMathUtilsTest.rxTestCalculateMedian( rxTest )

	-- Test case 1: Standard case
	local imageCountByDay1 = { 2, 4, 4, 4, 5, 7, 9 }
	local expectedMedian1 = 4
	local calculatedMedian1 = WFMathUtils.calculateMedian( imageCountByDay1 )
	rxTest:expectEqual( calculatedMedian1, expectedMedian1 )

	-- Test case 2: All same values
	local imageCountByDay2 = { 5, 5, 5, 5, 5 }
	local expectedMedian2 = 5
	local calculatedMedian2 = WFMathUtils.calculateMedian( imageCountByDay2 )
	rxTest:expectEqual( calculatedMedian2, expectedMedian2 )

	-- Test case 3: Single value
	local imageCountByDay4 = { 5 }
	local expectedMedian4 = 5
	local calculatedMedian4 = WFMathUtils.calculateMedian( imageCountByDay4 )
	rxTest:expectEqual( calculatedMedian4, expectedMedian4 )

	-- Test case 4: Empty list
	local imageCountByDay5 = {}
	local expectedMedian5 = 0
	local calculatedMedian5 = WFMathUtils.calculateMedian( imageCountByDay5 )
	rxTest:expectEqual( calculatedMedian5, expectedMedian5 )

end

--------------------------------------------------------------------------------

function WFMathUtilsTest.rxTestStandardizeValues( rxTest )

	-- Test case 1: Standard case
	local values1 = { 2, 4, 4, 4, 5, 5, 7, 9 }
	local mean1 = 5
	local stdDev1 = 2
	local expectedStandardizedValues1 = { -1.5, -0.5, -0.5, -0.5, 0, 0, 1, 2 }
	local calculatedStandardizedValues1 = WFMathUtils.standardizeValues( values1, mean1, stdDev1 )
	rxTest:expectEqual( calculatedStandardizedValues1, expectedStandardizedValues1 )

	-- Test case 2: All same values
	local values2 = { 5, 5, 5, 5, 5 }
	local mean2 = 5
	local stdDev2 = 0
	local expectedStandardizedValues2 = { 0, 0, 0, 0, 0 }
	local calculatedStandardizedValues2 = WFMathUtils.standardizeValues( values2, mean2, stdDev2 )
	rxTest:expectEqual( calculatedStandardizedValues2, expectedStandardizedValues2 )

	-- Test case 3: Including negative numbers
	local values3 = { -3, -1, -1, 0, 1, 1, 3 }
	local mean3 = 0
	local stdDev3 = 2
	local expectedStandardizedValues3 = { -1.5, -0.5, -0.5, 0, 0.5, 0.5, 1.5 }
	local calculatedStandardizedValues3 = WFMathUtils.standardizeValues( values3, mean3, stdDev3 )
	rxTest:expectEqual( calculatedStandardizedValues3, expectedStandardizedValues3 )

	-- Test case 4: Single value
	local values4 = { 5 }
	local mean4 = 5
	local stdDev4 = 0.0001 -- Avoid division by zero
	local expectedStandardizedValues4 = { 0 }
	local calculatedStandardizedValues4 = WFMathUtils.standardizeValues( values4, mean4, stdDev4 )
	rxTest:expectEqual( calculatedStandardizedValues4, expectedStandardizedValues4 )

	-- Test case 5: Empty list
	local values5 = {}
	local mean5 = 0
	local stdDev5 = 1
	local expectedStandardizedValues5 = { }
	local calculatedStandardizedValues5 = WFMathUtils.standardizeValues( values5, mean5, stdDev5 )
	rxTest:expectEqual( calculatedStandardizedValues5, expectedStandardizedValues5 )

end

--------------------------------------------------------------------------------

function WFMathUtilsTest.rxTestCalculateQuartiles( rxTest )

	-- Test case 1: Standard case
	local imageCountByDay1 = { 2, 4, 4, 4, 5, 5, 7, 9 }
	local expectedQ1_1 = 4
	local expectedQ3_1 = 6
	local calculatedQ1_1, calculatedQ3_1 = WFMathUtils.calculateQuartiles( imageCountByDay1 )
	rxTest:expectEqual( calculatedQ1_1, expectedQ1_1 )
	rxTest:expectEqual( calculatedQ3_1, expectedQ3_1 )

	-- Test case 2: All same values
	local imageCountByDay2 = { 5, 5, 5, 5, 5 }
	local expectedQ1_2 = 5
	local expectedQ3_2 = 5
	local calculatedQ1_2, calculatedQ3_2 = WFMathUtils.calculateQuartiles( imageCountByDay2 )
	rxTest:expectEqual( calculatedQ1_2, expectedQ1_2 )
	rxTest:expectEqual( calculatedQ3_2, expectedQ3_2 )

	-- Test case 3: Including negative numbers
	local imageCountByDay3 = { -3, -1, -1, 0, 1, 1, 3 }
	local expectedQ1_3 = -1
	local expectedQ3_3 = 1
	local calculatedQ1_3, calculatedQ3_3 = WFMathUtils.calculateQuartiles( imageCountByDay3 )
	rxTest:expectEqual( calculatedQ1_3, expectedQ1_3 )
	rxTest:expectEqual( calculatedQ3_3, expectedQ3_3 )

end


---------------------------------------------------------------------------------

function WFMathUtilsTest.rxTestHaversineDistance( rxTest )

	-- Test case 1: Standard case
	local lat1_1, lon1_1 = 36.12, -86.67
	local lat2_1, lon2_1 = 33.94, -118.40
	local expectedDistance1 = 2889.61 -- in kilometers
	local calculatedDistance1 = WFMathUtils.haversineDistance( lat1_1, lon1_1, lat2_1, lon2_1 )
	rxTest:expectEqual( math.floor( calculatedDistance1 * 100 ) / 100, expectedDistance1 )

	-- Test case 2: Same location
	local lat1_2, lon1_2 = 0, 0
	local lat2_2, lon2_2 = 0, 0
	local expectedDistance2 = 0
	local calculatedDistance2 = WFMathUtils.haversineDistance( lat1_2, lon1_2, lat2_2, lon2_2 )
	rxTest:expectEqual( calculatedDistance2, expectedDistance2 )

	-- Test case 3: Opposite points on the globe
	local lat1_3, lon1_3 = 0, 0
	local lat2_3, lon2_3 = 0, 180
	local expectedDistance3 = 20037.07 -- in kilometers (half the Earth's circumference)
	local calculatedDistance3 = WFMathUtils.haversineDistance( lat1_3, lon1_3, lat2_3, lon2_3 )
	rxTest:expectEqual( math.floor( calculatedDistance3 * 100 ) / 100, expectedDistance3 )

	-- Test case 4: North Pole to South Pole
	local lat1_4, lon1_4 = 90, 0
	local lat2_4, lon2_4 = -90, 0
	local expectedDistance4 = 20037.07 -- in kilometers (half the Earth's circumference)
	local calculatedDistance4 = WFMathUtils.haversineDistance( lat1_4, lon1_4, lat2_4, lon2_4 )
	rxTest:expectEqual( math.floor( calculatedDistance4 * 100 ) / 100, expectedDistance4 )

end

--------------------------------------------------------------------------------

function WFMathUtilsTest.rxTestGetHaversineDistances( rxTest )

	-- Test case 1: Standard case
	local latitudes1 = { 36.12, 33.94 }
	local longitudes1 = { -86.67, -118.40 }
	local latitudesSum1 = 36.12 + 33.94
	local longitudesSum1 = -86.67 + -118.40
	local count1 = 2
	local expectedDistances1 = { 1439.92 }
	local calculatedDistances1 = WFMathUtils.getHaversineDistances( latitudes1, latitudesSum1, longitudes1, longitudesSum1, count1 )
	rxTest:expectEqual( math.floor( calculatedDistances1[1] * 100 ) / 100, expectedDistances1[1] )

	-- Test case 2: Same location
	local latitudes2 = { 0, 0 }
	local longitudes2 = { 0, 0 }
	local latitudesSum2 = 0
	local longitudesSum2 = 0
	local count2 = 2
	local expectedDistances2 = { 0 }
	local calculatedDistances2 = WFMathUtils.getHaversineDistances( latitudes2, latitudesSum2, longitudes2, longitudesSum2, count2 )
	rxTest:expectEqual( calculatedDistances2[1], expectedDistances2[1] )

	-- Test case 3: Opposite points on the globe
	local latitudes3 = { 0, 0 }
	local longitudes3 = { 0, 180 }
	local latitudesSum3 = 0
	local longitudesSum3 = 180
	local count3 = 2
	local expectedDistances3 = { 10018.53 }
	local calculatedDistances3 = WFMathUtils.getHaversineDistances( latitudes3, latitudesSum3, longitudes3, longitudesSum3, count3 )
	rxTest:expectEqual( math.floor( calculatedDistances3[1] * 100 ) / 100, expectedDistances3[1] )

	-- Test case 4: North Pole to South Pole
	local latitudes4 = { 90, -90 }
	local longitudes4 = { 0, 0 }
	local latitudesSum4 = 90 + -90
	local longitudesSum4 = 0
	local count4 = 2
	local expectedDistances4 = { 10018.53 }
	local calculatedDistances4 = WFMathUtils.getHaversineDistances( latitudes4, latitudesSum4, longitudes4, longitudesSum4, count4 )
	rxTest:expectEqual( math.floor( calculatedDistances4[1] * 100 ) / 100, expectedDistances4[1] )

end

--------------------------------------------------------------------------------

function WFMathUtilsTest.rxTestRoundToTenths( rxTest )

	-- Test case 1: Standard case
	local value1 = 123.456
	local expectedRoundedValue1 = 123.5
	local calculatedRoundedValue1 = WFMathUtils.roundToTenths( value1 )
	rxTest:expectEqual( calculatedRoundedValue1, expectedRoundedValue1 )

	-- Test case 2: Rounding down
	local value2 = 123.454
	local expectedRoundedValue2 = 123.5
	local calculatedRoundedValue2 = WFMathUtils.roundToTenths( value2 )
	rxTest:expectEqual( calculatedRoundedValue2, expectedRoundedValue2 )

	-- Test case 3: Negative value
	local value3 = -123.456
	local expectedRoundedValue3 = -123.5
	local calculatedRoundedValue3 = WFMathUtils.roundToTenths( value3 )
	rxTest:expectEqual( calculatedRoundedValue3, expectedRoundedValue3 )

	-- Test case 4: Zero value
	local value4 = 0
	local expectedRoundedValue4 = 0
	local calculatedRoundedValue4 = WFMathUtils.roundToTenths( value4 )
	rxTest:expectEqual( calculatedRoundedValue4, expectedRoundedValue4 )

end

--------------------------------------------------------------------------------

function WFMathUtilsTest.rxTestGetDatePeriodIndex( rxTest )

	-- Test case 1: Standard case
	local date1 = 20230115 -- January 15, 2023
	local spanLength1 = 3
	local expectedYear1 = 2023
	local expectedSpan1 = 1
	local calculatedYear1, calculatedSpan1 = WFMathUtils.getDatePeriodIndex( date1, spanLength1 )
	rxTest:expectEqual( calculatedYear1, expectedYear1 )
	rxTest:expectEqual( calculatedSpan1, expectedSpan1 )

	-- Test case 2: Middle of the year
	local date2 = 20230615 -- June 15, 2023
	local spanLength2 = 3
	local expectedYear2 = 2023
	local expectedSpan2 = 2
	local calculatedYear2, calculatedSpan2 = WFMathUtils.getDatePeriodIndex( date2, spanLength2 )
	rxTest:expectEqual( calculatedYear2, expectedYear2 )
	rxTest:expectEqual( calculatedSpan2, expectedSpan2 )

	-- Test case 3: End of the year
	local date3 = 20231215 -- December 15, 2023
	local spanLength3 = 3
	local expectedYear3 = 2023
	local expectedSpan3 = 4
	local calculatedYear3, calculatedSpan3 = WFMathUtils.getDatePeriodIndex( date3, spanLength3 )
	rxTest:expectEqual( calculatedYear3, expectedYear3 )
	rxTest:expectEqual( calculatedSpan3, expectedSpan3 )

	-- Test case 4: Different span length
	local date4 = 20230115 -- January 15, 2023
	local spanLength4 = 6
	local expectedYear4 = 2023
	local expectedSpan4 = 1
	local calculatedYear4, calculatedSpan4 = WFMathUtils.getDatePeriodIndex( date4, spanLength4 )
	rxTest:expectEqual( calculatedYear4, expectedYear4 )
	rxTest:expectEqual( calculatedSpan4, expectedSpan4 )

	-- Test case 5: Different year
	local date5 = 20240115 -- January 15, 2024
	local spanLength5 = 3
	local expectedYear5 = 2024
	local expectedSpan5 = 1
	local calculatedYear5, calculatedSpan5 = WFMathUtils.getDatePeriodIndex( date5, spanLength5 )
	rxTest:expectEqual( calculatedYear5, expectedYear5 )
	rxTest:expectEqual( calculatedSpan5, expectedSpan5 )

end

--------------------------------------------------------------------------------

return WFRxTestCase.wrapSuite( 'WFMathUtilsTest', WFMathUtilsTest )
