-- Copyright (c) 2019-2022 David Vogel
--
-- This software is released under the MIT License.
-- https://opensource.org/licenses/MIT

local Hilbert = {}

---Rotate/flip quadrant.
---@param n integer
---@param x integer
---@param y integer
---@param rx boolean
---@param ry boolean
---@return integer
---@return integer
local function hilbertRotate(n, x, y, rx, ry)
	if not ry then
		if rx then
			x = n - 1 - x
			y = n - 1 - y
		end

		x, y = y, x
	end
	return x, y
end

---Maps t in the range of [0, (2^potSize)^2-1] to a position on the hilbert curve with the side length of 2^potSize (Power of two size).
---@param t integer
---@param potSize integer
---@return integer
---@return integer
function Hilbert.Map(t, potSize)
	local size = math.pow(2, potSize)
	local x, y = 0, 0

	if t < 0 or t >= size * size then
		error("variable t is outside of the range")
	end

	for i = 0, potSize - 1, 1 do
		local iPOT = math.pow(2, i)
		local rx = bit.band(t, 2) == 2
		local ry = bit.band(t, 1) == 1
		if rx then
			ry = not ry
		end

		x, y = hilbertRotate(iPOT, x, y, rx, ry)

		if rx then
			x = x + iPOT
		end
		if ry then
			y = y + iPOT
		end

		t = math.floor(t / 4)
	end

	return x, y
end

return Hilbert
