local int64 = require("../../init")

local rshift_n = int64.from_pair(0x80000000, 0x00000000)
for i = 0, 63 do
	local new = int64.rshift(rshift_n, i)
	local expected_bin = string.rep("0", i) .. "1" .. string.rep("0", 63 - i)
	assert(int64.to_bin_string(new) == expected_bin, `{int64.to_hex_string(rshift_n)} >>< {i} was wrong`)
end
assert(int64.rshift(rshift_n, 64) == vector.zero, `{int64.to_hex_string(rshift_n)} >>> 64 was wrong`)

local rshift_1 = int64.from_u32(0xcccccccc) -- 11001100_11001100_11001100_11001100
assert(int64.rshift(rshift_1, 0) == rshift_1, "0xcccccccc >>> 0 was wrong")

assert(int64.to_hex_string(int64.rshift(rshift_1, 01)) == "0000000066666666", "0xcccccccc >>> 01 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 02)) == "0000000033333333", "0xcccccccc >>> 02 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 03)) == "0000000019999999", "0xcccccccc >>> 03 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 04)) == "000000000ccccccc", "0xcccccccc >>> 04 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 05)) == "0000000006666666", "0xcccccccc >>> 05 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 06)) == "0000000003333333", "0xcccccccc >>> 06 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 07)) == "0000000001999999", "0xcccccccc >>> 07 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 08)) == "0000000000cccccc", "0xcccccccc >>> 08 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 09)) == "0000000000666666", "0xcccccccc >>> 09 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 10)) == "0000000000333333", "0xcccccccc >>> 10 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 11)) == "0000000000199999", "0xcccccccc >>> 11 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 12)) == "00000000000ccccc", "0xcccccccc >>> 12 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 13)) == "0000000000066666", "0xcccccccc >>> 13 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 14)) == "0000000000033333", "0xcccccccc >>> 14 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 15)) == "0000000000019999", "0xcccccccc >>> 15 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 16)) == "000000000000cccc", "0xcccccccc >>> 16 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 17)) == "0000000000006666", "0xcccccccc >>> 17 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 18)) == "0000000000003333", "0xcccccccc >>> 18 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 19)) == "0000000000001999", "0xcccccccc >>> 19 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 20)) == "0000000000000ccc", "0xcccccccc >>> 20 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 21)) == "0000000000000666", "0xcccccccc >>> 21 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 22)) == "0000000000000333", "0xcccccccc >>> 22 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 23)) == "0000000000000199", "0xcccccccc >>> 23 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 24)) == "00000000000000cc", "0xcccccccc >>> 24 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 25)) == "0000000000000066", "0xcccccccc >>> 25 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 26)) == "0000000000000033", "0xcccccccc >>> 26 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 27)) == "0000000000000019", "0xcccccccc >>> 27 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 28)) == "000000000000000c", "0xcccccccc >>> 28 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 29)) == "0000000000000006", "0xcccccccc >>> 29 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 30)) == "0000000000000003", "0xcccccccc >>> 30 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 31)) == "0000000000000001", "0xcccccccc >>> 31 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_1, 32)) == "0000000000000000", "0xcccccccc >>> 32 was wrong")
for i = 33, 64 do
	assert(int64.to_hex_string(int64.rshift(rshift_1, i)) == "0000000000000000", `0xcccccccc >>> {i} was wrong`)
end

local rshift_2 = int64.from_u32(0x33333333) -- 00110011_00110011_00110011_00110011
assert(int64.rshift(rshift_2, 0) == rshift_2, "0x33333333 >>> 0 was wrong")

assert(int64.to_hex_string(int64.rshift(rshift_2, 01)) == "0000000019999999", "0x33333333 >>> 01 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 02)) == "000000000ccccccc", "0x33333333 >>> 02 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 03)) == "0000000006666666", "0x33333333 >>> 03 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 04)) == "0000000003333333", "0x33333333 >>> 04 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 05)) == "0000000001999999", "0x33333333 >>> 05 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 06)) == "0000000000cccccc", "0x33333333 >>> 06 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 07)) == "0000000000666666", "0x33333333 >>> 07 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 08)) == "0000000000333333", "0x33333333 >>> 08 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 09)) == "0000000000199999", "0x33333333 >>> 09 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 10)) == "00000000000ccccc", "0x33333333 >>> 10 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 11)) == "0000000000066666", "0x33333333 >>> 11 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 12)) == "0000000000033333", "0x33333333 >>> 12 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 13)) == "0000000000019999", "0x33333333 >>> 13 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 14)) == "000000000000cccc", "0x33333333 >>> 14 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 15)) == "0000000000006666", "0x33333333 >>> 15 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 16)) == "0000000000003333", "0x33333333 >>> 16 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 17)) == "0000000000001999", "0x33333333 >>> 17 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 18)) == "0000000000000ccc", "0x33333333 >>> 18 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 19)) == "0000000000000666", "0x33333333 >>> 19 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 20)) == "0000000000000333", "0x33333333 >>> 20 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 21)) == "0000000000000199", "0x33333333 >>> 21 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 22)) == "00000000000000cc", "0x33333333 >>> 22 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 23)) == "0000000000000066", "0x33333333 >>> 23 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 24)) == "0000000000000033", "0x33333333 >>> 24 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 25)) == "0000000000000019", "0x33333333 >>> 25 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 26)) == "000000000000000c", "0x33333333 >>> 26 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 27)) == "0000000000000006", "0x33333333 >>> 27 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 28)) == "0000000000000003", "0x33333333 >>> 28 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 29)) == "0000000000000001", "0x33333333 >>> 29 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 30)) == "0000000000000000", "0x33333333 >>> 30 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 31)) == "0000000000000000", "0x33333333 >>> 31 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_2, 32)) == "0000000000000000", "0x33333333 >>> 32 was wrong")
for i = 33, 64 do
	assert(int64.to_hex_string(int64.rshift(rshift_2, i)) == "0000000000000000", `0x33333333 >>> {i} was wrong`)
end

local rshift_3 = int64.from_pair(0xdeadbeef, 0x77c0ffee) -- 11011110_10101101_10111110_11101111_00000111_11000000_11111111_11101110
assert(int64.rshift(rshift_3, 0) == rshift_3, "0xdeadbeef77c0ffee >>> 0 was wrong")

assert(int64.to_hex_string(int64.rshift(rshift_3, 01)) == "6f56df77bbe07ff7", "0xdeadbeef77c0ffee >>> 01 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 02)) == "37ab6fbbddf03ffb", "0xdeadbeef77c0ffee >>> 02 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 03)) == "1bd5b7ddeef81ffd", "0xdeadbeef77c0ffee >>> 03 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 04)) == "0deadbeef77c0ffe", "0xdeadbeef77c0ffee >>> 04 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 05)) == "06f56df77bbe07ff", "0xdeadbeef77c0ffee >>> 05 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 06)) == "037ab6fbbddf03ff", "0xdeadbeef77c0ffee >>> 06 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 07)) == "01bd5b7ddeef81ff", "0xdeadbeef77c0ffee >>> 07 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 08)) == "00deadbeef77c0ff", "0xdeadbeef77c0ffee >>> 08 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 09)) == "006f56df77bbe07f", "0xdeadbeef77c0ffee >>> 09 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 10)) == "0037ab6fbbddf03f", "0xdeadbeef77c0ffee >>> 10 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 11)) == "001bd5b7ddeef81f", "0xdeadbeef77c0ffee >>> 11 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 12)) == "000deadbeef77c0f", "0xdeadbeef77c0ffee >>> 12 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 13)) == "0006f56df77bbe07", "0xdeadbeef77c0ffee >>> 13 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 14)) == "00037ab6fbbddf03", "0xdeadbeef77c0ffee >>> 14 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 15)) == "0001bd5b7ddeef81", "0xdeadbeef77c0ffee >>> 15 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 16)) == "0000deadbeef77c0", "0xdeadbeef77c0ffee >>> 16 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 17)) == "00006f56df77bbe0", "0xdeadbeef77c0ffee >>> 17 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 18)) == "000037ab6fbbddf0", "0xdeadbeef77c0ffee >>> 18 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 19)) == "00001bd5b7ddeef8", "0xdeadbeef77c0ffee >>> 19 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 20)) == "00000deadbeef77c", "0xdeadbeef77c0ffee >>> 20 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 21)) == "000006f56df77bbe", "0xdeadbeef77c0ffee >>> 21 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 22)) == "0000037ab6fbbddf", "0xdeadbeef77c0ffee >>> 22 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 23)) == "000001bd5b7ddeef", "0xdeadbeef77c0ffee >>> 23 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 24)) == "000000deadbeef77", "0xdeadbeef77c0ffee >>> 24 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 25)) == "0000006f56df77bb", "0xdeadbeef77c0ffee >>> 25 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 26)) == "00000037ab6fbbdd", "0xdeadbeef77c0ffee >>> 26 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 27)) == "0000001bd5b7ddee", "0xdeadbeef77c0ffee >>> 27 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 28)) == "0000000deadbeef7", "0xdeadbeef77c0ffee >>> 28 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 29)) == "00000006f56df77b", "0xdeadbeef77c0ffee >>> 29 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 30)) == "000000037ab6fbbd", "0xdeadbeef77c0ffee >>> 30 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 31)) == "00000001bd5b7dde", "0xdeadbeef77c0ffee >>> 31 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 32)) == "00000000deadbeef", "0xdeadbeef77c0ffee >>> 32 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 33)) == "000000006f56df77", "0xdeadbeef77c0ffee >>> 33 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 34)) == "0000000037ab6fbb", "0xdeadbeef77c0ffee >>> 34 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 35)) == "000000001bd5b7dd", "0xdeadbeef77c0ffee >>> 35 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 36)) == "000000000deadbee", "0xdeadbeef77c0ffee >>> 36 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 37)) == "0000000006f56df7", "0xdeadbeef77c0ffee >>> 37 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 38)) == "00000000037ab6fb", "0xdeadbeef77c0ffee >>> 38 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 39)) == "0000000001bd5b7d", "0xdeadbeef77c0ffee >>> 39 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 40)) == "0000000000deadbe", "0xdeadbeef77c0ffee >>> 40 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 41)) == "00000000006f56df", "0xdeadbeef77c0ffee >>> 41 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 42)) == "000000000037ab6f", "0xdeadbeef77c0ffee >>> 42 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 43)) == "00000000001bd5b7", "0xdeadbeef77c0ffee >>> 43 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 44)) == "00000000000deadb", "0xdeadbeef77c0ffee >>> 44 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 45)) == "000000000006f56d", "0xdeadbeef77c0ffee >>> 45 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 46)) == "0000000000037ab6", "0xdeadbeef77c0ffee >>> 46 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 47)) == "000000000001bd5b", "0xdeadbeef77c0ffee >>> 47 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 48)) == "000000000000dead", "0xdeadbeef77c0ffee >>> 48 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 49)) == "0000000000006f56", "0xdeadbeef77c0ffee >>> 49 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 50)) == "00000000000037ab", "0xdeadbeef77c0ffee >>> 50 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 51)) == "0000000000001bd5", "0xdeadbeef77c0ffee >>> 51 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 52)) == "0000000000000dea", "0xdeadbeef77c0ffee >>> 52 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 53)) == "00000000000006f5", "0xdeadbeef77c0ffee >>> 53 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 54)) == "000000000000037a", "0xdeadbeef77c0ffee >>> 54 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 55)) == "00000000000001bd", "0xdeadbeef77c0ffee >>> 55 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 56)) == "00000000000000de", "0xdeadbeef77c0ffee >>> 56 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 57)) == "000000000000006f", "0xdeadbeef77c0ffee >>> 57 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 58)) == "0000000000000037", "0xdeadbeef77c0ffee >>> 58 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 59)) == "000000000000001b", "0xdeadbeef77c0ffee >>> 59 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 60)) == "000000000000000d", "0xdeadbeef77c0ffee >>> 60 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 61)) == "0000000000000006", "0xdeadbeef77c0ffee >>> 61 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 62)) == "0000000000000003", "0xdeadbeef77c0ffee >>> 62 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 63)) == "0000000000000001", "0xdeadbeef77c0ffee >>> 63 was wrong")
assert(int64.to_hex_string(int64.rshift(rshift_3, 64)) == "0000000000000000", "0xdeadbeef77c0ffee >>> 64 was wrong")

for i = -64, -1 do
	local shifted = pcall(int64.rshift, int64.ONE, i)
	assert(not shifted, "rshift allowed negative arguments when it shouldn't")
end

for i = 65, 128 do
	local shifted = int64.rshift(int64.MAX, i)
	assert(shifted == vector.zero, "rshift did not work for values greater than 64")
end

return {}


local int64 = require("../../init")

local band_1 = int64.band(int64.from_pair(0, 0), int64.from_pair(0, 0))
assert(int64.to_hex_string(band_1) == "0000000000000000", "band_1 was wrong")

local band_2 = int64.band(int64.from_pair(0, 1), int64.from_pair(0, 1))
assert(int64.to_hex_string(band_2) == "0000000000000001", "band_2 was wrong")

local band_3 = int64.band(int64.from_pair(0, 0b1111), int64.from_pair(0, 0b0010))
assert(int64.to_hex_string(band_3) == "0000000000000002", "band_3 was wrong")

local band_4 = int64.band(int64.from_pair(0, 0b1010), int64.from_pair(0, 0b0101))
assert(int64.to_hex_string(band_4) == "0000000000000000", "band_4 was wrong")

local band_5 = int64.band(int64.from_pair(0, 0xdeadbeef), int64.from_pair(0, 0xdeadbeef))
assert(int64.to_hex_string(band_5) == "00000000deadbeef", "band_5 was wrong")

local band_6 = int64.band(int64.from_pair(0x10203040, 0x50607080), int64.from_pair(0x11223344, 0x55667788))
assert(int64.to_hex_string(band_6) == "1020304050607080", "band_6 was wrong")

for i = 0, 255 do
	for j = 0, 255 do
		local u32 = bit32.band(i, j)
		local u64 = int64.band(int64.from_pair(0, i), int64.from_pair(0, j))
		assert(int64.to_hex_string(u64) == string.format("%016x", u32), `band was wrong for {i} AND {j}`)
	end
end

local btest_1 = int64.btest(int64.from_pair(0, 0), int64.from_pair(0, 0))
assert(btest_1 == false, "btest_1 was wrong")

local btest_2 = int64.btest(int64.from_pair(0, 1), int64.from_pair(0, 1))
assert(btest_2 == true, "btest_2 was wrong")

local btest_3 = int64.btest(int64.from_pair(0, 0b1111), int64.from_pair(0, 0b0010))
assert(btest_3 == true, "btest_3 was wrong")

local btest_4 = int64.btest(int64.from_pair(0, 0b1010), int64.from_pair(0, 0b0101))
assert(btest_4 == false, "btest_4 was wrong")

local btest_5 = int64.btest(int64.from_pair(0, 0xdeadbeef), int64.from_pair(0, 0xdeadbeef))
assert(btest_5 == true, "btest_5 was wrong")

local btest_6 = int64.btest(int64.from_pair(0x10203040, 0x50607080), int64.from_pair(0x11223344, 0x55667788))
assert(btest_6 == true, "btest_6 was wrong")

for i = 0, 255 do
	for j = 0, 255 do
		local u32 = bit32.btest(i, j)
		local u64 = int64.btest(int64.from_pair(0, i), int64.from_pair(0, j))
		assert(u64 == u32, `btest was wrong for ({i}, {j})`)
	end
end

local bor_1 = int64.bor(int64.from_pair(0, 0), int64.from_pair(0, 0))
assert(int64.to_hex_string(bor_1) == "0000000000000000", "bor_1 was wrong")

local bor_2 = int64.bor(int64.from_pair(0, 1), int64.from_pair(0, 1))
assert(int64.to_hex_string(bor_2) == "0000000000000001", "bor_2 was wrong")

local bor_3 = int64.bor(int64.from_pair(0, 0b0010), int64.from_pair(0, 0b0100))
assert(int64.to_hex_string(bor_3) == "0000000000000006", "bor_3 was wrong")

local bor_4 = int64.bor(int64.from_pair(0, 0b1010), int64.from_pair(0, 0b0101))
assert(int64.to_hex_string(bor_4) == "000000000000000f", "bor_4 was wrong")

local bor_5 = int64.bor(int64.from_pair(0, 0xdeadbeef), int64.from_pair(0, 0xdeadbeef))
assert(int64.to_hex_string(bor_5) == "00000000deadbeef", "bor_5 was wrong")

local bor_6 = int64.bor(int64.from_pair(0x10203040, 0x50607080), int64.from_pair(0x11223344, 0x55667788))
assert(int64.to_hex_string(bor_6) == "1122334455667788", "bor_6 was wrong")

for i = 0, 255 do
	for j = 0, 255 do
		local u32 = bit32.bor(i, j)
		local u64 = int64.bor(int64.from_pair(0, i), int64.from_pair(0, j))
		assert(int64.to_hex_string(u64) == string.format("%016x", u32), `bor was wrong for {i} OR {j}`)
	end
end

local bxor_1 = int64.bxor(int64.from_pair(0, 0), int64.from_pair(0, 0))
assert(int64.to_hex_string(bxor_1) == "0000000000000000", "bxor_1 was wrong")

local bxor_2 = int64.bxor(int64.from_pair(0, 1), int64.from_pair(0, 1))
assert(int64.to_hex_string(bxor_2) == "0000000000000000", "bxor_2 was wrong")

local bxor_3 = int64.bxor(int64.from_pair(0, 0b0010), int64.from_pair(0, 0b0100))
assert(int64.to_hex_string(bxor_3) == "0000000000000006", "bxor_3 was wrong")

local bxor_4 = int64.bxor(int64.from_pair(0, 0b1010), int64.from_pair(0, 0b0101))
assert(int64.to_hex_string(bxor_4) == "000000000000000f", "bxor_4 was wrong")

local bxor_5 = int64.bxor(int64.from_pair(0, 0xdeadbeef), int64.from_pair(0, 0xdeadbeef))
assert(int64.to_hex_string(bxor_5) == "0000000000000000", "bxor_5 was wrong")

local bxor_6 = int64.bxor(int64.from_pair(0x10203040, 0x50607080), int64.from_pair(0x11223344, 0x55667788))
assert(int64.to_hex_string(bxor_6) == "0102030405060708", "bxor_6 was wrong")

for i = 0, 255 do
	for j = 0, 255 do
		local u32 = bit32.bxor(i, j)
		local u64 = int64.bxor(int64.from_pair(0, i), int64.from_pair(0, j))
		assert(int64.to_hex_string(u64) == string.format("%016x", u32), `bxor was wrong for {i} XOR {j}`)
	end
end

local bnot_1 = int64.bnot(int64.from_pair(0, 0))
assert(int64.to_hex_string(bnot_1) == "ffffffffffffffff", "bnot_1 was wrong")

local bnot_2 = int64.bnot(int64.from_pair(0, 1))
assert(int64.to_hex_string(bnot_2) == "fffffffffffffffe", "bnot_2 was wrong")

local bnot_3 = int64.bnot(int64.from_pair(0, 0b0010))
assert(int64.to_hex_string(bnot_3) == "fffffffffffffffd", "bnot_3 was wrong")

local bnot_4 = int64.bnot(int64.from_pair(0, 0b1010))
assert(int64.to_hex_string(bnot_4) == "fffffffffffffff5", "bnot_4 was wrong")

local bnot_5 = int64.bnot(int64.from_pair(0, 0xdeadbeef))
assert(int64.to_hex_string(bnot_5) == "ffffffff21524110", "bnot_5 was wrong")

local bnot_6 = int64.bnot(int64.from_pair(0x1a2b3c4d, 0x5e6f7988))
assert(int64.to_hex_string(bnot_6) == "e5d4c3b2a1908677", "bnot_6 was wrong")

for i = 0, 255 do
	local u32 = bit32.bnot(i)
	local u64 = int64.bnot(int64.from_pair(0, i))
	-- Because `bit32.bnot` is 32-bit, we have to manually fill in the upper
	-- bits else our comparison is wrong.
	assert(int64.to_hex_string(u64) == `ffffffff{string.format("%08x", u32)}`, `bnot was wrong for NOT {i}`)
end

assert(int64.countrz(vector.zero) == 64, "countrz was wrong for 0")
assert(int64.countlz(vector.zero) == 64, "countlz was wrong for 0")

for i = 0, 52 do
	local u64 = int64.from_f64(2 ^ i)
	assert(int64.countrz(u64) == i, `countrz was wrong for 2 ^ {i}`)
	-- We have a bit set, so it's going to be off by one from what you may
	-- expect.
	assert(int64.countlz(u64) == 63 - i, `countlz was wrong for 2 ^ {i}`)
end

assert(int64.countrz(int64.from_pair(0x00200000, 0x00000000)) == 53, "countrz was wrong for 2 ^ 53")
assert(int64.countlz(int64.from_pair(0x00200000, 0x00000000)) == 10, "countlz was wrong for 2 ^ 53")

assert(int64.countrz(int64.from_pair(0x00400000, 0x00000000)) == 54, "countrz was wrong for 2 ^ 54")
assert(int64.countlz(int64.from_pair(0x00400000, 0x00000000)) == 9, "countlz was wrong for 2 ^ 54")

assert(int64.countrz(int64.from_pair(0x00800000, 0x00000000)) == 55, "countrz was wrong for 2 ^ 55")
assert(int64.countlz(int64.from_pair(0x00800000, 0x00000000)) == 8, "countlz was wrong for 2 ^ 55")

assert(int64.countrz(int64.from_pair(0x01000000, 0x00000000)) == 56, "countrz was wrong for 2 ^ 56")
assert(int64.countlz(int64.from_pair(0x01000000, 0x00000000)) == 7, "countlz was wrong for 2 ^ 56")

assert(int64.countrz(int64.from_pair(0x02000000, 0x00000000)) == 57, "countrz was wrong for 2 ^ 57")
assert(int64.countlz(int64.from_pair(0x02000000, 0x00000000)) == 6, "countlz was wrong for 2 ^ 57")

assert(int64.countrz(int64.from_pair(0x04000000, 0x00000000)) == 58, "countrz was wrong for 2 ^ 58")
assert(int64.countlz(int64.from_pair(0x04000000, 0x00000000)) == 5, "countlz was wrong for 2 ^ 58")

assert(int64.countrz(int64.from_pair(0x08000000, 0x00000000)) == 59, "countrz was wrong for 2 ^ 59")
assert(int64.countlz(int64.from_pair(0x08000000, 0x00000000)) == 4, "countlz was wrong for 2 ^ 59")

assert(int64.countrz(int64.from_pair(0x10000000, 0x00000000)) == 60, "countrz was wrong for 2 ^ 60")
assert(int64.countlz(int64.from_pair(0x10000000, 0x00000000)) == 3, "countlz was wrong for 2 ^ 60")

assert(int64.countrz(int64.from_pair(0x20000000, 0x00000000)) == 61, "countrz was wrong for 2 ^ 61")
assert(int64.countlz(int64.from_pair(0x20000000, 0x00000000)) == 2, "countlz was wrong for 2 ^ 61")

assert(int64.countrz(int64.from_pair(0x40000000, 0x00000000)) == 62, "countrz was wrong for 2 ^ 62")
assert(int64.countlz(int64.from_pair(0x40000000, 0x00000000)) == 1, "countlz was wrong for 2 ^ 62")

assert(int64.countrz(int64.from_pair(0x80000000, 0x00000000)) == 63, "countrz was wrong for 2 ^ 63")
assert(int64.countlz(int64.from_pair(0x80000000, 0x00000000)) == 0, "countlz was wrong for 2 ^ 63")

assert(int64.countrz(int64.from_pair(0x00000000, 0x00000000)) == 64, "countrz was wrong for 2 ^ 64 (or 0)")
assert(int64.countlz(int64.from_pair(0x00000000, 0x00000000)) == 64, "countlz was wrong for 2 ^ 64 (or 0)")

local byteswap_1 = int64.byteswap(int64.from_pair(0xdeadbeef, 0xc0ffee42))
assert(int64.to_hex_string(byteswap_1) == "42eeffc0efbeadde", "byteswap_1 was not correct")

local byteswap_2 = int64.byteswap(int64.from_pair(0x11223344, 0x55667788))
assert(int64.to_hex_string(byteswap_2) == "8877665544332211", "byteswap_2 was not correct")

local byteswap_3 = int64.byteswap(int64.from_pair(0, 1))
assert(int64.to_hex_string(byteswap_3) == "0100000000000000", "byteswap_3 was not correct")

local byteswap_4 = int64.byteswap(int64.from_pair(0, 0))
assert(int64.to_hex_string(byteswap_4) == "0000000000000000", "byteswap_4 was not correct")

local byteswap_5 = int64.byteswap(int64.from_pair(0x00000075, 0x42000000))
assert(int64.to_hex_string(byteswap_5) == "0000004275000000", "byteswap_5 was not correct")

return {}


--!strict

--[[
	Lune's `datetime` module uses lowerCamelCase, whereas Roblox's `DateTime` module uses UpperCamelCase.
	To allow for Roblox-style usage of DateTime objects, this module wraps Lune's `datetime` module to add
	Roblox-style properties and methods through the use of metatables.

	This module may need to be updated as different properties and methods are needed.
--]]

local datetime = require("@lune/datetime")

local DateTime = setmetatable({
	now = function()
		local now = datetime.now()
		return setmetatable({
			UnixTimestampMillis = now.unixTimestampMillis,
			UnixTimestamp = now.unixTimestamp,
		}, { __index = now })
	end,
}, { __index = datetime })

return DateTime


--!strict

--[[
	Jest depends on `loadmodule` existing on the `debug` api.
	On Roblox, it's locked behind a feature flag FFlagDebugLoadModule, which is not enabled by default.
	In Lune, it doesn't exist at all, so this module creates the interface for it but is not implemented.
	The _loader function needs to be overridden to be usable.
	In this project, the `lune/test.luau` script implements and sets the _loader function.
--]]

local Debug

Debug = setmetatable({
	_loader = function(_module: any)
		error("The loader for Debug.loadmodule is not implemented")
	end,
	loadmodule = function(module)
		return Debug._loader(module)
	end,
}, { __index = debug })

return Debug


--!strict

--[[
	Paths use \ on Windows and / on Unix. Lune doesn't have a built-in path assembler,
	so this script is used to provide a consistent path format for all platforms.
--]]

local process = require("@lune/process")

local Path = {}

function Path.join(...)
	local path = table.concat({ ... }, "/")
	if process.os == "windows" then
		path = path:gsub("/", "\\")
	end
	return path
end

return Path


--!strict

--[[
	Lune instances are tables containing a reference to the instance, but Lune creates
	different tables even for the same instance. Jest's caching behavior depends
	on the instance always being the same to look it up by key, so this function serves to
	always return the same "wrapped" instance when given the same instance. It has to also
	override the FindFirstChild, WaitForChild, and Instance property getter to ensure they
	also return a "wrapped" instance.
--]]

local roblox = require("@lune/roblox")

local ReducedInstance = {}
ReducedInstance._cache = {}

function ReducedInstance.once(instance: roblox.Instance): roblox.Instance
	assert(instance, "Instance is nil")

	local debugId = instance:GetDebugId()
	if ReducedInstance._cache[debugId] then
		return ReducedInstance._cache[debugId]
	end

	local self = setmetatable({
		instance = instance,
	}, {
		-- selene: allow(shadowing)
		__index = function(self, key): any?
			local value = self.instance[key]
			if type(value) == "function" then
				if key == "FindFirstChild" then
					return function(self, ...): roblox.Instance?
						local child = self.instance:FindFirstChild(...)
						if child then
							return ReducedInstance.once(child :: roblox.Instance)
						end
						return
					end
				elseif key == "WaitForChild" then
					return function(self, ...)
						return ReducedInstance.once(self.instance:WaitForChild(...) :: roblox.Instance)
					end
				else
					return function(self, ...)
						return value(self.instance, ...)
					end
				end
			elseif typeof(value) == "Instance" then
				return ReducedInstance.once((value :: never) :: roblox.Instance)
			end

			return value
		end,
		-- selene: allow(shadowing)
		__newindex = function(self, key, value)
			self.instance[key] = value
		end,
		__metatable = false,
	})

	ReducedInstance._cache[debugId] = self

	return (self :: any) :: roblox.Instance
end

return ReducedInstance


--!strict

--[[
	Provides a connection to a loop that runs every frame. This is used
	in the custom Heartbeat implementation in lune/test.luau
--]]

local task = require("@lune/task")

type Listener = {
	callback: (number) -> (),
	disconnected: boolean,
}

local Runtime = {}
Runtime._running = false
Runtime._listeners = {} :: { Listener }

function Runtime._loop()
	if Runtime._running then
		return
	end
	Runtime._running = true
	while #Runtime._listeners > 0 do
		local listeners = Runtime._listeners
		local dt = task.wait()
		for _, listener in listeners do
			if not listener.disconnected then
				listener.callback(dt)
			end
		end
		for i = #listeners, 1, -1 do
			if listeners[i].disconnected then
				table.remove(listeners, i)
			end
		end
	end
	Runtime._running = false
end

function Runtime.Connect(_, callback: (number) -> ())
	local listener: Listener = {
		callback = callback,
		disconnected = false,
	}

	table.insert(Runtime._listeners, listener)
	task.spawn(Runtime._loop)

	return {
		Connected = true,
		Disconnect = function(self)
			self.Connected = false
			listener.disconnected = true
		end,
	}
end

return Runtime


--[[
	On Unix-like systems, shell scripts can be run directly.
	On Windows, shell scripts need to be run by calling a shell executable, such as Git Bash.
	This script assumes the Windows machine has a shell runner installed and available in the PATH.
		- For example, Git Bash can be installed and C:\Program Files\Git\bin added to the PATH
--]]

local process = require("@lune/process")

local function runShellScript(scriptPath: string, args: { string }): process.SpawnResult
	local proc

	if process.os == "windows" then
		proc = process.spawn("sh", { scriptPath, table.unpack(args) })
	else
		proc = process.spawn(scriptPath, args)
	end

	return proc
end

return runShellScript


--!strict

--[[
	Builds the release artifacts of the finite state machine. Used by CI to upload to various release targets.

	Since the parent folder is named `lune`, the `lune` cli will automatically look in this directory for scripts to run.

	Usage (from project directory):
		lune run build 0-0-0

	Where 0-0-0 is the version of the release.
	This creates a build folder in the root directory with a subdirectory for each build target. For example,
		- build
			- rbxm
				StateQ-0-0-0.rbxm
			- wally
				stateq-0-0-0
			- zip
				StateQ-0-0-0.zip

	You can also build without appending the version number if you want to test locally without putting a dummy version number in.
		lune run build
	This produces a structure like the following
		- build
			- rbxm
				StateQ.rbxm
			- wally
				stateq
			- zip
				StateQ.zip
--]]

local Path = require("Utils/Path")
local fs = require("@lune/fs")
local process = require("@lune/process")

local version = process.args[1]
assert(version == nil or version:match("^%d+%-%d+%-%d+$"), `Invalid version format. Expected "x-y-z", got "{version}"`)
local suffix = if version then `-{version}` else ""
local fileName = `StateQ{suffix}`
local buildFolder = "build"

local function ensurePath(...: string)
	local path = Path.join(...)
	if not fs.isDir(path) then
		print("Creating path", path)
		fs.writeDir(path)
	end
	return path
end

local build_targets = {
	{
		description = "rbxm",
		command = "rojo",
		args = {
			"build",
			"rbxm.project.json",
			"--output",
			Path.join(ensurePath(buildFolder, "rbxm"), `{fileName}.rbxm`),
		},
	},
	{
		description = "Wally package",
		command = "wally",
		args = {
			"package",
			"--output",
			Path.join(ensurePath(buildFolder, "wally"), `{fileName:lower()}.tar`),
		},
	},
	{
		description = "zip",
		command = "zip",
		args = {
			"-r",
			Path.join(ensurePath(buildFolder, "zip"), `{fileName}.zip`),
			"src/StateQ",
			"default.project.json",
			"Packages",
			"README.md",
			"LICENSE.md",
		},
	},
}

for _, buildTarget in build_targets do
	print(`\nBuilding {buildTarget.description} artifact...`)
	print(buildTarget.command, table.concat(buildTarget.args, " "))
	local proc = process.spawn(buildTarget.command, buildTarget.args)
	print(proc.stdout)
	assert(proc.ok, proc.stderr)
end

process.exit(0)


--!strict

--[[
	Lune script to run tests on a given project.json file. By default, it uses `test.project.json`, but can be passed a
	different file as an argument.

	Since the parent folder is named `lune`, the `lune` cli will automatically look in this directory for scripts to run.

	Usage (from project directory):
		lune run test [project.json]
--]]

local fs = require("@lune/fs")
local luau = require("@lune/luau")
local process = require("@lune/process")
local roblox = require("@lune/roblox")
local serde = require("@lune/serde")
local task = require("@lune/task")

-- DEPENDENTS: [Jest]
local DateTime = require("Context/DateTime")
local Debug = require("Context/Debug")
local Runtime = require("Utils/Runtime")

-- DEPENDENTS: [test.luau, Jest]
local ReducedInstance = require("Utils/ReducedInstance")

-- DEPENDENTS: [test.luau]
local Path = require("Utils/Path")

type RojoProject = {
	name: string,
	tree: any,
}

local DEFAULT_PROJECT_FILE_PATH = "test.project.json"

local function readRojoProject(filePath: string): RojoProject
	assert(
		fs.isFile(filePath),
		`Rojo project file {filePath} not found. Please create it or specify a different file as an argument.`
	)

	local success, result = pcall(serde.decode, "json" :: "json", fs.readFile(filePath))
	assert(success, `Failed to read Rojo project file {filePath}: {result}`)
	assert(result.name, "Rojo project name is required in the project file")

	return result :: RojoProject
end

local function buildProject(rojoProjectFilePath: string)
	local rojoProject = readRojoProject(rojoProjectFilePath)
	local builtProjectFilePath = `{rojoProject.name}.rbxl`
	local root = process.env.HOME or process.env.USERPROFILE
	local rojoPath = Path.join(root, ".rokit", "bin", "rojo")

	print(`Building project {rojoProjectFilePath} into {builtProjectFilePath} with Rojo...\n`)
	local proc = process.spawn(rojoPath, { "build", rojoProjectFilePath, "--output", builtProjectFilePath })
	assert(proc.ok, `Failed to build project [{builtProjectFilePath}]: {proc.stderr}`)

	print(`Deserializing {builtProjectFilePath}...`)
	local success, result = pcall(roblox.deserializePlace, fs.readFile(builtProjectFilePath))
	assert(success, `Failed to deserialize built project [{builtProjectFilePath}]: {result}`)

	return result
end

local function implementRobloxMethods()
	-- DEPENDENTS: [Jest]
	roblox.implementMethod("Instance", "WaitForChild", function(self, ...)
		local child = self:FindFirstChild(...)
		local childName = select(1, { ... })
		assert(
			child,
			`WaitForChild is not implemented in Lune, so FindFirstChild was substituted but {self:GetFullName()} does not contain child {childName} at the time of calling.`
		)
		return child
	end)

	-- DEPENDENTS: [Jest]
	roblox.implementMethod("Instance", "isA", function(self, className: string)
		return self:IsA(className)
	end)

	-- DEPENDENTS: [Jest]
	roblox.implementProperty("RunService", "Heartbeat", function()
		return {
			Wait = function(_self)
				local thread = coroutine.running()
				local conn
				conn = Runtime:Connect(function(dt)
					conn:Disconnect()
					coroutine.resume(thread, dt)
				end)
				return coroutine.yield()
			end,
			Connect = Runtime.Connect,
		}
	end)
end

local rojoProjectFilePath = process.args[1] or DEFAULT_PROJECT_FILE_PATH
local game = buildProject(rojoProjectFilePath)
implementRobloxMethods()

-- Declaring because it's needed by `loadScript`, but `loadScript` is also needed in `requireModule`. Defined later.
local requireModule

-- DEPENDENTS: [TestService/Source/run.server.luau]
-- ProcessService isn't implemented in Lune, so this creates a workable implementation of it by mapping it to process.exit
local gameWithContext = setmetatable({
	GetService = function(_self, serviceName: string)
		if serviceName == "ProcessService" then
			return {
				ExitAsync = function(_self, code: number)
					process.exit(code)
				end,
			} :: any
		end

		return game:GetService(serviceName)
	end,
}, { __index = game })

-- DEPENDENTS: [test.luau, Jest]
local function loadScript(script: roblox.Instance): (((...any) -> ...any)?, string?)
	script = ReducedInstance.once(script)
	if not script:IsA("LuaSourceContainer") then
		return nil, "Attempt to load a non LuaSourceContainer"
	end

	local bytecodeSuccess, bytecode = pcall(luau.compile, (script :: any).Source)
	if not bytecodeSuccess then
		return nil, bytecode
	end

	local callableFn = luau.load(bytecode, {
		debugName = script:GetFullName(),
		environment = setmetatable({
			game = gameWithContext,
			script = script,
			require = requireModule,
			tick = os.clock,
			task = task,
			DateTime = DateTime,
			debug = Debug,
		}, { __index = roblox }) :: any,
	})

	return callableFn
end

-- Override the unimplemented _loader function with the above implementation
Debug._loader = loadScript

-- Luau
local MODULE_REGISTRY = {}

-- DEPENDENTS: [Jest]
function requireModule(moduleScript: roblox.Instance)
	assert(moduleScript and moduleScript:IsA("ModuleScript"), `Attempt to require a non ModuleScript {moduleScript}`)

	local cached = MODULE_REGISTRY[moduleScript]
	if cached then
		return cached
	end

	local func, err = loadScript(moduleScript)
	assert(func, err)

	local result = func()
	MODULE_REGISTRY[moduleScript] = result
	return result
end

-- Main
-- TODO: Flexibly run any script from a command line argument

print(`Starting main...\n`)
local TestService = game:GetService("TestService")
local Source = TestService:FindFirstChild("Source")
assert(Source, "game.TestService.Source not found")

local run = Source:FindFirstChild("run")
assert(run, "game.TestService.Source.run not found")

local func, err = loadScript(ReducedInstance.once(run))
assert(func, err)

print(`Running tests from game.TestService.Source.run...\n`)
func()


--!strict

--[[
	A lightweight, strictly typed logger class designed with a minimal set of requirements for the StateQ project, so it is not feature rich.
	This logger class includes a few features such:
	- A concept of log levels (Debug, Info, Warning, Error): These levels allow for categorizing the importance of the logs.
	- Methods for logging at these levels: Convenient methods for logging at each level.
	- The ability to add handlers: Handlers are functions that process logs. They can be added to the logger to customize its behavior.
	- Handler results (Sink, Continue): Handlers return a result which determines if the logger should proceed with processing the log in other handlers.
	- An assert method: Checks a condition, where failing the assertion invokes the appropriate handlers.
	- A wrap method: Wraps a callback in pcall, invoking the appropriate handlers if an error occurs.

	Handlers are processed from highest minimum level to lowest minimum level. Within a level, they are processed in the
	opposite order that they are added (LIFO). If a handler returns 'Sink', no other handlers will be invoked for that record.

	Adding a default handler pipes non-erroneous records to output via print or warn depending on level,
	and processes errors by throwing them, which will halt the execution of the program unless caught by an error handling mechanism.

	Usage:
		local Logger = require(path.to.Logger)
		local logger = Logger.new("ModuleName", Logger.LogLevel.Info)
		logger:addDefaultHandler()
		logger:addHandler(Logger.LogLevel.Error, function(level, name, ...)
			-- Custom error handling that runs before the default handler, and prevents the default handler from handling this
			print("Custom error handler:", name, level, ...)
			return Logger.HandlerResult.Sink
		end)
		logger:debug("This is a debug message") -- Doesn't get handled because logger level is set to Info
		logger:info("This is an info message")
		logger:warning("This is a warning message")
		logger:error("This is an error message")
		logger:assert(false, "This is an assertion error")
		logger:wrap(function()
			error("This is an error thrown from a wrapped function")
		end)

	A primary purpose of this class existing is to be able to check for errors from within tests. The StateQ queues events,
	starting a new thread to process them, which means errors that occur are not propagated to the main thread and can't be tested for.
	This logger class allows for the testing of errors by adding a handler for errors, and then checking for those errors in tests.
--]]

export type LogLevel = "Error" | "Warning" | "Info" | "Debug"
local LogLevel: { [string]: LogLevel } = {
	Debug = "Debug" :: "Debug",
	Info = "Info" :: "Info",
	Warning = "Warning" :: "Warning",
	Error = "Error" :: "Error",
}
table.freeze(LogLevel)

export type HandlerResult = "Sink" | "Continue"
local HandlerResult: { [string]: HandlerResult } = {
	Sink = "Sink" :: "Sink",
	Continue = "Continue" :: "Continue",
}
table.freeze(HandlerResult)

local orderedLogLevels: { LogLevel } = {
	LogLevel.Error,
	LogLevel.Warning,
	LogLevel.Info,
	LogLevel.Debug,
}

export type Handler = (level: LogLevel, name: string, ...any?) -> HandlerResult?

-- Returning a negative number means levelA is less than levelB, 0 means they are equal,
-- and a positive number means levelA is greater than levelB
local function compareLevels(levelA: LogLevel, levelB: LogLevel): number
	local indexA = table.find(orderedLogLevels, levelA)
	assert(indexA, `Invalid log level: {levelA}`)

	local indexB = table.find(orderedLogLevels, levelB)
	assert(indexB, `Invalid log level: {levelB}`)
	return indexB - indexA
end

local Logger = {}
Logger.LogLevel = LogLevel
Logger.HandlerResult = HandlerResult
Logger.__index = Logger

export type ClassType = typeof(setmetatable(
	{} :: {
		name: string,
		_handlersByLevel: { [LogLevel]: { Handler } },
		_minimumLevel: LogLevel,
	},
	Logger
))

function Logger.new(name: string, minimumLevel: LogLevel?): ClassType
	local self = {
		name = name,
		_handlersByLevel = {},
		_minimumLevel = minimumLevel or LogLevel.Info,
	}
	setmetatable(self, Logger)

	return self
end

function Logger.addDefaultHandler(self: ClassType)
	self:addHandler(LogLevel.Debug, function(level: LogLevel, name: string, ...: any?)
		if level == LogLevel.Debug or level == LogLevel.Info then
			print(`{name}:{level}:`, ...)
		elseif level == LogLevel.Warning then
			warn(`{name}:{level}:`, ...)
		elseif level == LogLevel.Error then
			error(`{name}:{level}: {...}`, 4)
		else
			error(`Invalid log level: {level}`)
		end

		return HandlerResult.Continue
	end)
end

function Logger.addHandler(self: ClassType, minimumLevel: LogLevel, handler: Handler)
	if not self._handlersByLevel[minimumLevel] then
		self._handlersByLevel[minimumLevel] = {}
	end

	-- Insert at the beginning of the list so newer handlers are processed first (LIFO)
	table.insert(self._handlersByLevel[minimumLevel], 1, handler)
end

function Logger._log(self: ClassType, level: LogLevel, ...: any?)
	if compareLevels(level, self._minimumLevel) < 0 then
		-- Log level is lower than the minimum logger level, so we ignore it
		return
	end

	for _, minimumHandlerLevel: LogLevel in orderedLogLevels do
		if compareLevels(level, minimumHandlerLevel) < 0 then
			-- Log level is lower than the minimum handler level, so we ignore it
			continue
		end

		local handlers = self._handlersByLevel[minimumHandlerLevel]

		if not handlers then
			-- No handlers for this level
			continue
		end

		for _, handler in handlers do
			local result = handler(level, self.name, ...)
			if result == HandlerResult.Sink then
				return
			end
		end
	end
end

function Logger.log(self: ClassType, level: LogLevel, ...: any?)
	self:_log(level, ...)
end

function Logger.debug(self: ClassType, ...: any?)
	self:_log(LogLevel.Debug, ...)
end

function Logger.info(self: ClassType, ...: any?)
	self:_log(LogLevel.Info, ...)
end

function Logger.warning(self: ClassType, ...: any?)
	self:_log(LogLevel.Warning, ...)
end

function Logger.error(self: ClassType, ...: any?)
	self:_log(LogLevel.Error, ...)
end

function Logger.assert(self: ClassType, condition: any, message: string?, level: LogLevel?)
	if not condition then
		self:_log(level or LogLevel.Error, message or "Assertion failed")
	end
end

function Logger.wrap(self: ClassType, callback: (...any) -> ...any, ...: any?)
	-- FIXME: Return all results, not just the first one
	local success, result = pcall(callback, ...)
	-- Not using self:assert to ensure the stack trace level is consistent for error tracebacks
	if not success then
		self:_log(LogLevel.Error, result)
	end
	return success, result
end

return Logger


--!strict

--------------------------------------------------------------------------------
--               Batched Yield-Safe Signal Implementation                     --
-- This is a Signal class which has effectively identical behavior to a       --
-- normal RBXScriptSignal, with the only difference being a couple extra      --
-- stack frames at the bottom of the stack trace when an error is thrown.     --
-- This implementation caches runner coroutines, so the ability to yield in   --
-- the signal handlers comes at minimal extra cost over a naive signal        --
-- implementation that either always or never spawns a thread.                --
--                                                                            --
-- API:                                                                       --
--   local Signal = require(THIS MODULE)                                      --
--   local sig = Signal.new()                                                 --
--   local connection = sig:Connect(function(arg1, arg2, ...) ... end)        --
--   sig:Fire(arg1, arg2, ...)                                                --
--   connection:Disconnect()                                                  --
--   sig:DisconnectAll()                                                      --
--   local arg1, arg2, ... = sig:Wait()                                       --
--                                                                            --
-- Licence:                                                                   --
--   Licenced under the MIT licence.                                          --
--                                                                            --
-- Authors:                                                                   --
--   stravant - July 31st, 2021 - Created the file.                           --
--------------------------------------------------------------------------------
-- This file has been modified to add Luau typing support

type Callback = (...any?) -> ...any?

-- The currently idle thread to run the next handler on
local freeRunnerThread: thread? = nil

-- Function which acquires the currently idle handler runner thread, runs the
-- function fn on it, and then releases the thread, returning it to being the
-- currently idle one.
-- If there was a currently idle runner thread already, that's okay, that old
-- one will just get thrown and eventually GCed.
local function acquireRunnerThreadAndCallEventHandler(fn, ...)
	local acquiredRunnerThread = freeRunnerThread
	freeRunnerThread = nil
	fn(...)
	-- The handler finished running, this runner thread is free again.
	freeRunnerThread = acquiredRunnerThread
end

-- Coroutine runner that we create coroutines of. The coroutine can be
-- repeatedly resumed with functions to run followed by the argument to run
-- them with.
local function runEventHandlerInFreeThread(...)
	acquireRunnerThreadAndCallEventHandler(...)
	while true do
		acquireRunnerThreadAndCallEventHandler(coroutine.yield())
	end
end

-- Connection class
local Connection = {}
Connection.__index = Connection

export type SignalConnection = typeof(setmetatable(
	{} :: {
		_connected: boolean,
		_signal: ClassType,
		_fn: Callback,
		_next: SignalConnection?,
	},
	Connection
))

function Connection.new(signal: ClassType, fn: Callback): SignalConnection
	local self = {
		_connected = true,
		_signal = signal,
		_fn = fn,
		_next = nil,
	}

	setmetatable(self, Connection)

	return self
end

function Connection.Disconnect(self: SignalConnection)
	assert(self._connected, "Can't disconnect a connection twice.")
	self._connected = false

	-- Unhook the node, but DON'T clear it. That way any fire calls that are
	-- currently sitting on this node will be able to iterate forwards off of
	-- it, but any subsequent fire calls will not hit it, and it will be GCed
	-- when no more fire calls are sitting on it.
	if self._signal._handlerListHead and (self._signal._handlerListHead :: SignalConnection) == self then
		self._signal._handlerListHead = self._next
	else
		local prev = self._signal._handlerListHead
		while prev and prev._next ~= self do
			prev = prev._next
		end
		if prev then
			prev._next = self._next
		end
	end
end

-- Signal class
local Signal = {}
Signal.__index = Signal

export type ClassType = typeof(setmetatable(
	{} :: {
		_handlerListHead: SignalConnection?,
	},
	Signal
))

function Signal.new(): ClassType
	return setmetatable({
		_handlerListHead = nil,
	}, Signal)
end

function Signal.Connect(self: ClassType, fn: Callback): SignalConnection
	local connection = Connection.new(self, fn)
	if self._handlerListHead then
		connection._next = self._handlerListHead :: SignalConnection

		self._handlerListHead = connection
	else
		self._handlerListHead = connection
	end
	return connection
end

-- Disconnect all handlers. Since we use a linked list it suffices to clear the
-- reference to the head handler.
function Signal.DisconnectAll(self: ClassType)
	self._handlerListHead = nil
end

-- Signal:Fire(...) implemented by running the handler functions on the
-- coRunnerThread, and any time the resulting thread yielded without returning
-- to us, that means that it yielded to the Roblox scheduler and has been taken
-- over by Roblox scheduling, meaning we have to make a new coroutine runner.
function Signal.Fire(self: ClassType, ...: any?)
	local item = self._handlerListHead
	while item do
		if item._connected then
			if not freeRunnerThread then
				freeRunnerThread = coroutine.create(runEventHandlerInFreeThread)
			end
			task.spawn(freeRunnerThread :: thread, item._fn, ...)
		end
		item = item._next
	end
end

-- Implement Signal:Wait() in terms of a temporary connection using
-- a Signal:Connect() which disconnects itself.
function Signal.Wait(self: ClassType)
	local waitingCoroutine = coroutine.running()
	local cn: SignalConnection
	cn = self:Connect(function(...: any?)
		cn:Disconnect()
		task.spawn(waitingCoroutine, ...)
	end)
	return coroutine.yield()
end

return Signal


--!strict

--[[
	ThreadQueue is a task scheduler and rate limiter that allows callbacks to be queued to be executed, yielding the requesting thread
	until the callback has returned.

	Provides similar functionality to the NodeJS module Bottleneck (https://www.npmjs.com/package/bottleneck) however, it exposes
	its scheduling functionality through coroutines rather than promises.
--]]

local ThreadQueue = {}
ThreadQueue.__index = ThreadQueue

export type ClassType = typeof(setmetatable(
	{} :: {
		_queue: { QueueEntry },
		_queueRunning: boolean,
		_timeBetween: number,
		_maxQueueLength: number,
		_enableConcurrency: boolean,
	},
	ThreadQueue
))

type Callback = (...any) -> ...any

type QueueEntry = {
	thread: thread,
	callback: Callback,
}

function ThreadQueue.new(timeBetween: number?, maxQueueLength: number?, enableConcurrency: boolean?): ClassType
	local self = {
		_queue = {},
		_queueRunning = false,
		_timeBetween = (timeBetween or 0),
		_maxQueueLength = (maxQueueLength or math.huge),
		_enableConcurrency = enableConcurrency and true or false,
	}
	setmetatable(self, ThreadQueue)

	return self
end

function ThreadQueue.submitAsync(self: ClassType, callback: Callback)
	if #self._queue > self._maxQueueLength then
		return false, string.format("Queue is at capacity (%i)", self._maxQueueLength)
	end

	local queueEntry: QueueEntry = {
		thread = coroutine.running(),
		callback = callback,
	}

	table.insert(self._queue, queueEntry)

	self:_startQueue()

	return coroutine.yield()
end

function ThreadQueue.getLength(self: ClassType): number
	return #self._queue
end

function ThreadQueue.skipToLastEnqueued(self: ClassType)
	-- This method will not skip currently executing requests, but will remove
	-- all pending requests from the queue aside from the most recently enqueued
	if #self._queue > 1 then
		local lastEnqueuedEntry = self._queue[#self._queue]
		self._queue = { lastEnqueuedEntry }
	end
end

function ThreadQueue._startQueue(self: ClassType)
	-- Wait for the next resumption cycle, so the requesting thread has time to yield first
	task.defer(function()
		if self._queueRunning then
			return
		end
		self._queueRunning = true

		while #self._queue > 0 do
			self:_popQueueAsync()

			if self._timeBetween > 0 then
				task.wait(self._timeBetween)
			end
		end

		self._queueRunning = false
	end)
end

function ThreadQueue._popQueueAsync(self: ClassType)
	local entry = table.remove(self._queue, 1) :: QueueEntry

	local function execute()
		-- This callback can yield. Note the callback is called synchronously on this thread.
		-- The task.spawn is used to resume the calling thread with the pcall'd results of the callback.
		task.spawn(entry.thread, pcall(entry.callback))
	end

	-- If concurrency is enabled, we do not want to yield the queue while we wait for the callback to return
	if self._enableConcurrency then
		-- Use spawn to call execute so entry.callback doesn't block us from proceeding
		task.spawn(execute)
	else
		execute()
	end
end

return ThreadQueue


local TestService = game:GetService("TestService")

local Jest = require(TestService.Source.DevPackages.Jest)
local runCLI = Jest.runCLI

-- Jest.TestBootstrap:run({ TestService.Source.Tests })

print("Checking for ProcessService...")
local processServiceExists, ProcessService = pcall(function()
	-- selene: allow(incorrect_standard_library_use)
	return game:GetService("ProcessService")
end)
print(`ProcessService {processServiceExists and "exists" or "does not exist"}`)

print("Running jest tests cli...")
local status, result = runCLI(TestService.Source.Tests, {
	verbose = false,
	ci = false,
}, { TestService.Source.Tests }):awaitStatus()
print(`Jest tests completed with status {status}`)

if status == "Rejected" then
	print(result)
end

if status == "Resolved" and result.results.numFailedTestSuites == 0 and result.results.numFailedTests == 0 then
	if processServiceExists then
		ProcessService:ExitAsync(0)
	end
end

if processServiceExists then
	ProcessService:ExitAsync(1)
end

return nil


local l_CoreGui_0 = game:GetService("CoreGui");
local l_CorePackages_0 = game:GetService("CorePackages");
local l_RobloxGui_0 = l_CoreGui_0:WaitForChild("RobloxGui");
require(l_CorePackages_0.UIBlox).init((require(l_RobloxGui_0:WaitForChild("Modules").UIBloxInGameConfig)));
local v3 = game:DefineFastFlag("FeedbackModuleEarlyFontInitialization", false);
local v4 = game:DefineFastFlag("CaptureModeNativeExitSupport", false);
local l_GetFFlagSelectInSceneReportMenu_0 = require(l_CorePackages_0.Workspace.Packages.SharedFlags).GetFFlagSelectInSceneReportMenu;
if v3 then
    local l_TextService_0 = game:GetService("TextService");
    local l_GetTextBoundsParams_0 = Instance.new("GetTextBoundsParams");
    l_GetTextBoundsParams_0.Text = "random text";
    l_GetTextBoundsParams_0.Font = Font.fromEnum(Enum.Font.Gotham);
    l_GetTextBoundsParams_0.Size = 19;
    l_GetTextBoundsParams_0.Width = 0;
    local _ = l_TextService_0:GetTextBoundsAsync(l_GetTextBoundsParams_0);
end;
if v4 then
    game:GetService("GuiService").NativeClose:Connect(function()
        game:GetService("ExperienceStateCaptureService"):ToggleCaptureMode();
    end);
end;
if not l_GetFFlagSelectInSceneReportMenu_0() then
    require(l_RobloxGui_0.Modules.Feedback).initialize();
    return ;
else
    game:WaitForChild("SafetyService");
    if not game:GetService("SafetyService").IsCaptureModeForReport then
        require(l_RobloxGui_0.Modules.Feedback).initialize();
        return ;
    else
        require(l_CorePackages_0.Workspace.Packages.InGameAssetReporting).initialize();
        return ;
    end;
end;


local l_AdService_0 = game:GetService("AdService");
local v1 = require(game:GetService("CoreGui"):WaitForChild("RobloxGui").Modules.Ads.AdTeleportPromptHandler);
local v2 = nil;
onPortalPrompt = function(v3, v4, v5)
    if not v2 or not v2.hasActiveWindow then
        v2 = v1.new(v4, v3);
        if v2 == nil then
            error("Error in instantiating handler for adteleportprompt.");
            return ;
        else
            assert(v2 ~= nil, "Appeasing the type checking");
            local l_status_0, l_result_0 = pcall(function()
                return v2:fetchTitle();
            end);
            if not l_status_0 or l_result_0 == nil then
                v2:rejectPrompt(false, false);
                error("Error: Marketplace service failed to get placename for ad portal prompt.");
                return ;
            else
                v2:instantiatePrompt(l_result_0, v5);
                v2:updateCreateTime();
                return ;
            end;
        end;
    else
        return ;
    end;
end;
l_AdService_0.PortalPrompt:Connect(onPortalPrompt);


local l_CorePackages_0 = game:GetService("CorePackages");
local l_CoreGui_0 = game:GetService("CoreGui");
local _ = l_CoreGui_0:WaitForChild("RobloxGui");
local v3 = require(l_CorePackages_0.Packages.React);
local v4 = require(l_CorePackages_0.Packages.ReactRoblox);
local v5 = require(l_CoreGui_0.RobloxGui.Modules.ApolloClient);
local l_InExperienceAppChat_0 = require(l_CorePackages_0.Workspace.Packages.AppChat).App.InExperienceAppChat;
local l_ScreenGui_0 = Instance.new("ScreenGui");
l_ScreenGui_0.Name = "AppChat";
l_ScreenGui_0.Parent = l_CoreGui_0;
l_ScreenGui_0.ResetOnSpawn = false;
l_ScreenGui_0.IgnoreGuiInset = true;
l_ScreenGui_0.ZIndexBehavior = Enum.ZIndexBehavior.Sibling;
l_ScreenGui_0.DisplayOrder = 7;
v4.createRoot(l_ScreenGui_0):render(v3.createElement(l_InExperienceAppChat_0, {
    apolloClient = v5
}));


local l_RobloxGui_0 = game:GetService("CoreGui"):WaitForChild("RobloxGui");
local l_game_Service_0 = game:FindService("FaceAnimatorService");
local l_RunService_0 = game:GetService("RunService");
local l_Players_0 = game:GetService("Players");
local l_game_EngineFeature_0 = game:GetEngineFeature("AvatarChatProtocolDebugV1");
local l_FacialAnimationStreamingServiceV2_0 = game:GetService("FacialAnimationStreamingServiceV2");
local v6 = require(l_RobloxGui_0.Modules.Logger):new(script.Name);
local v7 = true;
local v8 = true;
v6:trace("AvatarChatDebugVisualization 3_3_2023__2");
local v9 = 0;
local v10 = "";
local v11 = Color3.new(0.00491341, 1, 0.0105745);
local v12 = Color3.new(0.96199, 0.736614, 0.0616617);
local v13 = Color3.new(0.96199, 0.0360113, 0);
local v14 = false;
local v15 = nil;
local v16 = {};
local v17 = {};
local v18 = {
    CharacterAdded = "CharacterAdded",
    CharacterRemoving = "CharacterRemoving"
};
clearConnection = function(v19, v20)
    if not (not v17[v19.UserId] or not v17[v19.UserId][v20]) then
        v17[v19.UserId][v20]:Disconnect();
        v17[v19.UserId][v20] = nil;
    end;
end;
clearAllConnections = function(v21)
    for _, v23 in ipairs(v18) do
        clearConnection(v21, v23);
    end;
    v17[v21.UserId] = {};
end;
getFaceControls = function(v24)
    return v24:FindFirstChildWhichIsA("FaceControls", true);
end;
findObjectOfNameAndTypeName = function(v25, v26, v27)
    for _, v29 in (v27:GetDescendants()), nil, nil do
        if not (not (v29.Name == v25) or not v29:IsA(v26)) then
            return v29;
        end;
    end;
end;
getHumanoidRootPart = function(v30)
    if v30 then
        return (v30:WaitForChild("HumanoidRootPart", 2));
    else
        return nil;
    end;
end;
getHead = function(v31, v32)
    if v31 then
        local v33 = nil;
        if v32 then
            v33 = v31:WaitForChild("Head", 5);
        end;
        local v34 = getFaceControls(v31);
        if v34 ~= nil then
            v33 = v34.Parent;
        end;
        if not v33 then
            v33 = findObjectOfNameAndTypeName("Head", "MeshPart", v31) or v31:FindFirstChild("Head", true) and (v33:IsA("Pose") and nil);
        end;
        v6:trace("Self View: fn getHead(), head: " .. tostring(v33));
        return v33;
    else
        return nil;
    end;
end;
if l_game_Service_0 then
    local l_l_game_Service_0_TrackerLodController_0 = l_game_Service_0:GetTrackerLodController();
    if not l_l_game_Service_0_TrackerLodController_0 then
        v6:trace("[FaceAnimatorUI] TrackerLodController is nil. UI disabled.");
    end;
    local v36 = Instance.new("Frame", l_RobloxGui_0);
    v36.Name = "AvatarChatDebugVisualizationFrame";
    v36.Position = UDim2.new(0, 0, 0, 0);
    v36.Size = UDim2.new(1, 0, 1, 0);
    v36.BackgroundTransparency = 1;
    local function v45()
        if l_game_Service_0:IsStarted() and l_l_game_Service_0_TrackerLodController_0 then
            local v37 = l_l_game_Service_0_TrackerLodController_0:getExtrapolation();
            local v38 = l_l_game_Service_0_TrackerLodController_0:isAudioEnabled();
            local v39 = l_l_game_Service_0_TrackerLodController_0:isVideoEnabled();
            local v40 = l_l_game_Service_0_TrackerLodController_0:getVideoLod();
            if v15 then
                local l_ForceDisabled_0 = Enum.TrackerExtrapolationFlagMode.ForceDisabled;
                if v37 == 1 then
                    l_ForceDisabled_0 = Enum.TrackerExtrapolationFlagMode.ExtrapolateFacsAndPose;
                end;
                if v37 == 2 then
                    l_ForceDisabled_0 = Enum.TrackerExtrapolationFlagMode.ExtrapolateFacsOnly;
                end;
                local v42 = 0;
                if v39 then
                    v42 = v42 + 1;
                end;
                if v38 then
                    v42 = v42 + 1;
                end;
                v10 = "Tracker: \nLoD: ";
                if (v39 and l_ForceDisabled_0 == Enum.TrackerExtrapolationFlagMode.ForceDisabled) and v40 == 0 then
                    v42 = v42 + 2;
                end;
                if (v39 and l_ForceDisabled_0 == Enum.TrackerExtrapolationFlagMode.ExtrapolateFacsOnly) and v40 == 0 then
                    v42 = v42 + 1;
                end;
                if (v39 and l_ForceDisabled_0 == Enum.TrackerExtrapolationFlagMode.ForceDisabled) and v40 == 1 then
                    v42 = v42 + 3;
                end;
                if (v39 and l_ForceDisabled_0 == Enum.TrackerExtrapolationFlagMode.ExtrapolateFacsOnly) and v40 == 1 then
                    v42 = v42 + 2;
                end;
                if (v39 and l_ForceDisabled_0 == Enum.TrackerExtrapolationFlagMode.ExtrapolateFacsAndPose) and v40 == 1 then
                    v42 = v42 + 1;
                end;
                v10 = v10 .. tostring(v42);
                local _ = "" .. tostring(l_l_game_Service_0_TrackerLodController_0.VideoExtrapolationMode);
                v6:trace("lodController.VideoExtrapolationMode:" .. tostring(l_l_game_Service_0_TrackerLodController_0.VideoExtrapolationMode) .. ", extrapolation:" .. tostring(Enum.TrackerExtrapolationFlagMode:GetEnumItems()[v37 + 1]) .. ", (Enum::any).TrackerExtrapolationFlagMode.ForceDisabled: " .. tostring(Enum.TrackerExtrapolationFlagMode.ForceDisabled));
                local v44 = l_game_Service_0.IsStarted and l_game_Service_0.VideoAnimationEnabled;
                v10 = v10 .. "\ntracker mode:";
                if not (not v38 or not v39) then
                    v10 = v10 .. "av2c";
                end;
                if not (not v38 or v39) then
                    v10 = v10 .. "a2c";
                end;
                if not (v38 or not v39) then
                    v10 = v10 .. "v2c";
                end;
                if not (v38 or v39) then
                    v10 = v10 .. "off";
                end;
                v10 = v10 .. "\nvideoLoD: " .. tostring(v40) .. "\nUser camera: ";
                if not v44 then
                    v10 = v10 .. "off";
                else
                    v10 = v10 .. "on";
                end;
                v15.Text = v10;
            end;
            return ;
        else
            v6:trace("[FaceAnimatorUI] FaceAnimatorService not started or LodController not enabled. Tracker LoD UI not active.");
            return ;
        end;
    end;
    if l_l_game_Service_0_TrackerLodController_0 then
        l_l_game_Service_0_TrackerLodController_0.UpdateState:connect(v45);
        v14 = true;
    end;
    destroyDebugUIOfPlayer = function(v46)
        local v47 = 0;
        for _, v49 in pairs(v16) do
            v47 = v47 + 1;
            if v49.player == v46 then
                if v49.debugLabel then
                    v49.debugLabel:Destroy();
                end;
                if v49.billboardGUI then
                    v49.billboardGUI:Destroy();
                end;
                if v49.markerCircle then
                    v49.markerCircle:Destroy();
                end;
                table.remove(v16, v47);
                v49.player = nil;
                v49 = nil;
            end;
        end;
    end;
    getBillboardAdornee = function(v50)
        if v50 then
            local _ = nil;
            local v52 = v50.Character or v50.CharacterAdded:Wait();
            v6:trace("character:" .. tostring(v52));
            local v53 = getHumanoidRootPart(v52);
            local _ = v52:WaitForChild("UpperTorso", 2);
            if not v53 then
                return (getHead(v52, true));
            else
                return v53;
            end;
        else
            return nil;
        end;
    end;
    prepDeltaTimesList = function(v55)
        for _ = 1, 61 do
            table.insert(v55, 1);
        end;
    end;
    createDebugUIOfPlayer = function(v57)
        local v58 = {};
        local v59 = v57.UserId == l_Players_0.LocalPlayer.UserId;
        v6:trace("createDebugUIOfPlayer(), isLocalPlayer:" .. tostring(v59) .. ",player.UserId: " .. tostring(v57.UserId) .. ",Players.LocalPlayer.UserId:" .. tostring(l_Players_0.LocalPlayer.UserId));
        local v60 = Instance.new("BillboardGui", v36);
        local v61 = getBillboardAdornee(v57);
        v60.Adornee = v61;
        v60.StudsOffsetWorldSpace = Vector3.new(0, 6.5, 0);
        v60.Name = "AboveAvatarBillboardGui_" .. tostring(v57.UserId);
        v60.Size = UDim2.new(0, 200, 0, 150);
        local v62 = nil;
        if not v59 then
            v62 = Instance.new("CylinderHandleAdornment", workspace);
            v62.Name = "markerCircle_" .. tostring(v57.UserId);
            v62.Color3 = v13;
            v62.Transparency = 0.25;
            v62.Adornee = v61;
            v62.Angle = 360;
            v62.CFrame = CFrame.new(0, -3, 0) * CFrame.Angles(1.5707963267948966, 0, 0);
            v62.Height = 0.1;
            v62.InnerRadius = 2.9;
            v62.Radius = 3;
        end;
        local l_TextLabel_0 = Instance.new("TextLabel");
        l_TextLabel_0.BackgroundColor3 = Color3.new(0, 0, 0);
        l_TextLabel_0.BackgroundTransparency = 0.4;
        l_TextLabel_0.Text = "";
        l_TextLabel_0.TextColor3 = Color3.new(1, 1, 1);
        l_TextLabel_0.TextStrokeColor3 = Color3.new(0.35935, 0.35761, 0.361074);
        l_TextLabel_0.TextStrokeTransparency = 0.2;
        l_TextLabel_0.Font = Enum.Font.Code;
        l_TextLabel_0.TextSize = 14;
        l_TextLabel_0.Position = UDim2.new(0, 20, 0, 20);
        l_TextLabel_0.Size = UDim2.new(0, 120, 0, 80);
        if not v59 then
            l_TextLabel_0.Parent = v60;
        else
            l_TextLabel_0.Parent = v36;
        end;
        if v59 then
            v15 = l_TextLabel_0;
            if v14 then
                v45();
            end;
        end;
        v58.isLocalPlayer = v59;
        v58.player = v57;
        v58.debugLabel = l_TextLabel_0;
        v58.billboardGUI = v60;
        v58.markerCircle = v62;
        v58.previousFacsRecv = 0;
        v58.timeLastReceived = 0;
        v58.facsFPS = 0;
        v58.deltaTimesList = {};
        prepDeltaTimesList(v58.deltaTimesList);
        v58.lastFrameIndex = 0;
        table.insert(v16, v58);
    end;
    calculateAverageFPS = function(v64)
        local v65 = 0;
        for _, v67 in ipairs(v64.deltaTimesList) do
            v65 = v65 + v67;
        end;
        return #v64.deltaTimesList / v65;
    end;
    updateDebugUIsOfPlayers = function()
        for _, v69 in pairs(v16) do
            if v69 ~= nil then
                local l_player_0 = v69.player;
                if not v69.isLocalPlayer then
                    if l_game_EngineFeature_0 then
                        local l_WithPlayerId_0 = l_FacialAnimationStreamingServiceV2_0:GetStats():GetWithPlayerId("facs/lost", l_player_0.UserId);
                        local l_WithPlayerId_1 = l_FacialAnimationStreamingServiceV2_0:GetStats():GetWithPlayerId("facs/recv", l_player_0.UserId);
                        local v73 = "" .. "recv: " .. l_WithPlayerId_1 .. "\nlost: " .. l_WithPlayerId_0;
                        if l_WithPlayerId_0 > 0 then
                            v73 = v73 .. "\nloss: " .. tostring((math.floor(100 * (l_WithPlayerId_0 / l_WithPlayerId_1)))) .. "%";
                        end;
                        local v74 = v9 - v69.timeLastReceived;
                        local l_v73_0 = v73;
                        if l_WithPlayerId_1 ~= v69.previousFacsRecv then
                            local v76 = math.floor(1 / v74);
                            v69.deltaTimesList[v69.lastFrameIndex + 1] = v74;
                            v69.lastFrameIndex = (v69.lastFrameIndex + 2) % #v69.deltaTimesList;
                            v69.facsFPS = tostring(v76) .. "\naverage: " .. tostring((math.floor((calculateAverageFPS(v69)))));
                            if v74 < 0.1 then
                                v69.markerCircle.Color3 = v11;
                            else
                                v69.markerCircle.Color3 = v12;
                            end;
                            v69.timeLastReceived = v9;
                        end;
                        l_v73_0 = l_v73_0 .. "\nFACS FPS: " .. v69.facsFPS;
                        if (l_WithPlayerId_1 == v69.previousFacsRecv and v74 > 0.17) and v74 <= 5 then
                            v69.markerCircle.Color3 = v12;
                        end;
                        if l_WithPlayerId_1 == v69.previousFacsRecv and v74 > 5 then
                            v69.markerCircle.Color3 = v13;
                            v69.facsFPS = 0;
                        end;
                        v69.previousFacsRecv = l_WithPlayerId_1;
                        v69.debugLabel.Text = l_v73_0;
                        v69.debugLabel.Visible = v8;
                    end;
                else
                    v15.Text = v10;
                    if not v7 then
                        v15.Visible = false;
                    else
                        v15.Visible = true;
                    end;
                end;
            end;
        end;
    end;
    renderStepped = function()
        v9 = time();
        updateDebugUIsOfPlayers();
    end;
    local _ = function(v77, _)
        createDebugUIOfPlayer(v77);
    end;
    local _ = function(v80, _)
        v6:trace("onCharacterRemoving: player: " .. tostring(v80));
        destroyDebugUIOfPlayer(v80);
        clearAllConnections(v80);
    end;
    onPlayerAdded = function(v83)
        if v83.Character then
            local _ = v83.Character;
            createDebugUIOfPlayer(v83);
        end;
        v17[v83.UserId] = {};
        v17[v83.UserId][v18.CharacterAdded] = v83.CharacterAdded:Connect(function(_)
            createDebugUIOfPlayer(v83);
        end);
        v17[v83.UserId][v18.CharacterRemoving] = v83.CharacterRemoving:Connect(function(_)
            local l_v83_0 = v83;
            v6:trace("onCharacterRemoving: player: " .. tostring(l_v83_0));
            destroyDebugUIOfPlayer(l_v83_0);
            clearAllConnections(l_v83_0);
        end);
    end;
    onPlayerRemoved = function(v88)
        destroyDebugUIOfPlayer(v88);
        clearAllConnections(v88);
    end;
    for _, v90 in pairs(l_Players_0:GetPlayers()) do
        onPlayerAdded(v90);
    end;
    l_Players_0.PlayerAdded:Connect(onPlayerAdded);
    l_Players_0.PlayerRemoving:Connect(onPlayerRemoved);
    l_RunService_0.RenderStepped:Connect(renderStepped);
    if v14 then
        v45();
    end;
    game:GetService("UserInputService").InputBegan:Connect(function(v91, v92)
        if not v92 then
            if v91.KeyCode == Enum.KeyCode.T then
                v7 = not v7;
                print("T pressed, showInfoOfLocalPlayer: " .. tostring(v7));
            end;
            if v91.KeyCode == Enum.KeyCode.F then
                v8 = not v8;
                print("F pressed, showInfoOfRemotePlayers: " .. tostring(v8));
            end;
        end;
    end);
    return ;
else
    v6:trace("[FaceAnimatorUI] FaceAnimatorService is nil. UI disabled.");
    return ;
end;


local v0 = game:GetService("RunService"):IsStudio();
local v1 = false;
local v2 = TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out);
local v3 = TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.In);
local l_UserInputService_0 = game:GetService("UserInputService");
local l_ContextActionService_0 = game:GetService("ContextActionService");
local l_Players_0 = game:GetService("Players");
local l_TweenService_0 = game:GetService("TweenService");
local l_CoreGui_0 = game:GetService("CoreGui");
local l_StarterGui_0 = game:GetService("StarterGui");
local l_GuiService_0 = game:GetService("GuiService");
local l_RbxAnalyticsService_0 = game:GetService("RbxAnalyticsService");
local l_RunService_0 = game:GetService("RunService");
local l_Chat_0 = game:GetService("Chat");
local v14 = false;
enableAvatarContextMenu = function(v15)
    v1 = not not v15;
    if not (not v1 or v14) then
        v14 = true;
        l_RbxAnalyticsService_0:TrackEvent("Game", "AvatarContextMenuEnabled", "placeId: " .. tostring(game.PlaceId));
    end;
end;
l_StarterGui_0:RegisterSetCore("SetAvatarContextMenuEnabled", enableAvatarContextMenu);
l_StarterGui_0:RegisterSetCore("AvatarContextMenuEnabled", enableAvatarContextMenu);
l_StarterGui_0:RegisterGetCore("AvatarContextMenuEnabled", function()
    return v1;
end);
local l_Modules_0 = l_CoreGui_0:WaitForChild("RobloxGui"):WaitForChild("Modules");
local l_AvatarContextMenu_0 = l_Modules_0:WaitForChild("AvatarContextMenu");
local l_ContextMenuGui_0 = require(l_AvatarContextMenu_0:WaitForChild("ContextMenuGui"));
local l_ContextMenuItems_0 = require(l_AvatarContextMenu_0:WaitForChild("ContextMenuItems"));
local l_ContextMenuUtil_0 = require(l_AvatarContextMenu_0:WaitForChild("ContextMenuUtil"));
local l_SelectedCharacterIndicator_0 = require(l_AvatarContextMenu_0:WaitForChild("SelectedCharacterIndicator"));
local v22 = require(l_AvatarContextMenu_0.ThemeHandler);
local v23 = require(l_Modules_0.BackpackScript);
local v24 = require(l_Modules_0.EmotesMenu.EmotesMenuMaster);
local v25 = require(l_Modules_0.BlockingUtility);
local v26 = require(l_Modules_0.Flags.FFlagAvatarContextMenuItemsChatButtonRefactor);
local l_LocalPlayer_0 = l_Players_0.LocalPlayer;
while not l_LocalPlayer_0 do
    l_Players_0.PlayerAdded:wait();
    l_LocalPlayer_0 = l_Players_0.LocalPlayer;
end;
if not (l_LocalPlayer_0.UserId <= 0) or v0 then
    local v28 = nil;
    local v29 = nil;
    local v30 = false;
    local v31 = false;
    local v32 = nil;
    local v33 = nil;
    local v34 = nil;
    local v35 = nil;
    local v36 = nil;
    v29 = l_ContextMenuGui_0:CreateMenuFrame(v22:GetTheme());
    v28 = l_ContextMenuItems_0.new(v29.Content.ContextActionList);
    SetSelectedPlayer = function(v37, v38)
        if v32 == v37 then
            return ;
        else
            v32 = v37;
            l_SelectedCharacterIndicator_0:ChangeSelectedPlayer(v32, v22:GetTheme());
            if not v26 then
                v28:BuildContextMenuItems(v32);
            else
                local v39 = {};
                local l_status_0, l_result_0 = pcall(function()
                    return l_Chat_0:CanUserChatAsync(l_LocalPlayer_0.UserId);
                end);
                v39.localPlayerChatEnabled = l_status_0 and (l_RunService_0:IsStudio() or l_result_0);
                v39.localPlayerCanChatWithSelectedPlayer = false;
                if v32 then
                    v39.localPlayerCanChatWithSelectedPlayer = l_ContextMenuUtil_0:GetCanChatWith(v32);
                end;
                v28:BuildContextMenuItems(v32, v39);
            end;
            l_ContextMenuGui_0:SwitchToPlayerEntry(v32, v38);
            return ;
        end;
    end;
    OpenMenu = function(v42)
        v30 = true;
        v29.Visible = true;
        v29.Content.ContextActionList.CanvasPosition = Vector2.new(0, 0);
        v29.Position = v42.OffScreenPosition;
        v36 = l_ContextMenuGui_0.SelectedPlayerChanged:connect(function()
            SetSelectedPlayer(l_ContextMenuGui_0:GetSelectedPlayer());
        end);
        v29.Position = v42.OffScreenPosition;
        local v43 = l_TweenService_0:Create(v29, v2, {
            Position = v42.OnScreenPosition
        });
        v43:Play();
        v43.Completed:wait();
        v30 = false;
    end;
    BindMenuActions = function()
        l_ContextActionService_0:BindCoreAction("EscapeAvatarContextMenu", function(_, v45, _)
            if v45 ~= Enum.UserInputState.Begin then
                return ;
            else
                CloseContextMenu();
                return ;
            end;
        end, false, Enum.KeyCode.Escape, Enum.KeyCode.ButtonB);
        l_ContextActionService_0:BindCoreAction("SwitchPageAvatarContextMenu", function(_, v48, v49)
            if v48 == Enum.UserInputState.Begin then
                if v49.KeyCode == Enum.KeyCode.ButtonR1 then
                    l_ContextMenuGui_0:OffsetPlayerEntry(1);
                    return ;
                elseif v49.KeyCode == Enum.KeyCode.ButtonL1 then
                    l_ContextMenuGui_0:OffsetPlayerEntry(-1);
                end;
            end;
        end, false, Enum.KeyCode.ButtonR1, Enum.KeyCode.ButtonL1);
        local v50 = nil;
        v50 = l_GuiService_0.MenuOpened:connect(function()
            v50:disconnect();
            if Enum.UserInputState.Begin ~= Enum.UserInputState.Begin then
                return ;
            else
                CloseContextMenu();
                return ;
            end;
        end);
    end;
    BuildPlayerCarousel = function(_, v52)
        local v53 = {};
        local l_l_Players_0_Players_0 = l_Players_0:GetPlayers();
        for v55 = 1, #l_l_Players_0_Players_0 do
            if not (l_l_Players_0_Players_0[v55].UserId <= 0 and not v0) and l_l_Players_0_Players_0[v55] ~= l_LocalPlayer_0 then
                local l_l_ContextMenuUtil_0_PlayerPosition_0 = l_ContextMenuUtil_0:GetPlayerPosition(l_l_Players_0_Players_0[v55]);
                if l_l_ContextMenuUtil_0_PlayerPosition_0 then
                    table.insert(v53, {
                        l_l_Players_0_Players_0[v55],
                        (v52 - l_l_ContextMenuUtil_0_PlayerPosition_0).magnitude
                    });
                end;
            end;
        end;
        table.sort(v53, function(v57, v58)
            return v58[2] < v57[2];
        end);
        l_ContextMenuGui_0:BuildPlayerCarousel(v53, v22:GetTheme());
    end;
    l_Players_0.PlayerRemoving:connect(function(v59)
        if v31 and v59 ~= v32 then
            l_ContextMenuGui_0:RemovePlayerEntry(v59);
        end;
    end);
    local function _()
        if v24:isOpen() then
            v24:close();
        end;
        if v23.IsOpen then
            v23.OpenClose();
        end;
    end;
    OpenContextMenu = function(v61, v62)
        if not (v30 or v31) and v1 then
            v31 = true;
            if v24:isOpen() then
                v24:close();
            end;
            if v23.IsOpen then
                v23.OpenClose();
            end;
            BuildPlayerCarousel(v61, v62);
            l_ContextMenuUtil_0:DisablePlayerMovement();
            BindMenuActions();
            SetSelectedPlayer(v61, true);
            l_ContextMenuGui_0:UpdateGuiTheme(v22:GetTheme());
            OpenMenu(v22:GetTheme());
            return ;
        else
            return ;
        end;
    end;
    CloseContextMenu = function()
        l_GuiService_0.SelectedCoreObject = nil;
        l_ContextActionService_0:UnbindCoreAction("EscapeAvatarContextMenu");
        l_ContextActionService_0:UnbindCoreAction("SwitchPageAvatarContextMenu");
        l_ContextMenuUtil_0:EnablePlayerMovement();
        if v36 then
            v36:disconnect();
        end;
        local v63 = l_TweenService_0:Create(v29, v3, {
            Position = UDim2.new(0.5, 0, 1, v29.AbsoluteSize.Y)
        });
        v63:Play();
        v63.Completed:wait();
        v29.Visible = false;
        SetSelectedPlayer(nil);
        v31 = false;
    end;
    l_ContextMenuGui_0:SetCloseMenuFunc(CloseContextMenu);
    v28:SetCloseMenuFunc(CloseContextMenu);
    local function _(v64, v65, v66)
        local v67 = false;
        if v65.X <= v64.X then
            v67 = false;
            if v64.X <= v66.X then
                v67 = false;
                if v65.Y <= v64.Y then
                    v67 = v64.Y <= v66.Y;
                end;
            end;
        end;
        return v67;
    end;
    PointInSwipeArea = function(v69)
        local l_AbsolutePosition_0 = v29.AbsolutePosition;
        local l_NameTag_0 = v29.Content:FindFirstChild("NameTag");
        local v72 = Vector2.new(l_AbsolutePosition_0.x + v29.AbsoluteSize.x, l_NameTag_0.AbsolutePosition.y + l_NameTag_0.AbsoluteSize.y);
        local v73 = false;
        if l_AbsolutePosition_0.X <= v69.X then
            v73 = false;
            if v69.X <= v72.X then
                v73 = false;
                if l_AbsolutePosition_0.Y <= v69.Y then
                    v73 = v69.Y <= v72.Y;
                end;
            end;
        end;
        return v73;
    end;
    LocalPlayerHasToolEquipped = function()
        if l_LocalPlayer_0.Character then
            for _, v75 in ipairs(l_LocalPlayer_0.Character:GetChildren()) do
                if v75:IsA("BackpackItem") then
                    return true;
                end;
            end;
            return false;
        else
            return false;
        end;
    end;
    shouldIgnoreLocalCharacter = function()
        if l_LocalPlayer_0.Character then
            local l_Head_0 = l_LocalPlayer_0.Character:FindFirstChild("Head");
            if not (not l_Head_0 or not l_Head_0:IsA("BasePart")) then
                return l_Head_0.LocalTransparencyModifier >= 0.95;
            end;
        end;
    end;
    clickedOnPoint = function(v77)
        local l_CurrentCamera_0 = workspace.CurrentCamera;
        if l_CurrentCamera_0 then
            if not LocalPlayerHasToolEquipped() then
                local v79 = l_CurrentCamera_0:ScreenPointToRay(v77.X, v77.Y);
                v79 = Ray.new(v79.Origin, v79.Direction * 100);
                local v80 = nil;
                local v81 = nil;
                if not shouldIgnoreLocalCharacter() then
                    local l_workspace_PartOnRay_0, v83 = workspace:FindPartOnRay(v79, nil, false, true);
                    v80 = l_workspace_PartOnRay_0;
                    v81 = v83;
                else
                    local l_workspace_PartOnRay_1, v85 = workspace:FindPartOnRay(v79, l_LocalPlayer_0.Character, false, true);
                    v80 = l_workspace_PartOnRay_1;
                    v81 = v85;
                end;
                local l_l_ContextMenuUtil_0_PlayerFromPart_0 = l_ContextMenuUtil_0:FindPlayerFromPart(v80);
                if l_l_ContextMenuUtil_0_PlayerFromPart_0 and (not (not v0 or l_l_ContextMenuUtil_0_PlayerFromPart_0 == l_LocalPlayer_0) or l_l_ContextMenuUtil_0_PlayerFromPart_0 ~= l_LocalPlayer_0 and l_l_ContextMenuUtil_0_PlayerFromPart_0.UserId > 0) then
                    if not v31 then
                        OpenContextMenu(l_l_ContextMenuUtil_0_PlayerFromPart_0, v81);
                        return ;
                    else
                        SetSelectedPlayer(l_l_ContextMenuUtil_0_PlayerFromPart_0);
                        return ;
                    end;
                else
                    if not (l_l_ContextMenuUtil_0_PlayerFromPart_0 or not v31) then
                        CloseContextMenu();
                    end;
                    return ;
                end;
            else
                return ;
            end;
        else
            return ;
        end;
    end;
    OnUserInput = function(v87, v88)
        if v88.UserInputState == Enum.UserInputState.Begin and v33 == nil then
            v33 = v88;
            v34 = v87;
            return ;
        else
            if v33 == v88 and v88.UserInputState == Enum.UserInputState.Change then
                if (v87 - v34).magnitude > 5 then
                    v33 = nil;
                    v34 = nil;
                    return ;
                end;
            elseif v88.UserInputState == Enum.UserInputState.End and v33 == v88 then
                v33 = nil;
                v34 = nil;
                clickedOnPoint(v87);
            end;
            return ;
        end;
    end;
    OnMouseMoved = function(v89)
        if not (v31 or not v33) and (v89 - v34).magnitude > 20 then
            v33 = nil;
            v34 = nil;
        end;
    end;
    trackTouchSwipeInput = function(v90)
        if v90.UserInputType == Enum.UserInputType.Touch then
            if not v35 and v90.UserInputState == Enum.UserInputState.Begin then
                if PointInSwipeArea(v90.Position) then
                    v35 = v90;
                    return ;
                end;
            elseif v35 == v90 and v90.UserInputState == Enum.UserInputState.End then
                spawn(function()
                    v35 = nil;
                end);
            end;
        end;
    end;
    local function v93(v91, v92)
        trackTouchSwipeInput(v91);
        if not v92 then
            if not (v91.UserInputType ~= Enum.UserInputType.MouseButton1) or v91.UserInputType == Enum.UserInputType.Touch then
                OnUserInput(Vector2.new(v91.Position.X, v91.Position.Y), v91);
                return ;
            else
                if v91.UserInputType == Enum.UserInputType.MouseMovement then
                    OnMouseMoved(Vector2.new(v91.Position.X, v91.Position.Y));
                end;
                return ;
            end;
        else
            if v91 == v33 then
                v33 = nil;
            end;
            return ;
        end;
    end;
    l_UserInputService_0.InputBegan:Connect(v93);
    l_UserInputService_0.InputChanged:Connect(v93);
    l_UserInputService_0.InputEnded:Connect(v93);
    l_UserInputService_0.TouchSwipe:Connect(function(v94, _, v96)
        if v96 then
            if v31 then
                if v35 then
                    local v97 = 0;
                    if v94 == Enum.SwipeDirection.Left then
                        v97 = 1;
                    elseif v94 == Enum.SwipeDirection.Right then
                        v97 = -1;
                    end;
                    if v97 ~= 0 then
                        l_ContextMenuGui_0:OffsetPlayerEntry(v97);
                        SetSelectedPlayer(l_ContextMenuGui_0:GetSelectedPlayer());
                    end;
                    return ;
                else
                    return ;
                end;
            else
                return ;
            end;
        else
            return ;
        end;
    end);
    l_LocalPlayer_0.FriendStatusChanged:Connect(function(v98, v99)
        if v98 and v98 == v32 then
            v28:UpdateFriendButton(v99, (v25:IsPlayerBlockedByUserId(v98.UserId)));
        end;
    end);
    v23.StateChanged.Event:Connect(function(v100)
        if not (not v100 or not v31) then
            CloseContextMenu();
        end;
    end);
    v24.EmotesMenuToggled.Event:Connect(function(v101)
        if not (not v101 or not v31) then
            CloseContextMenu();
        end;
    end);
    GetWorldPoint = function(v102)
        if v102.Character then
            local l_HumanoidRootPart_0 = v102.Character:FindFirstChild("HumanoidRootPart");
            if l_HumanoidRootPart_0 then
                return l_HumanoidRootPart_0.Position;
            end;
        end;
        if v102 ~= l_LocalPlayer_0 then
            return GetWorldPoint(l_LocalPlayer_0);
        else
            return (Vector3.new(0, 0, 0));
        end;
    end;
    l_StarterGui_0:RegisterGetCore("AvatarContextMenuTarget", function()
        return v32;
    end);
    l_StarterGui_0:RegisterSetCore("AvatarContextMenuTarget", function(v104)
        local v105 = false;
        if typeof(v104) == "Instance" then
            v105 = v104:IsA("Player");
        end;
        if not v105 then
            if v104 == nil then
                CloseContextMenu();
                return ;
            else
                error("AvatarContextMenuTarget argument must be a Player or nil");
                return ;
            end;
        elseif v104.Parent ~= nil then
            if not v31 and not v30 then
                OpenContextMenu(v104, GetWorldPoint(v104));
                return ;
            else
                SetSelectedPlayer(v104, true);
                return ;
            end;
        else
            error("AvatarContextMenuTarget Player must be in the game");
            return ;
        end;
    end);
    local function v114()
        if l_LocalPlayer_0.Character then
            local l_HumanoidRootPart_1 = l_LocalPlayer_0.Character:FindFirstChild("HumanoidRootPart");
            if l_HumanoidRootPart_1 then
                local l_Position_0 = l_HumanoidRootPart_1.Position;
                local v108 = nil;
                local l_huge_0 = math.huge;
                local v110 = nil;
                for _, v112 in ipairs((l_Players_0:GetPlayers())) do
                    if not (not (v112 ~= l_LocalPlayer_0) or not v112.Character) then
                        local l_HumanoidRootPart_2 = v112.Character:FindFirstChild("HumanoidRootPart");
                        if l_HumanoidRootPart_2 and (l_HumanoidRootPart_2.Position - l_Position_0).magnitude < l_huge_0 then
                            v108 = v112;
                            l_huge_0 = (l_HumanoidRootPart_2.Position - l_Position_0).magnitude;
                            v110 = l_HumanoidRootPart_2.Position;
                        end;
                    end;
                end;
                return v108, v110;
            else
                return ;
            end;
        else
            return ;
        end;
    end;
    l_ContextActionService_0:BindCoreAction("GamepadOpenAvatarContextMenu", function(_, v116, _)
        if v1 then
            if not l_GuiService_0.SelectedCoreObject and not l_GuiService_0.SelectedObject then
                if v116 ~= Enum.UserInputState.Begin then
                    return Enum.ContextActionResult.Sink;
                else
                    if not v31 then
                        local v118, v119 = v114();
                        if v118 then
                            OpenContextMenu(v118, v119);
                        end;
                    else
                        CloseContextMenu();
                    end;
                    return Enum.ContextActionResult.Sink;
                end;
            else
                return Enum.ContextActionResult.Pass;
            end;
        else
            return Enum.ContextActionResult.Pass;
        end;
    end, false, Enum.KeyCode.DPadUp);
    return ;
else
    return ;
end;