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

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
local WFArguments = require 'WFArguments'
local WFVectorUtilsImp = require 'WFVectorUtilsImp'

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

local math_sqrt = assert( math.sqrt )
local WFArguments_check = assert( WFArguments.check )
local WFVectorUtilsImp_dotProduct = assert( WFVectorUtilsImp.dotProduct )
local WFVectorUtilsImp_cosineSimilarity = assert( WFVectorUtilsImp.cosineSimilarity )
local WFVectorUtilsImp_dbscan = assert( WFVectorUtilsImp.dbscan )
local WFVectorUtilsImp_normalizeVector = assert( WFVectorUtilsImp.normalizeVector )
local WFVectorUtilsImp_test_dotProductAlternate = assert( WFVectorUtilsImp._test_dotProductAlternate )
local WFVectorUtilsImp_test_cosineSimilarityAlternate = assert( WFVectorUtilsImp._test_cosineSimilarityAlternate )
local WFVectorUtilsImp_test_normalizeVectorAlternate = assert( WFVectorUtilsImp._test_normalizeVectorAlternate )

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

local WFVectorUtils = {}

--------------------------------------------------------------------------------
--@mark Hardware Optimized Implementations
--------------------------------------------------------------------------------

--[[!

	\brief Calculates the dot product between two vectors using hardware-optimized implementation.
		This implementation uses stack allocation and direct Lua table access for maximum performance.
		For better numerical stability with large values, consider normalizing the vectors first.
		Large values (>1e6) may cause precision loss due to floating-point limitations.

	\param a (table) First vector (array of numbers)
	\param b (table) Second vector (array of numbers)

	\return (number) Dot product

--]]

function WFVectorUtils.dotProduct( a, b )

	WFArguments_check( 'table', 'table' )

	return WFVectorUtilsImp_dotProduct( a, b )

end

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

--[[!

	\brief Calculates the cosine similarity between two vectors.
		For better numerical stability with large values, consider normalizing
		the vectors first. Large values (>1e6) may cause precision loss due to
		floating-point limitations.

	\param a (table) First vector (array of numbers)
	\param b (table) Second vector (array of numbers)

	\return (number) Cosine similarity

--]]

function WFVectorUtils.cosineSimilarity( a, b )

	WFArguments_check( 'table', 'table' )

	return WFVectorUtilsImp_cosineSimilarity( a, b )

end

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

--[[!

	\brief Calculates the cosine distance between two vectors.
		For better numerical stability with large values, consider normalizing
		the vectors first. Large values (>1e6) may cause precision loss due to
		floating-point limitations.

	\param a (table) First vector (array of numbers)
	\param b (table) Second vector (array of numbers)

	\return (number) Cosine distance (1 - cosine similarity)

--]]

function WFVectorUtils.cosineDistance( a, b )

	WFArguments_check( 'table', 'table' )

	return 1.0 - WFVectorUtilsImp_cosineSimilarity( a, b )

end

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

--[[!

	\brief Performs DBSCAN clustering on a set of embeddings. For better numerical
		stability with large values, consider normalizing the embeddings first.
		Large values (>1e6) may cause precision loss due to floating-point limitations.

	\param embeddings (table) Array of embeddings, where each embedding is an array of numbers
	\param epsilon (number) Distance threshold
	\param minPoints (number) Minimum number of points required to form a cluster.

	\return (table) Array of clusters, where each cluster is an array of indices into the input embeddings array.
		The indices are 1-based to match Lua's array indexing.

--]]

function WFVectorUtils.dbscan( embeddings, epsilon, minPoints )

	WFArguments_check( 'table', 'number', 'number' )

	if not next( embeddings ) then
		error( "Empty embeddings list" )
	end

	return WFVectorUtilsImp_dbscan( embeddings, epsilon, minPoints )

end

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

--[[!

	\brief Normalizes a vector to unit length using hardware-optimized implementation.
		For better numerical stability with large values, consider normalizing vectors before
		performing other operations. Large values (>1e6) may cause precision loss due to
		floating-point limitations.

	\param vec (table) Vector to normalize (array of numbers)

	\return (table) Normalized vector (unit length)

--]]

function WFVectorUtils.normalizeVector( vec )

	WFArguments_check( 'table' )

	if not next( vec ) then
		error( "Empty vector" )
	end

	return WFVectorUtilsImp_normalizeVector( vec )

end

--------------------------------------------------------------------------------
--@mark Lua-Based Implementations
--------------------------------------------------------------------------------

--[[!

	\brief Calculates the dot product between two vectors directly in Lua.

	\param a (table) First vector
	\param b (table) Second vector

	\return (number) Dot product

--]]

function WFVectorUtils.dotProductLua( a, b )

	WFArguments_check( 'table', 'table' )

	if #a ~= #b then
		error( "Vectors must have the same length" )
	end

	local dot = 0

	for i = 1, #a do
		dot = dot + a[ i ] * b[ i ]
	end

	return dot

end

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

--[[!

	\brief Calculates the cosine similarity between two vectors directly in Lua

	\param a (table) First vector (array of numbers)
	\param b (table) Second vector (array of numbers)

	\return (number) Cosine similarity

--]]

function WFVectorUtils.cosineSimilarityLua( a, b )

	WFArguments_check( 'table', 'table' )

	if #a ~= #b then
		error( "Vectors must have the same length" )
	end

	local dot = 0
	local normA = 0
	local normB = 0

	for i = 1, #a do

		local ai = a[ i ]
		local bi = b[ i ]

		dot = dot + ai * bi
		normA = normA + ai * ai
		normB = normB + bi * bi

	end

	return dot / ( math_sqrt( normA ) * math_sqrt( normB ) )

end

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

--[[!

	\brief Calculates the cosine distance between two vectors directly in Lua

	\param a (table) First vector (array of numbers)
	\param b (table) Second vector (array of numbers)

	\return (number) Cosine distance (1 - cosine similarity)

--]]

function WFVectorUtils.cosineDistanceLua( a, b )

	WFArguments_check( 'table', 'table' )

	return 1.0 - WFVectorUtils.cosineSimilarityLua( a, b )

end

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

--[[!

	\brief Normalizes a vector to unit length using pure Lua implementation.
		This is mainly used for validation and testing purposes.

	\param vec (table) Vector to normalize (array of numbers)

	\return (table) Normalized vector (unit length)

--]]

function WFVectorUtils.normalizeVectorLua( vec )

	WFArguments_check( 'table' )

	if not next( vec ) then
		error( "Empty vector" )
	end

	-- Calculate sum of squares

	local sum = 0

	for i = 1, #vec do
		sum = sum + vec[ i ] * vec[ i ]
	end

	-- Check for zero vector

	if sum == 0 then
		error( "Cannot normalize zero vector" )
	end

	-- Calculate reciprocal of square root

	local inv_norm = 1.0 / math_sqrt( sum )

	-- Create new table with normalized values

	local normalized = {}

	for i = 1, #vec do
		normalized[ i ] = vec[ i ] * inv_norm
	end

	return normalized

end

--------------------------------------------------------------------------------
--@mark Other
--------------------------------------------------------------------------------

--[[!

	\brief Validates that a vector is normalized (has unit length).
		Useful for testing and debugging.

	\param vec (table) Vector to check (array of numbers)
	\param epsilon (number) Optional tolerance for floating-point comparison

	\return (boolean) True if vector is normalized within epsilon

--]]

function WFVectorUtils.isNormalized( vec, epsilon )

	WFArguments_check( 'table', '?number' )

	epsilon = epsilon or 1e-5

	-- Calculate sum of squares

	local sum = 0

	for i = 1, #vec do
		sum = sum + vec[ i ] * vec[ i ]
	end

	-- Check if length is close to 1

	return math.abs( sum - 1.0 ) <= epsilon

end

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

--[[!

	\brief Calculates the dot product between two vectors using original vector-based approach.
		This is kept for testing and comparison purposes.

	\param a (table) First vector
	\param b (table) Second vector

	\return (number) Dot product

--]]

function WFVectorUtils._test_dotProductAlternate( a, b )

	WFArguments_check( 'table', 'table' )

	return WFVectorUtilsImp_test_dotProductAlternate( a, b )

end

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

--[[!

	\brief Calculates the cosine similarity between two vectors using original vector-based approach.
		This is kept for testing and comparison purposes.

	\param a (table) First vector
	\param b (table) Second vector

	\return (number) Cosine similarity

--]]

function WFVectorUtils._test_cosineSimilarityAlternate( a, b )

	WFArguments_check( 'table', 'table' )

	return WFVectorUtilsImp_test_cosineSimilarityAlternate( a, b )

end

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

--[[!

	\brief Normalizes a vector to unit length using original vector-based approach.
		This is kept for testing and comparison purposes.

	\param vec (table) Vector to normalize (array of numbers)

	\return (table) Normalized vector (unit length)

--]]

function WFVectorUtils._test_normalizeVectorAlternate( vec )

	WFArguments_check( 'table' )

	return WFVectorUtilsImp_test_normalizeVectorAlternate( vec )

end

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

return WFVectorUtils
