local sha512 = require("../modules/sha512")

local t = os.clock()
local startgc = gcinfo()

local hash, digest = sha512("abc")
assert(
	hash
		== "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
	"(SHA-512) abc hash does not match"
)
assert(digest[1] == 0xddaf35a1, "(SHA-512) front half of first word of abc hash was incorrect")
assert(digest[2] == 0x93617aba, "(SHA-512) back half of first word hash was incorrect")
assert(digest[3] == 0xcc417349, "(SHA-512) front half of second word of abc hash was incorrect")
assert(digest[4] == 0xae204131, "(SHA-512) back half of second word of abc hash was incorrect")
assert(digest[5] == 0x12e6fa4e, "(SHA-512) front half of third word of abc hash was incorrect")
assert(digest[6] == 0x89a97ea2, "(SHA-512) back half of third word of abc hash was incorrect")
assert(digest[7] == 0x0a9eeee6, "(SHA-512) front half of fourth word of abc hash was incorrect")
assert(digest[8] == 0x4b55d39a, "(SHA-512) back half of fourth word of abc hash was incorrect")
assert(digest[9] == 0x2192992a, "(SHA-512) front half of fifth word of abc hash was incorrect")
assert(digest[10] == 0x274fc1a8, "(SHA-512) back half of fifth word of abc hash was incorrect")
assert(digest[11] == 0x36ba3c23, "(SHA-512) front half of sixth word of abc hash was incorrect")
assert(digest[12] == 0xa3feebbd, "(SHA-512) back half of sixth word of abc hash was incorrect")
assert(digest[13] == 0x454d4423, "(SHA-512) front half of seventh word of abc hash was incorrect")
assert(digest[14] == 0x643ce80e, "(SHA-512) back half of seventh word of abc hash was incorrect")
assert(digest[15] == 0x2a9ac94f, "(SHA-512) front half of eighth word of abc hash was incorrect")
assert(digest[16] == 0xa54ca49f, "(SHA-512) back half of eighth word of abc hash was incorrect")
assert(table.isfrozen(digest), "(SHA-512) returned digest of abc hash was not frozen")

assert(
	sha512("")
		== "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
	"(SHA-512) empty hash does not match"
)
assert(
	sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq")
		== "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445",
	"(SHA-512) 448 bit alphabet hash does not match"
)
assert(
	sha512(
		"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
	)
		== "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909",
	"(SHA-512) 896 bit alphabet hash does not match"
)
assert(
	sha512("foo")
		== "f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7",
	"(SHA-512) foo hash does not match"
)
assert(
	sha512("bar")
		== "d82c4eb5261cb9c8aa9855edd67d1bd10482f41529858d925094d173fa662aa91ff39bc5b188615273484021dfb16fd8284cf684ccf0fc795be3aa2fc1e6c181",
	"(SHA-512) bar hash does not match"
)
assert(
	sha512("baz")
		== "22b41602570746d784cef124fa6713eec180f93af02a1bfee05528e94a1b053e4136b446015161d04e9900849575bd8f95f857773868a205dbed42413cd054f1",
	"(SHA-512) baz hash does not match"
)
assert(
	sha512("The Fitness-Gram Pacer Test is a multi-stage aerobic capacity test")
		== "e6511df683e77e5233c274f41bc94892e96efbe20ee5abc763ebed33a7bb8c26c8c6d57ae31fb6b636f5431e4c33042c1c83563d60c5561a0de4fa6c4bbf15d4",
	"(SHA-512) Fitness-Gram hash does not match"
)

assert(
	sha512("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
		== "b0220c772cbf6c1822e2cb38a437d0e1d58772417a4bbb21c961364f8b6143e05aa6316dca8d1d7b19e16448419076395f6086cb55101fbd6d5497b148e1745f",
	"(SHA-512) 55 character a hash does not match"
)

assert(
	sha512(
		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	)
		== "130396a75cb483f2eee8c56d8a668bb3d2641f5243212c0bee2bd33da096ad9eb8179fe18f9eaacf76e09fae9de4c3f14ba13341e345be05bf76c182cc3468cb",
	"(SHA-512) 119 character a hash does not match"
)

assert(
	sha512(
		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	)
		== "fa9121c7b32b9e01733d034cfc78cbf67f926c7ed83e82200ef86818196921760b4beff48404df811b953828274461673c68d04e297b0eb7b2b4d60fc6b566a2",
	"(SHA-512) 111 character a hash does not match"
)

assert(
	sha512(
		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	)
		== "52c853cb8d907f3d4d6b889beb027985d7c273486d75f8baf26f80d24e90c74c6c3de3e22131582380a7d14d43f2941a31385439cd6ddc469f628015e50bf286",
	"(SHA-512) 239 character a hash does not match"
)

if true then
	local e = string.rep("e", 199999)
	local a = string.rep("a", 1e6)
	assert(
		sha512(e)
			== "954960c0efb4746f545ca84f52096a8748d909d8de6f9134c6fb1964f96dbbd9a89ffbd68c22ddb56ceaf7873fd11daf27a9e41b3797ab3edfcaa2d6e0ca0174",
		"(SHA-512) e hash does not match"
	)
	assert(
		sha512(a)
			== "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b",
		"(SHA-512) million a hash does not match"
	)
end

print("Change in memory:", gcinfo() - startgc)
print("SHA-512 tests completed. Took", os.clock() - t)


local xxhash32 = require("../modules/xxhash32")

local t = os.clock()
local startgc = gcinfo()

assert(xxhash32("abc") == 0x32d153ff, "(XXH32) abc hash does not match")
-- 852579327
assert(xxhash32("abc", 0x32d153ff) == 0x9041e0d8, "(XXH32) abc hash with seed does not match")

assert(xxhash32("") == 0x02cc5d05, "(XXH32) empty hash does not match")
--46947589
assert(xxhash32("", 0x02cc5d05) == 0xf6114164, "(XXH32) empty hash with seed does not match")
assert(
	xxhash32("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") == 0x89ea60c3,
	"(XXH32) 448 bit alphabet hash does not match"
)
--2313838787
assert(
	xxhash32("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 0x89ea60c3) == 0x404871e3,
	"(XXH32) 448 bit alphabet hash with seed does not match"
)
assert(
	xxhash32(
		"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
	) == 0x590b4165,
	"(XXH32) 896 bit alphabet hash does not match"
)
-- 1493909861
assert(
	xxhash32(
		"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
		0x590b4165
	) == 0xc033cd93,
	"(XXH32) 896 bit alphabet hash with seed does not match"
)
assert(xxhash32("foo") == 0xe20f0dd9, "(XXH32) foo hash does not match")
--3792637401
assert(xxhash32("foo", 0xe20f0dd9) == 0xf4d3f6c9, "(XXH32) foo hash with seed does not match")
assert(xxhash32("bar") == 0x41a22b2c, "(XXH32) bar hash does not match")
--1101146924
assert(xxhash32("bar", 0x41a22b2c) == 0x9ad4ead3, "(XXH32) bar hash with seed does not match")
assert(xxhash32("baz") == 0x9bab0987, "(XXH32) baz hash does not match")
--2611677575
assert(xxhash32("baz", 0x9bab0987) == 0x23af9b99, "(XXH32) baz hash with seed does not match")
assert(
	xxhash32("The Fitness-Gram Pacer Test is a multi-stage aerobic capacity test") == 0xbea696a7,
	"(XXH32) Fitness-Gram hash does not match"
)
--3198588583
assert(
	xxhash32("The Fitness-Gram Pacer Test is a multi-stage aerobic capacity test", 0xbea696a7) == 0x3c6ef7ed,
	"(XXH32) Fitness-Gram hash with seed does not match"
)
if true then
	local e = string.rep("e", 199999)
	local a = string.rep("a", 1e6)

	-- local t2 = os.clock()
	assert(xxhash32(e) == 0xb27dd558, "(XXH32) e hash does not match")
	--2994591064
	assert(xxhash32(e, 0xb27dd558) == 0x6174a9bd, "(XXH32) e hash with seed does not match")
	-- print(`e hash took {os.clock() - t2}`)

	-- t2 = os.clock()
	assert(xxhash32(a) == 0xe1155920, "(XXH32) million a hash does not match")
	--3776272672
	assert(xxhash32(a, 0xe1155920) == 0xc0c7beae, "(XXH32) million a hash with seed does not match")
	-- print(`million a hash took {os.clock() - t2}`)
end

print("Change in memory:", gcinfo() - startgc)
print("XXH32 tests completed. Took", os.clock() - t)


-- From dphfox/tiniest, licenced under BSD
--!strict

local tiniest_plugin = require("./tiniest_plugin")

type Context = DescribeContext | RunContext

type DescribeContext = {
	type: "describe",
	labels: {string},
	add_test: (Test) -> ()
}

type RunContext = {
	type: "run"
}

export type ErrorReport = {
	type: "tiniest.ErrorReport",
	line: number,
	source: string,
	message: string,
	trace: string,
	code: {
		snippet: string,
		line: string
	}?
}

export type Test = {
	labels: {string},
	run: () -> ()
}

export type RunResult = {
	tests: {Test},
	status_tally: {
		pass: number,
		fail: number
	},
	individual: {[Test]: TestRunResult}
}

export type TestRunResult = PassTestRunResult | FailTestRunResult

export type PassTestRunResult = {
	status: "pass"
}

export type FailTestRunResult = {
	status: "fail",
	error: ErrorReport
}

export type Options = {
	plugins: nil | {tiniest_plugin.Plugin}
}

export type RunOptions = {}

local function catch_errors(func: any, ...)
	local outer_trace
	local function handler(message)
		local report: ErrorReport
		if typeof(message) == "table" and message.type == "tiniest.ErrorReport" then
			report = message :: ErrorReport
		else
			local source, line = debug.info(2, "sl")
			report = {
				type = "tiniest.ErrorReport",
				line = line,
				source = source,
				message = tostring(message),
				trace = debug.traceback(nil, 2)
			}
		end
		local from, to = string.find(report.trace, outer_trace, 1, true)
		if from ~= nil and to ~= nil then
			report.trace = report.trace:sub(1, from - 1) .. report.trace:sub(to + 1)
		end
		return report
	end
	outer_trace = debug.traceback(); return xpcall(func, handler, ...)
end

local tiniest = {}

function tiniest.configure(
	options: Options
)

	local plugins = tiniest_plugin.configure(options.plugins)

	local get_context, with_root_context, with_inner_context
	do
		local current_context: Context? = nil

		function get_context(): Context
			assert(current_context ~= nil, "This function can only be called from inside of a test suite")
			return current_context
		end

		function with_root_context(
			root_context: Context,
			inner: (Context) -> ()
		): ()
			assert(current_context == nil, "This function can't be called from inside of a test suite")
			current_context = root_context
			local ok, err = catch_errors(inner :: any, root_context)
			current_context = nil
			if not ok then
				error(err, 0)
			end
		end

		function with_inner_context(
			make_context: (Context) -> Context,
			inner: (Context) -> ()
		): ()
			local outer_context = get_context()
			local inner_context = make_context(outer_context)
			current_context = inner_context
			local ok, err = catch_errors(inner :: any, inner_context)
			current_context = outer_context
			if not ok then
				error(err, 0)
			end
		end
	end

	local self = {}

	function self.describe(
		label: string,
		inner: () -> ()
	): ()
		with_inner_context(function(outer_context: Context): Context
			assert(outer_context.type == "describe", "This function can only be called outside of tests")
			local context = table.clone(outer_context)
			context.labels = table.clone(context.labels)
			table.insert(context.labels, label)
			return context
		end, inner)
	end

	function self.test(
		label: string,
		run: () -> ()
	): ()
		local context = get_context()
		assert(context.type == "describe", "This function can only be called outside of tests")
		local labels = table.clone(context.labels)
		table.insert(labels, label)
		local test: Test = {
			labels = labels,
			run = run
		}
		context.add_test(test)
	end

	function self.collect_tests(
		inner: () -> ()
	): {Test}
		local tests = {}
		local context: Context = {
			type = "describe",
			labels = {},
			add_test = function(test)
				table.insert(tests, test)
			end
		}
		with_root_context(context, inner)
		return tests
	end

	function self.run_tests(
		tests: {Test},
		run_options: RunOptions
	): RunResult
		plugins.notify("before_run", tests, run_options)

		local context: Context = {
			type = "run"
		}

		local individual = {}
		for _, test in tests do
			plugins.notify("before_test", test, run_options)
			local _, test_run_result = xpcall(
				function()
					with_root_context(context, test.run)
					local pass: TestRunResult = {
						status = "pass"
					}
					return pass
				end,
				function(message)
					if typeof(message) == "table" and message.type == "tiniest.ErrorReport" then
						local fail: TestRunResult = {
							status = "fail",
							error = message :: ErrorReport
						}
						return fail
					else
						local source, line = debug.info(1, "sl")
						local error: ErrorReport = {
							type = "tiniest.ErrorReport",
							line = line,
							source = source,
							message = tostring(message),
							trace = debug.traceback()
						}
						local fail: TestRunResult = {
							status = "fail",
							error = error
						}
						return fail
					end
				end
			)
			plugins.notify("after_test", test, test_run_result, run_options)
			individual[test] = test_run_result
		end

		local status_tally = {
			pass = 0,
			fail = 0
		}

		for _, result in individual do
			status_tally[result.status] += 1
		end

		local result: RunResult = {
			tests = tests,
			status_tally = status_tally,
			individual = individual
		}
		plugins.notify("after_run", result, run_options)

		return result
	end

	return self
end

return tiniest


-- From dphfox/tiniest, licenced under BSD
--!strict

local tiniest_quote = require("./tiniest_quote")

local tiniest_expect = {}

type Context = {
	target: unknown,
	test: string,
	params: {unknown}
}

local context: Context = {
	target = nil,
	test = "",
	params = {}
}

local function fail(
	message: string?
): never
	local quoted = {}
	for index, param in context.params do
		quoted[index] = tiniest_quote(param)
	end
	local param_list = table.concat(quoted, ",")
	local source, line = debug.info(4, "sl")
	error({
		type = "tiniest.ErrorReport",
		line = line,
		source = source,
		message = "Expectation not met" .. if message == nil then "" else `, because:\n{message}`,
		trace = debug.traceback(nil, 4),
		code = {
			snippet = `expect({tiniest_quote(context.target)}).{context.test}({param_list})`,
			line = line
		}
	}, 0)
end

local function check(
	condition: boolean,
	cause: string
): ()
	if not condition then
		error(`Bad usage of expect().{context.test}()\nCause: {cause}`, 0)
	end
end

local function is_indexable(
	x: unknown
): boolean
	return type(x) == "table" or type(x) == "userdata"
end

function tiniest_expect.expect(
	a: any
)
	local tests = {}

	function tests.exists()
		return if a ~= nil then tests else fail()
	end

	function tests.never_exists()
		return if a == nil then tests else fail()
	end

	function tests.is(
		b: any
	)
		return if a == b then tests else fail()
	end

	function tests.never_is(
		b: any
	)
		return if a ~= b then tests else fail()
	end

	function tests.is_true()
		check(typeof(a) == "boolean", "expect() value must be boolean")
		return if a then tests else fail()
	end

	function tests.never_is_true()
		check(typeof(a) == "boolean", "expect() value must be boolean")
		return if a then tests else fail()
	end

	function tests.is_a(
		b: string
	)
		return if typeof(a) == b then tests else fail()
	end

	function tests.never_is_a(
		b: string
	)
		return if typeof(a) ~= b then tests else fail()
	end

	function tests.has_key(
		b: any
	)
		check(is_indexable(a), "expect() value must be indexable")
		check(b ~= nil, "key cannot be nil")
		return if a[b] then tests else fail()
	end

	function tests.never_has_key(
		b: any
	)
		check(is_indexable(a), "expect() value must be indexable")
		check(b ~= nil, "key cannot be nil")
		return if not a[b] then tests else fail(`[{tiniest_quote(b)}] = {tiniest_quote(a[b])}`)
	end

	function tests.has_value(
		b: any
	)
		check(typeof(a) == "table", "expect() value must be table")
		check(b ~= nil, "value cannot be nil")
		return if table.find(a, b) then tests else fail()
	end

	function tests.never_has_value(
		b: any
	)
		check(typeof(a) == "table", "expect() value must be table")
		check(b ~= nil, "value cannot be nil")
		local index = table.find(a, b)
		return if index == nil then tests else fail(`Found at index {index}`)
	end

	function tests.fails()
		check(typeof(a) == "function", "expect() value must be function")
		local ok = pcall(a)
		return if not ok then tests else fail()
	end

	function tests.never_fails()
		check(typeof(a) == "function", "expect() value must be function")
		local ok, err = pcall(a)
		return if ok then tests else fail(`Failed with {tiniest_quote(err)}`)
	end

	function tests.fails_with(
		b: string
	)
		check(typeof(a) == "function", "expect() value must be function")
		local ok, err = pcall(a)
		return if not ok and err:lower():match(b:lower()) then tests else
				if ok then fail("Did not fail")
				else fail(`Failed with {tiniest_quote(err)}`)
	end

	function tests.never_fails_with(
		b: string
	)
		check(typeof(a) == "function", "expect() value must be function")
		local ok, err = pcall(a)
		return if ok or not err:lower():match(b:lower()) then tests else fail()
	end

	for name: any, body: any in tests do
		tests[name] = function(...)
			context.target = a
			context.test = name
			context.params = {...}
			return body(...)
		end
	end

	return tests
end

return tiniest_expect


-- From dphfox/tiniest, licenced under BSD
--!strict

local tiniest_expect = require("./tiniest_expect")
local tiniest_time = require("./tiniest_time")
local tiniest_pretty = require("./tiniest_pretty")
local tiniest = require("./tiniest")

export type Options = {
	snapshot_path: string?,
	save_snapshots: boolean?,
	pretty: nil | {
		disable_emoji: boolean?,
		disable_unicode: boolean?,
		disable_output: nil | {
			after_run: boolean?
		}
	}
}

export type CollectOptions = {
	file_name_pattern: string?
}

local tiniest_for_roblox = {}

function tiniest_for_roblox.configure(
	options: Options
)
	local self = {}

	self.expect = tiniest_expect.expect

	local tiniest_time = tiniest_time.configure({
		get_timestamp = os.clock
	})

	local tiniest_pretty = tiniest_pretty.configure({
		disable_colour = true,
		disable_emoji = options.pretty and options.pretty.disable_emoji,
		disable_unicode = options.pretty and options.pretty.disable_unicode,
		disable_output = options.pretty and options.pretty.disable_output,
		plugins = { tiniest_time :: any }
	})
	self.format_run = tiniest_pretty.format_run

	local tiniest = tiniest.configure({
		plugins = { tiniest_time :: any, tiniest_pretty }
	})
	self.describe = tiniest.describe
	self.test = tiniest.test
	self.collect_tests = tiniest.collect_tests
	self.run_tests = tiniest.run_tests

	function self.collect_tests_from_hierarchy(
		ancestor: Instance,
		declared_collect_options: CollectOptions?
	)
		local collect_options: CollectOptions = declared_collect_options or {}
		local file_name_pattern = collect_options.file_name_pattern or "%.spec$"
		local function discover(
			ancestor: Instance
		): ()
			self.describe(ancestor.Name, function()
				if ancestor:IsA("ModuleScript") and ancestor.Name:match(file_name_pattern) then
					local requireOk, module = pcall(require, ancestor)
					if not requireOk then
						error(`Error while requiring {ancestor:GetFullName()}`, 0)
					elseif typeof(module) ~= "function" then
						error(`Did not get a function from {ancestor:GetFullName()}`, 0)
					end
					local collectOk, err = pcall(module, self)
					if not collectOk then
						error(err, 0)
					end
				end
				local children = ancestor:GetChildren()
				if #children > 0 then
					table.sort(children, function(a, b)
						return a.Name < b.Name
					end)
					for _, child in children do
						discover(child)
					end
				end
			end)

		end
		return self.collect_tests(function()
			discover(ancestor)
		end)
	end

	return self
end

export type Configured = typeof(tiniest_for_roblox.configure({}))

return tiniest_for_roblox


-- From dphfox/tiniest, licenced under BSD
--!strict

export type Plugin = {
	is_tiniest_plugin: true
}

local tiniest_plugin = {}

function tiniest_plugin.configure(
	plugins: {any}?
)
	if plugins ~= nil then
		for index, plugin in plugins do
			if not plugin.is_tiniest_plugin then
				error(`sanity check: plugin #{index} is not a valid plugin`)
			end
		end
	end

	local self = {}

	function self.notify(
		method_name: string,
		...
	): ()
		if plugins == nil then
			return
		end
		for _, plugin in plugins do
			local method = plugin[method_name]
			if typeof(method) == "function" then
				method(...)
			end
		end
	end

	return self
end

return tiniest_plugin


-- From dphfox/tiniest, licenced under BSD
--!strict

local tiniest_plugin = require("./tiniest_plugin")
local tiniest = require("./tiniest")
type Test = tiniest.Test
type TestRunResult = tiniest.TestRunResult
type RunResult = tiniest.RunResult

export type Options = {
	plugins: nil | {tiniest_plugin.Plugin},
	disable_emoji: boolean?,
	disable_unicode: boolean?,
	disable_colour: boolean?,
	disable_output: nil | {
		after_run: boolean?
	}
}

local tiniest_pretty = {}

function tiniest_pretty.configure(
	options: Options
)
	if options.disable_unicode then
		options.disable_emoji = true
	end

	local plugins = tiniest_plugin.configure(options.plugins)

	local function string_len(text: string)
		if options.disable_unicode then
			return string.len(text)
		else
			return utf8.len(text) or string.len(text)
		end
	end
	local status_icons = {
		pass = if options.disable_emoji then "[PASS]" else "✅",
		fail = if options.disable_emoji then "[FAIL]" else "❌"
	}
	local crumb_trail = if options.disable_unicode then " > " else " ▸ "
	local divider = if options.disable_unicode then "=" else "═"
	local margin_line = if options.disable_unicode then "|" else "│"

	local paint = {}
	do
		local function ansi_mode(...)
			if options.disable_colour then
				return ""
			else
				return `{string.char(27)}[{table.concat({...}, ";")}m`
			end
		end
		local DIM = ansi_mode("2")
		local PASS = ansi_mode("1", "32")
		local PASS_DIM = ansi_mode("2", "32")
		local FAIL = ansi_mode("1", "31")
		local FAIL_DIM = ansi_mode("2", "31")
		local TRACE = ansi_mode("1", "34")
		local TRACE_DIM = ansi_mode("2", "34")
		local NUMBER = ansi_mode("1", "36")
		local STRING = ansi_mode("1", "33")
		local KEYWORD = ansi_mode("1", "35")
		local RESET = ansi_mode("0")

		function paint.dim(
			text: string
		): string
			return DIM .. text .. RESET
		end

		function paint.pass(
			text: string
		): string
			return PASS .. text .. RESET
		end

		function paint.pass_dim(
			text: string
		): string
			return PASS_DIM .. text .. RESET
		end

		function paint.fail(
			text: string
		): string
			return FAIL .. text .. RESET
		end

		function paint.fail_dim(
			text: string
		): string
			return FAIL_DIM .. text .. RESET
		end

		function paint.trace(
			text: string
		): string
			return TRACE .. text .. RESET
		end

		function paint.trace_dim(
			text: string
		): string
			return TRACE_DIM .. text .. RESET
		end

		function paint.number(
			text: string
		): string
			return NUMBER .. text .. RESET
		end

		function paint.string(
			text: string
		): string
			return STRING .. text .. RESET
		end

		function paint.keyword(
			text: string
		): string
			return KEYWORD .. text .. RESET
		end
	end

	local LINE_LENGTH = 80
	local full_width_divider = paint.dim(string.rep(divider, LINE_LENGTH))
	local function title(
		text: string
	): string
		local no_ansi = text:gsub(`{string.char(27)}.-m`, "")
		local divider_count = (LINE_LENGTH - string_len(no_ansi) - 2) / 2
		local divider_lhs = string.rep(divider, math.ceil(divider_count))
		local divider_rhs = string.rep(divider, math.floor(divider_count))
		return paint.dim(divider_lhs) .. " " .. text .. " " .. paint.dim(divider_rhs)
	end
	local function syntax(
		snippet: string
	): string
		return snippet
			:gsub("0?[xXbB]?%d*%.?%d*[eE]?%-?%d+", paint.number)
			:gsub("\".-\"", paint.string)
			:gsub("\'.-\'", paint.string)
			:gsub("function", paint.keyword)
			:gsub("end", paint.keyword)
			:gsub("true", paint.keyword)
			:gsub("false", paint.keyword)
			:gsub("nil", paint.keyword)
			:gsub("%-%-.-\n", paint.dim)
	end
	local function indent(
		passage: string,
		indentation: string
	): string
		return passage:gsub("\n", "\n" .. indentation)
	end

	local self = {}
	self.is_tiniest_plugin = true

	function self.format_run(
		run_result: RunResult
	): string
		local lines = {}

		local pretty_results = {}
		for _, test in run_result.tests do
			local result = run_result.individual[test]
		local painted_labels = table.clone(test.labels)
		for index, label in painted_labels do
			local style = result.status .. if index < #painted_labels then "_dim" else ""
			painted_labels[index] = paint[style](label)
		end
		local annotations = ""
		local function add_annotation(
			annotation: string
		): ()
			annotations ..= ` - {annotation}`
		end
		plugins.notify("add_annotations", result, options, add_annotation)
			local pretty = {
			test = test,
			result = result,
			crumbs = table.concat(painted_labels, paint[result.status .. "_dim"](crumb_trail)),
			icon = status_icons[result.status],
			annotations = paint.dim(annotations)
		}
			table.insert(pretty_results, pretty)
		end

		if run_result.status_tally.fail > 0 then
			table.insert(lines, title(`Errors from {run_result.status_tally.fail} test(s)`))
			table.insert(lines, "")
			for _, pretty in pretty_results do
				if pretty.result.status == "pass" then continue end
				table.insert(lines, `{pretty.icon} {pretty.crumbs}`)
				table.insert(lines, paint.trace(pretty.result.error.message))
				local code = pretty.result.error.code
				if code ~= nil then
					local num_length = string_len(code.line)
					local empty_margin = string.rep(" ", num_length + 1) .. paint.dim(margin_line) .. " "
					table.insert(lines, empty_margin)
					table.insert(lines, `{paint.dim(`{code.line} {margin_line}`)} {indent(syntax(code.snippet), empty_margin)}`)
					table.insert(lines, empty_margin)
				end
				local trace = pretty.result.error.trace:gsub("\n+$", "")
				table.insert(lines, paint.trace_dim(trace))
				table.insert(lines, "")
			end
		end

		local line_items = {}
		local function add_line_items(
			key: string,
			value: string
		): ()
			table.insert(line_items, `{paint.dim(key .. ":")} {value}`)
		end
		plugins.notify("add_line_items", run_result, options, add_line_items)

		table.insert(lines, title(`Status of {#pretty_results} test(s)`))
		table.insert(lines, "")
		for _, pretty in pretty_results do
			table.insert(lines, `{pretty.icon} {pretty.crumbs}{pretty.annotations}`)
		end
		table.insert(lines, "")
		table.insert(lines, title(`{paint.pass(`{run_result.status_tally.pass} passed`)}, {paint.fail(`{run_result.status_tally.fail} failed`)}`))
		if #line_items > 0 then
			table.insert(lines, "")
			for _, line_item in line_items do
				table.insert(lines, line_item)
			end
			table.insert(lines, "")
			table.insert(lines, full_width_divider)
		end

		return table.concat(lines, "\n")
	end

	function self.after_run(
		run_result: RunResult,
		_
	): ()
		if options.disable_output and options.disable_output.after_run then
			return
		end
		print(self.format_run(run_result))
	end

	return self
end

return tiniest_pretty


-- From dphfox/tiniest, licenced under BSD
--!strict

local function tiniest_quote(
	x: unknown,
	given_indent_amount: number?
): string
	if
		type(x) == "nil" or
		type(x) == "number" or
		type(x) == "boolean" or
		type(x) == "userdata"
	then
		return tostring(x)
	elseif type(x) == "string" then
		return string.format("%q", x)
	elseif type(x) == "function" then
		return "function() ... end"
	elseif type(x) == "table" then
		local outer_indent_amount = given_indent_amount or 0
		local inner_indent_amount = outer_indent_amount + 1
		local outer_indent = string.rep("  ", outer_indent_amount)
		local inner_indent = string.rep("  ", inner_indent_amount)

		local tbl: {} = x :: any
		local sorted_pairs = {}
		for key, value in tbl do
			table.insert(sorted_pairs, {
				key = tiniest_quote(key, inner_indent_amount),
				value = tiniest_quote(value, inner_indent_amount)
			})
		end
		table.sort(sorted_pairs, function(a, b)
			return a.key < b.key
		end)
		local frozen = table.isfrozen(tbl)
		local lines = {if frozen then "table.freeze {" else "{"}
		for _, pair in sorted_pairs do
			table.insert(lines, `{inner_indent}[{pair.key}] = {pair.value};`)
		end
		table.insert(lines, outer_indent .. "}")
		return table.concat(lines, "\n")
	else
		return `{type(x)}({tostring(x)})`
	end
end

return tiniest_quote :: (unknown) -> string


-- From dphfox/tiniest, licensed under BSD3
--!strict

local tiniest_quote = require("./tiniest_quote")
local tiniest = require("./tiniest")
type Test = tiniest.Test

export type TestRunResult = tiniest.TestRunResult & {
	num_snapshots_updated: number,
	num_snapshots_obsolete: number
}
export type RunResult = tiniest.RunResult & {
	num_snapshots_updated: number,
	num_snapshots_obsolete: number
}

export type Options = {
	save_snapshots: nil | (key: string, values: {string}) -> (),
	load_snapshots: nil | (key: string) -> {string}?
}

type TestContext = {
	key: string,
	snapshots: {string},
	next_snapshot_index: number,
	num_updated: number
}

type RunContext = {
	num_updated: number,
	num_obsolete: number
}

local tiniest_snapshot = {}

function tiniest_snapshot.configure(
	options: Options
)

	local self = {}
	self.is_tiniest_plugin = true

	local function get_test_key(
		test: Test
	)
		local labels = {}
		for _, label in test.labels do
			label = label:lower():gsub("[^%w%s]", ""):gsub(" ", "_")
			table.insert(labels, label)
		end
		return table.concat(labels, ".")
	end

	local run_context: RunContext?
	local test_context: TestContext?

	function self.snapshot(
		x: unknown
	): ()
		if options.load_snapshots == nil then
			error("snapshot() is unavailable - snapshots have not been configured", 0)
		elseif run_context == nil or test_context == nil then
			error("snapshot() can only be used while a test is running with tiniest_snapshot", 0)
		end

		local snapshot_index = test_context.next_snapshot_index
		test_context.next_snapshot_index += 1

		local fresh_snapshot = tiniest_quote(x)
		local snapshot_on_disk = test_context.snapshots[snapshot_index]
		test_context.snapshots[snapshot_index] = fresh_snapshot

		if fresh_snapshot == snapshot_on_disk then
			return
		end

		if options.save_snapshots then
			run_context.num_updated += 1
			test_context.num_updated += 1
			test_context.snapshots[snapshot_index] = fresh_snapshot
		elseif snapshot_on_disk == nil then
			local source, line = debug.info(2, "sl")
			error({
				type = "tiniest.ErrorReport",
				source = source,
				message = "New snapshot() call needs to be saved.\nRun while saving snapshots to save it to disk.",
				trace = debug.traceback(nil, 2),
				code = {
					snippet = `snapshot({fresh_snapshot})`,
					line = line
				}
			}, 0)
		else
			local source, line = debug.info(2, "sl")
			error({
				type = "tiniest.ErrorReport",
				source = source,
				message = "Snapshot does not match",
				trace = debug.traceback(nil, 2),
				code = {
					snippet = `snapshot({fresh_snapshot})\n\n-- snapshot on disk:\nsnapshot({snapshot_on_disk})`,
					line = line
				}
			}, 0)
		end
	end

	function self.before_run(
		_, _
	): ()
		run_context = {
			num_updated = 0,
			num_obsolete = 0
		}
	end

	function self.after_run(
		original_run_result: tiniest.RunResult,
		_
	): ()
		assert(run_context ~= nil)
		local run_result = original_run_result :: RunResult
		run_result.num_snapshots_updated = run_context.num_updated
		run_result.num_snapshots_obsolete = run_context.num_obsolete
		run_context = nil
	end

	function self.before_test(
		test: Test,
		_
	): ()
		assert(run_context ~= nil)
		local key = get_test_key(test)
		test_context = {
			key = key,
			snapshots = options.load_snapshots and options.load_snapshots(key) or {},
			next_snapshot_index = 1,
			num_updated = 0
		}
	end

	function self.after_test(
		_,
		original_run_result: tiniest.TestRunResult,
		_
	): ()
		assert(run_context ~= nil)
		assert(test_context ~= nil)
		local run_result = original_run_result :: TestRunResult
		run_result.num_snapshots_updated = test_context.num_updated
		run_result.num_snapshots_obsolete = test_context.next_snapshot_index - #test_context.snapshots - 1
		run_context.num_obsolete += run_result.num_snapshots_obsolete
		if test_context.num_updated > 0 and options.save_snapshots ~= nil then
			options.save_snapshots(test_context.key, test_context.snapshots)
		end
		test_context = nil
	end

	function self.add_annotations(
		original_run_result: tiniest.TestRunResult,
		_,
		add_annotation: (string) -> ()
	)
		local run_result = original_run_result :: TestRunResult
		if run_result.num_snapshots_updated > 0 then
			add_annotation(`{run_result.num_snapshots_updated} snapshot(s) updated`)
		end
		if run_result.num_snapshots_obsolete > 0 then
			add_annotation(`{run_result.num_snapshots_obsolete} snapshot(s) obsolete`)
		end
	end

	function self.add_line_items(
		original_run_result: tiniest.RunResult,
		_,
		add_line_item: (string, string) -> ()
	)
		local run_result = original_run_result :: RunResult
		if run_result.num_snapshots_updated > 0 then
			add_line_item("Updated snapshots", tostring(run_result.num_snapshots_updated))
		end
		if run_result.num_snapshots_obsolete > 0 then
			add_line_item("Obsolete snapshots", tostring(run_result.num_snapshots_obsolete))
		end
	end

	return self
end

return tiniest_snapshot


-- From dphfox/tiniest, licenced under BSD
--!strict

local tiniest = require("./tiniest")
local tiniest_pretty = require("./tiniest_pretty")
type Test = tiniest.Test

export type TestRunResult = tiniest.TestRunResult & {
	duration: number
}
export type RunResult = tiniest.RunResult & {
	duration: number
}

export type Options = {
	get_timestamp: () -> number
}

local tiniest_time = {}

local function format_duration(
	seconds: number,
	options: tiniest_pretty.Options
): string
	local SECOND = 1
	local MILLISECOND = SECOND / 1000
	local MICROSECOND = MILLISECOND / 1000
	local suffix = {
		micro = if options.disable_unicode then "u" else "µ",
		milli = "m",
	}

	if seconds < 100 * MICROSECOND then
		return `{math.ceil(seconds / MICROSECOND * 100) / 100}{suffix.micro}s`
	elseif seconds < 100 * MILLISECOND then
		return `{math.ceil(seconds / MILLISECOND * 100) / 100}{suffix.milli}s`
	else
		return `{math.ceil(seconds * 100) / 100}s`
	end
end

function tiniest_time.configure(
	options: Options
)

	local self = {}
	self.is_tiniest_plugin = true

	local start_times = {}

	function self.before_run(
		tests: {Test},
		_
	): ()
		start_times[tests] = options.get_timestamp()
	end

	function self.after_run(
		original_run_result: tiniest.RunResult,
		_
	): ()
		local run_result = original_run_result :: RunResult
		run_result.duration = options.get_timestamp() - start_times[run_result.tests]
		start_times[run_result.tests] = nil
	end

	function self.before_test(
		test: Test,
		_
	): ()
		start_times[test] = options.get_timestamp()
	end

	function self.after_test(
		test: Test,
		original_run_result: tiniest.TestRunResult,
		_
	): ()
		local run_result = original_run_result :: TestRunResult
		run_result.duration = options.get_timestamp() - start_times[test]
		start_times[test] = nil
	end

	function self.add_annotations(
		original_run_result: tiniest.TestRunResult,
		options: tiniest_pretty.Options,
		add_annotation: (string) -> ()
	)
		local run_result = original_run_result :: TestRunResult
		add_annotation(format_duration(run_result.duration, options))
	end

	function self.add_line_items(
		original_run_result: tiniest.RunResult,
		options: tiniest_pretty.Options,
		add_line_item: (string, string) -> ()
	)
		local run_result = original_run_result :: RunResult
		add_line_item("Time to run", format_duration(run_result.duration, options))
	end

	return self
end

return tiniest_time


return function(name: string, test: () -> ())
	local start = os.clock()
	local iter = 0
	while os.clock() - start < 2 do
		iter += 1
		test()
	end
	local endTime = os.clock()
	local elapsed = endTime - start
	print(`{name}: {elapsed / iter * 1000 * 1000} us/iter`)
end


local signals = require("../src")
local bench = require("./bench")

local computed = signals.computed
local effect = signals.effect
local signal = signals.signal

local dimensions = { 1, 10, 100 }

for _, w in dimensions do
	for _, h in dimensions do
		bench(`propagate: {w} * {h}`, function()
			local src, setSrc = signal({ w = w, h = h })
			for i = 1, w do
				local last = src
				for j = 1, h do
					local prev = last
					last = computed(function()
						return {
							[`{i}-{j}`] = prev(),
						}
					end)
				end
				effect(function()
					last()
				end)
			end
			setSrc({ upstream = src() })
		end)
	end
end


local signals = require("../src")
local bench = require("./bench")

local computed = signals.computed
local effect = signals.effect
local signal = signals.signal

local dimensions = { 1, 10, 100 }

for _, w in dimensions do
	for _, h in dimensions do
		bench(`propagate: {w} * {h}`, function()
			local src, setSrc = signal(1)
			for _ = 1, w do
				local last = src
				for _ = 1, h do
					local prev = last
					last = computed(function()
						return prev() + 1
					end)
				end
				effect(function()
					last()
				end)
			end
			setSrc(src() + 1)
		end)
	end
end


local system = require("@self/system")

local createReactiveSystem = system.createReactiveSystem
local SubscriberFlags = system.SubscriberFlags

type Subscriber = system.Subscriber
type Dependency = system.Dependency
type SubscriberFlags = system.SubscriberFlags

local Computed = SubscriberFlags.Computed
local Effect = SubscriberFlags.Effect
local Dirty = SubscriberFlags.Dirty
local PendingComputed = SubscriberFlags.PendingComputed
local PendingEffect = SubscriberFlags.PendingEffect

type EffectScope = {
	isScope: true,
} & Subscriber

type Effect = {
	fn: () -> (),
} & Subscriber & Dependency

type Signal<T> = {
	currentValue: T,
} & Dependency

type Computed<T> = {
	getter: (previousValue: T?) -> T,
} & Signal<T?> & Subscriber

local batchDepth = 0
local activeSub: Subscriber?
local activeScope: EffectScope?

local reactive: typeof(createReactiveSystem(function() end, function() end)) = {}
local startTracking = reactive.startTracking
local endTracking = reactive.endTracking
local updateDirtyFlag = reactive.updateDirtyFlag
local processPendingInnerEffects = reactive.processPendingInnerEffects

local function notifyEffect(e: Effect): boolean
	local flags = e.flags
	if bit32.band(flags, Dirty) ~= 0 or (bit32.band(flags, PendingComputed) ~= 0 and updateDirtyFlag(e, flags)) then
		local prevSub = activeSub
		activeSub = e
		startTracking(e)
		local success, error = pcall(e.fn)
		activeSub = prevSub
		endTracking(e)
		if not success then
			error(error)
		end
	else
		processPendingInnerEffects(e, e.flags)
	end
	return true
end

local function notifyEffectScope(e: EffectScope): boolean
	local flags = e.flags
	if bit32.band(flags, PendingEffect) ~= 0 then
		processPendingInnerEffects(e, e.flags)
		return true
	end
	return false
end

local function effectStop(this: Subscriber): () -> ()
	return function()
		startTracking(this)
		endTracking(this)
	end
end

local function updateComputed<T>(computed: Computed<T>): boolean
	local prevSub = activeSub
	activeSub = computed
	startTracking(computed)

	local oldValue = computed.currentValue
	local success, newValue = pcall(computed.getter, oldValue)
	activeSub = prevSub
	endTracking(computed)
	if not success then
		error(newValue)
	end
	local result = false
	if newValue ~= oldValue then
		computed.currentValue = newValue
		result = true
	end
	return result
end

local function notify(e: Effect | EffectScope)
	if e.isScope then
		return notifyEffectScope(e)
	else
		return notifyEffect(e)
	end
end

reactive = createReactiveSystem(updateComputed, notify)

startTracking = reactive.startTracking
endTracking = reactive.endTracking
updateDirtyFlag = reactive.updateDirtyFlag
processPendingInnerEffects = reactive.processPendingInnerEffects
local link = reactive.link
local propagate = reactive.propagate
local processEffectNotifications = reactive.processEffectNotifications
local processComputedUpdate = reactive.processComputedUpdate

local function computedGetter<T>(this: Computed<T>): () -> T
	return function()
		local flags = this.flags
		if bit32.band(flags, bit32.bor(Dirty, PendingComputed)) ~= 0 then
			processComputedUpdate(this, flags)
		end
		if activeSub then
			link(this, activeSub)
		elseif activeScope then
			link(this, activeScope)
		end
		return this.currentValue
	end
end

local function signalGetterSetter<T>(this: Signal<T>): (() -> T?, (value: T) -> ())
	local function getter(): T?
		if activeSub then
			link(this, activeSub)
		end
		return this.currentValue
	end

	local function setter(value)
		if this.currentValue ~= value then
			this.currentValue = value
			local subs = this.subs
			if subs then
				propagate(subs)
				if batchDepth == 0 then
					processEffectNotifications()
				end
			end
		end
	end

	return getter, setter
end

local pauseStack: { Subscriber? } = {}

local function startBatch()
	batchDepth += 1
end

local function endBatch()
	batchDepth -= 1
	if batchDepth == 0 then
		processEffectNotifications()
	end
end

local function pauseTracking()
	table.insert(pauseStack, activeSub)
	activeSub = nil
end

local function resumeTracking()
	activeSub = table.remove(pauseStack)
end

local function signal<T>(intialValue: T?): (() -> T?, (value: T?) -> ())
	return signalGetterSetter({
		currentValue = intialValue,
	})
end

local function computed<T>(getter: (previousValue: T?) -> T): () -> T
	return computedGetter({
		flags = bit32.bor(Computed, Dirty),
		getter = getter,
	})
end

local function effect<T>(fn: () -> T): () -> ()
	local e: Effect = {
		fn = fn,
		flags = Effect,
	}
	if activeSub then
		link(e, activeSub)
	elseif activeScope then
		link(e, activeScope)
	end
	local prevSub = activeSub
	activeSub = e
	local success, error = pcall(e.fn)
	activeSub = prevSub
	if not success then
		error(error)
	end
	return effectStop(e)
end

local function effectScope<T>(fn: () -> T): () -> ()
	local e: EffectScope = {
		flags = Effect,
		isScope = true,
	}
	local prevSub = activeScope
	activeScope = e
	local success, error = pcall(fn)
	activeScope = prevSub
	if not success then
		error(error)
	end
	return effectStop(e)
end

return {
	batchDepth = batchDepth,
	startBatch = startBatch,
	endBatch = endBatch,
	pauseTracking = pauseTracking,
	resumeTracking = resumeTracking,
	signal = signal,
	computed = computed,
	effect = effect,
	effectScope = effectScope,
	createReactiveSystem = createReactiveSystem,
	SubscriberFlags = SubscriberFlags,
}


export type Dependency = {
	subs: Link?,
	subsTail: Link?,
}

export type Subscriber = {
	flags: SubscriberFlags,
	deps: Link?,
	depsTail: Link?,
}

export type Link = {
	dep: Dependency | (Dependency & Subscriber),
	sub: Subscriber | (Dependency & Subscriber),
	prevSub: Link?,
	nextSub: Link?,
	nextDep: Link?,
}

type OneWayLink<T> = {
	target: T,
	linked: OneWayLink<T>?,
}

export type SubscriberFlags = number

local Computed = 0b00000001
local Effect = 0b00000010
local Tracking = 0b00000100
local Notified = 0b00001000
local Recursed = 0b00010000
local Dirty = 0b00100000
local PendingComputed = 0b01000000
local PendingEffect = 0b10000000
local Propagated = 0b11100000

local SubscriberFlags = {
	Computed = Computed,
	Effect = Effect,
	Tracking = Tracking,
	Notified = Notified,
	Recursed = Recursed,
	Dirty = Dirty,
	PendingComputed = PendingComputed,
	PendingEffect = PendingEffect,
	Propagated = Propagated,
}

local function createReactiveSystem(
	updateComputed: (computed: Dependency & Subscriber) -> boolean,
	notifyEffect: (effect: Subscriber) -> boolean
)
	local notifyBuffer: { Subscriber? } = {}
	local notifyBufferIndex = 1
	local notifyBufferLength = 0

	local function linkNewDep(dep: Dependency, sub: Subscriber, nextDep: Link?, depsTail: Link?): Link
		local newLink: Link = {
			dep = dep,
			sub = sub,
			nextDep = nextDep,
		}
		if depsTail == nil then
			sub.deps = newLink
		else
			depsTail.nextDep = newLink
		end
		if dep.subs == nil then
			dep.subs = newLink
		else
			local oldTail = dep.subsTail
			newLink.prevSub = oldTail
			oldTail.nextSub = newLink
		end
		sub.depsTail = newLink
		dep.subsTail = newLink
		return newLink
	end

	local function isValidLink(checkLink: Link, sub: Subscriber): boolean
		local depsTail = sub.depsTail
		if depsTail then
			local link = sub.deps
			repeat
				if link == checkLink then
					return true
				end
				if link == depsTail then
					break
				end
				link = (link :: typeof(sub.deps)).nextDep
			until link == nil
		end
		return false
	end

	local function createLink(dep: Dependency, sub: Subscriber): Link?
		local currentDep = sub.depsTail
		if currentDep and currentDep.dep == dep then
			return
		end
		local nextDep = if currentDep then currentDep.nextDep else sub.deps
		if nextDep and nextDep.dep == dep then
			sub.depsTail = nextDep
			return
		end
		local depLastSub = dep.subsTail
		if depLastSub and depLastSub.sub == sub and isValidLink(depLastSub, sub) then
			return
		end
		return linkNewDep(dep, sub, nextDep, currentDep)
	end

	local function propagate(current: Link)
		local next = current.nextSub
		local branches: OneWayLink<Link?>?
		local branchDepth = 0
		local targetFlag = Dirty

		while true do
			local sub = current.sub
			local subFlags = sub.flags

			local shouldNotify = false

			if bit32.band(subFlags, bit32.bor(Tracking, Recursed, Propagated)) == 0 then
				sub.flags = bit32.bor(subFlags, targetFlag, Notified)
				shouldNotify = true
			elseif bit32.band(subFlags, Recursed) ~= 0 and bit32.band(subFlags, Tracking) == 0 then
				sub.flags = bit32.bor(bit32.band(subFlags, bit32.bnot(Recursed)), targetFlag, Notified)
				shouldNotify = true
			elseif bit32.band(subFlags, Propagated) == 0 and isValidLink(current, sub) then
				sub.flags = bit32.bor(subFlags, Recursed, targetFlag, Notified)
				shouldNotify = (sub :: Dependency).subs ~= nil
			end

			if shouldNotify then
				local subSubs = (sub :: Dependency).subs
				if subSubs then
					current = subSubs
					if subSubs.nextSub then
						branches = { target = next, linked = branches }
						branchDepth += 1
						next = current.nextSub
						targetFlag = PendingComputed
					else
						targetFlag = if bit32.band(subFlags, Effect) ~= 0 then PendingEffect else PendingComputed
					end
					continue
				end
				if bit32.band(subFlags, Effect) ~= 0 then
					notifyBufferLength += 1
					notifyBuffer[notifyBufferLength] = sub
				end
			elseif bit32.band(subFlags, bit32.bor(Tracking, targetFlag)) == 0 then
				sub.flags = bit32.bor(subFlags, targetFlag, Notified)
				if bit32.band(subFlags, bit32.bor(Effect, Notified)) == Effect then
					notifyBufferLength += 1
					notifyBuffer[notifyBufferLength] = sub
				end
			elseif
				bit32.band(subFlags, targetFlag) == 0
				and bit32.band(subFlags, Propagated) ~= 0
				and isValidLink(current, sub)
			then
				sub.flags = bit32.bor(subFlags, targetFlag)
			end

			current = next

			if current then
				next = current.nextSub
				targetFlag = if branchDepth > 0 then PendingComputed else Dirty
				continue
			end

			local shouldContinue = false

			while branchDepth > 0 do
				branchDepth -= 1
				current = (branches :: OneWayLink<Link?>).target
				branches = (branches :: OneWayLink<Link?>).linked
				if current then
					next = current.nextSub
					targetFlag = if branchDepth > 0 then PendingComputed else Dirty
					shouldContinue = true
					break
				end
			end

			if shouldContinue then
				continue
			end

			break
		end
	end

	local function startTracking(sub: Subscriber)
		sub.depsTail = nil
		sub.flags = bit32.bor(bit32.band(sub.flags, bit32.bnot(bit32.bor(Notified, Recursed, Propagated))), Tracking)
	end

	local function clearTracking(link: Link)
		repeat
			local dep = link.dep
			local nextDep = link.nextDep
			local nextSub = link.nextSub
			local prevSub = link.prevSub

			if nextSub then
				nextSub.prevSub = prevSub
			else
				dep.subsTail = prevSub
			end

			if prevSub then
				prevSub.nextSub = nextSub
			else
				dep.subs = nextSub
			end

			if dep.subs == nil and dep.deps then
				local depFlags = dep.flags
				if bit32.band(depFlags, Dirty) == 0 then
					dep.flags = bit32.bor(depFlags, Dirty)
				end
				local depDeps = dep.deps
				if depDeps then
					link = depDeps
					dep.depsTail.nextDep = nextDep
					dep.deps = nil
					dep.depsTail = nil
					continue
				end
			end
			link = nextDep
		until link == nil
	end

	local function endTracking(sub: Subscriber)
		local depsTail = sub.depsTail
		if depsTail then
			local nextDep = depsTail.nextDep
			if nextDep then
				clearTracking(nextDep)
				depsTail.nextDep = nil
			end
		elseif sub.deps then
			clearTracking(sub.deps)
			sub.deps = nil
		end
		sub.flags = bit32.band(sub.flags, bit32.bnot(Tracking))
	end

	local function shallowPropagate(link: Link)
		repeat
			local sub = link.sub
			local subFlags = sub.flags
			if bit32.band(subFlags, bit32.bor(PendingComputed, Dirty)) == PendingComputed then
				sub.flags = bit32.bor(subFlags, Dirty, Notified)
				if bit32.band(subFlags, bit32.bor(Effect, Notified)) == Effect then
					notifyBufferLength += 1
					notifyBuffer[notifyBufferLength] = sub
				end
			end
			link = link.nextSub
		until link == nil
	end

	local function checkDirty(current: Link): boolean
		local prevLinks: OneWayLink<Link>?
		local checkDepth = 0
		local dirty: boolean

		while true do
			dirty = false
			local dep = current.dep

			if bit32.band(current.sub.flags, Dirty) ~= 0 then
				dirty = true
			elseif dep.flags then
				local depFlags = dep.flags
				if bit32.band(depFlags, bit32.bor(Computed, Dirty)) == bit32.bor(Computed, Dirty) then
					if updateComputed(dep) then
						local subs = dep.subs
						if subs.nextSub then
							shallowPropagate(subs)
						end
						dirty = true
					end
				elseif
					bit32.band(depFlags, bit32.bor(Computed, PendingComputed)) == bit32.bor(Computed, PendingComputed)
				then
					if current.nextSub or current.prevSub then
						prevLinks = { target = current, linked = prevLinks }
					end
					current = dep.deps
					checkDepth += 1
					continue
				end
			end

			if not dirty and current.nextDep then
				current = current.nextDep
				continue
			end

			local shouldContinue = false

			while checkDepth > 0 do
				checkDepth -= 1
				local sub = current.sub :: Dependency & Subscriber
				local firstSub = sub.subs
				if dirty then
					if updateComputed(sub) then
						if firstSub.nextSub then
							current = (prevLinks :: OneWayLink<Link>).target
							prevLinks = (prevLinks :: OneWayLink<Link>).linked
							shallowPropagate(firstSub)
						else
							current = firstSub
						end
						continue
					end
				else
					sub.flags = bit32.band(sub.flags, bit32.bnot(PendingComputed))
				end
				if firstSub.nextSub then
					current = (prevLinks :: OneWayLink<Link>).target
					prevLinks = (prevLinks :: OneWayLink<Link>).linked
				else
					current = firstSub
				end
				if current.nextDep then
					current = current.nextDep
					shouldContinue = true
					break
				end

				dirty = false
			end

			if shouldContinue then
				continue
			end

			return dirty
		end
	end

	local function updateDirtyFlag(sub: Subscriber, flags: SubscriberFlags): boolean
		if checkDirty(sub.deps) then
			sub.flags = bit32.bor(flags, Dirty)
			return true
		else
			sub.flags = bit32.band(flags, bit32.bnot(PendingComputed))
			return false
		end
	end

	local function processComputedUpdate(computed: Dependency & Subscriber, flags: SubscriberFlags)
		if bit32.band(flags, Dirty) ~= 0 or checkDirty(computed.deps) then
			if updateComputed(computed) then
				local subs = computed.subs
				if subs then
					shallowPropagate(subs)
				end
			end
		else
			computed.flags = bit32.band(flags, bit32.bnot(PendingComputed))
		end
	end

	local function processPendingInnerEffects(sub: Subscriber, flags: SubscriberFlags)
		if bit32.band(flags, PendingEffect) ~= 0 then
			sub.flags = bit32.band(flags, bit32.bnot(PendingEffect))
			local link = sub.deps
			repeat
				local dep = (link :: typeof(sub.deps)).dep
				if dep.flags and bit32.band(dep.flags, Effect) ~= 0 and bit32.band(dep.flags, Propagated) ~= 0 then
					notifyEffect(dep)
				end
				link = (link :: typeof(sub.deps)).nextDep
			until link == nil
		end
	end

	local function processEffectNotifications()
		while notifyBufferIndex <= notifyBufferLength do
			local effect = notifyBuffer[notifyBufferIndex]
			notifyBuffer[notifyBufferIndex] = nil
			notifyBufferIndex += 1
			if not notifyEffect(effect) then
				effect.flags = bit32.band(effect.flags, bit32.bnot(Notified))
			end
		end
		notifyBufferIndex = 1
		notifyBufferLength = 0
	end

	return {
		link = createLink,
		propagate = propagate,
		updateDirtyFlag = updateDirtyFlag,
		startTracking = startTracking,
		endTracking = endTracking,
		processEffectNotifications = processEffectNotifications,
		processComputedUpdate = processComputedUpdate,
		processPendingInnerEffects = processPendingInnerEffects,
	}
end

return {
	createReactiveSystem = createReactiveSystem,
	SubscriberFlags = SubscriberFlags,
}


require("@self/computed.spec")
require("@self/effect.spec")
require("@self/effectScope.spec")
require("@self/untrack.spec")
require("@self/issue_48.spec")
require("@self/topology.spec")

print("All tests passed!")


--!native
--!optimize 2

-- stylua: ignore
local HEX_TO_BINARY = {
    ["0"] = "0000", ["1"] = "0001", ["2"] = "0010", ["3"] = "0011",
    ["4"] = "0100", ["5"] = "0101", ["6"] = "0110", ["7"] = "0111",
    ["8"] = "1000", ["9"] = "1001", ["a"] = "1010", ["b"] = "1011",
    ["c"] = "1100", ["d"] = "1101", ["e"] = "1110", ["f"] = "1111",
}

--- A constant representing the 64-bit representation of `0`.
local ZERO = vector.create(0, 0, 0)
--- A constant representating the 64-bit representation of `1`.
local ONE = vector.create(0, 0, 1)
--- A constant representating the 64-bit representation of `2`.
local TWO = vector.create(0, 0, 2)

--- A constant representing the maximum possible 64-bit value
--- (`18446744073709551615`, or `2 ^ 64 - 1`)
local MAX = vector.create(0x3fffff, 0xfffff, 0x3fffff)
--- A constant representing the maximum possible 32-bit value
--- (`4294967295`, or `2 ^ 32 - 1`).
local MAX_U32 = vector.create(0, 0x3ff, 0x3fffff)
--- A constant representing the maximum integer that is exactly representable
--- by a normal Luau number (`9007199254740992` or `2 ^ 53`).
local MAX_F64 = vector.create(0x800, 0, 0)
--- A constant representing the maximum integer that is exactly representable
--- by a 32-bit floating point value (`16777216`, or `2 ^ 24`).
local MAX_F32 = vector.create(0, 4, 0)

--[=[
	Normalizes a vector, trimming off any bits that would interfere with
	the library's functionality.
]=]
local function normalize(u64: vector): vector
	return vector.create(
		bit32.band(u64.x, 0b1111_1111_1111_1111_1111_11),
		bit32.band(u64.y, 0b1111_1111_1111_1111_1111),
		bit32.band(u64.z, 0b1111_1111_1111_1111_1111_11)
	)
end

--[=[
	Constructs a 64-bit integer from the passed `u32` values.

	The provided `most` value fills the upper 32 bits of the integer,
	and the provided `least` value fills the lower 32-bits.
]=]
local function from_pair(most: number, least: number): vector
	-- 32 - 22 = 10
	-- x = upper 22 of most
	-- y = lower 10 of most and upper 10 of least
	-- z = lower 22 of least
	return vector.create(
		bit32.rshift(most, 10),
		bit32.bor(bit32.extract(least, 22, 10), bit32.lshift(bit32.band(most, 0b1111_1111_11), 10)),
		bit32.band(least, 0b1111_1111_1111_1111_1111_11)
	)
end

--[=[
	Constructs a 64-bit integer from the passed `u32`.

	This number is truncated in a 32-bit integer, even if it is larger than
	the max value.
]=]
local function from_u32(u32: number): vector
	return vector.create(0, bit32.rshift(u32, 22), bit32.band(u32, 0b1111_1111_1111_1111_1111_11))
end

--[=[
	Constructs a 64-bit integer from the passed `f64`.
	Put more plainly, converts a normal Luau number to a 64-bit integer.

	The number is truncated into an integer first. Note that Luau numbers
	cannot accurately represent integers past `2 ^ 53`.
]=]
local function from_f64(f64: number): vector
	return from_pair(bit32.bor(f64 // 2 ^ 32), bit32.bor(f64 % 2 ^ 32))
end

--[=[
	Converts the provided `u64` into two 32-bit integers.

	This returns the most significant portion of the number first.
]=]
local function to_pair(u64: vector): (number, number)
	-- x = upper 22 of most
	-- y = lower 10 of most and upper 10 of least
	-- z = lower 22 of least
	local most = bit32.bor(bit32.lshift(u64.x, 10), bit32.rshift(u64.y, 10))
	local least = bit32.bor(bit32.lshift(u64.y, 22), u64.z)

	return most, least
end

--[=[
	Converts the provided `u64` into four 16-bit integers.

	This returns the most significant portion of the number first.
]=]
local function to_quartet(u64: vector): (number, number, number, number)
	-- 0000000000000000000000 00000000000000000000 0000000000000000000000
	-- 0000000000000000 000000 0000000000 0000000000 000000 0000000000000000
	-- 1111000111100010 1101001111000100 1000000010011010 1011110011011110
	local most, least = to_pair(u64)
	local q1, q2 = bit32.rshift(most, 16), bit32.band(most, 0xffff)
	local q3, q4 = bit32.rshift(least, 16), bit32.band(least, 0xffff)
	-- local q1 = bit32.rshift(u64.x, 6)
	-- local q2 = bit32.bor(bit32.lshift(u64.y, 16), bit32.rshift(u64.y, 10))
	-- local q3 = bit32.bor(bit32.band(u64.y, 0b1111_1111_11), bit32.rshift(u64.z, 16))
	-- local q4 = bit32.band(0b1111_1111_1111_1111)
	return q1, q2, q3, q4
end

--[=[
	Converts the provided `u64` to an `f64` (or put plainly: a normal Luau
	number).

	This function does not check for precision loss. `f64` values lose precision
	past `2 ^ 53`.
]=]
local function to_f64(u64: vector): number
	local most, least = to_pair(u64)
	return most * 2 ^ 32 + least
end

--[=[
	Returns whether the provided `u64` is zero or not.

	Equivalent to `u64 == from_u32(0)`.
]=]
local function is_zero(u64: vector): boolean
	return u64 == ZERO
end

--[=[
	Computes the bitwise AND of the two provide values.

	This does not accept a vararg like the `bit32` equivalent for performance
	reasons.
]=]
local function band(lhs: vector, rhs: vector): vector
	return vector.create(bit32.band(lhs.x, rhs.x), bit32.band(lhs.y, rhs.y), bit32.band(lhs.z, rhs.z))
end

--[=[
	Computes the bitwise OR of the two provide values.

	This does not accept a vararg like the `bit32` equivalent for performance
	reasons.
]=]
local function bor(lhs: vector, rhs: vector): vector
	return vector.create(bit32.bor(lhs.x, rhs.x), bit32.bor(lhs.y, rhs.y), bit32.bor(lhs.z, rhs.z))
end

--[=[
	Computes the bitwise XOR of the two provide values.

	This does not accept a vararg like the `bit32` equivalent for performance
	reasons.
]=]
local function bxor(lhs: vector, rhs: vector): vector
	return vector.create(bit32.bxor(lhs.x, rhs.x), bit32.bxor(lhs.y, rhs.y), bit32.bxor(lhs.z, rhs.z))
end

--[=[
	Computes the bitwise negation of the provided value.
]=]
local function bnot(u64: vector): vector
	return vector.create(
		bit32.band(bit32.bnot(u64.x), 0b1111_1111_1111_1111_1111_11),
		bit32.band(bit32.bnot(u64.y), 0b1111_1111_1111_1111_1111),
		bit32.band(bit32.bnot(u64.z), 0b1111_1111_1111_1111_1111_11)
	)
end

--[=[
	Shifts the provided `u64` logically left by `n` bits.

	This function will error if `n` is negative.
]=]
local function lshift(u64: vector, n: number): vector
	if n >= 64 then
		return vector.zero
	elseif n == 0 then
		return u64
	end
	local most, least = to_pair(u64)
	if n < 32 then
		local remainder = 32 - n
		local ret = from_pair(bit32.replace(bit32.rshift(least, remainder), most, n, remainder), bit32.lshift(least, n))
		return ret
	else
		local ret = from_pair(bit32.lshift(least, n - 32), 0)
		return ret
	end
end

--[=[
	Shifts the provided `u64` logically right by `n` bits.

	This function will error if `n` is negative.
]=]
local function rshift(u64: vector, n: number): vector
	if n == 0 then
		return u64
	end
	local most, least = to_pair(u64)
	if n < 32 then
		local ret = from_pair(bit32.rshift(most, n), bit32.replace(bit32.rshift(least, n), most, 32 - n, n))
		return ret
	else
		local ret = from_pair(0, bit32.rshift(most, n - 32))
		return ret
	end
end

--[=[
	Shifts the provided `u64` arithmetically right by `n` bits. Since these
	numbers are unsigned, this effectively just copies the most significant
	bit into the empty space rather than filling them with zeros.

	This function will error if `n` is negative.
]=]
local function arshift(u64: vector, n: number): vector
	-- If the sign bit is set, we do arshift
	if bit32.btest(u64.x, 0x20_0000) then
		if n >= 64 then
			return MAX
		else
			return bor(rshift(u64, n), bnot(rshift(MAX, n)))
		end
	else
		-- Otherwise it's just an rshift
		return rshift(u64, n)
	end
end

--[=[
	Rotates the bits of the provided `u64` left by `n` bits.

	If `n` is negative, this is equivalent to `rrotate`. Otherwise, if `n` is
	greater than 64-bits, it will wrap around.
]=]
local function lrotate(u64: vector, n: number): vector
	n = bit32.band(n, 63)
	local lshifted = lshift(u64, n)
	local rshifted = rshift(u64, 64 - n)
	return bor(lshifted, rshifted)
end

--[=[
	Rotates the bits of the provided `u64` right by `n` bits.

	If `n` is negative, this is equivalent to `lrotate`. Otherwise, if `n` is
	greater than 64-bits, it will wrap around.
]=]
local function rrotate(u64: vector, n: number): vector
	n = bit32.band(n, 63)
	local lshifted = lshift(u64, 64 - n)
	local rshifted = rshift(u64, n)
	return bor(lshifted, rshifted)
end

--[=[
	Returns the number of consecutive zero bits in the provided `u64` starting
	from the left-most (most significant) bit.
]=]
local function countlz(u64: vector): number
	local most, least = to_pair(u64)
	if most == 0 then
		return bit32.countlz(least) + 32
	else
		return bit32.countlz(most)
	end
end

--[=[
	Returns the number of consecutive zero bits in the provided `u64` starting
	from the right-most (least significant) bit.
]=]
local function countrz(u64: vector): number
	local most, least = to_pair(u64)
	if least == 0 then
		return bit32.countrz(most) + 32
	else
		return bit32.countrz(least)
	end
end

--[=[
	Returns a boolean describing whether the bitwise AND of `lhs` and
	`rhs` are different than zero.

	This does not accept a vararg like the `bit32` equivalent for performance
	reasons.
]=]
local function btest(lhs: vector, rhs: vector): boolean
	return band(lhs, rhs) ~= ZERO
end

--[=[
	Returns the provided `u64` with the order of bytes swapped.
]=]
local function byteswap(u64: vector): vector
	local most, least = to_pair(u64)
	return from_pair(bit32.byteswap(least), bit32.byteswap(most))
end

--[=[
	Returns whether `lhs` is greater than `rhs`.
]=]
local function gt(lhs: vector, rhs: vector): boolean
	local l_most, l_least = to_pair(lhs)
	local r_most, r_least = to_pair(rhs)

	if l_most == r_most then
		return l_least > r_least
	else
		return l_most > r_most
	end
end

--[=[
	Returns whether `lhs` is greater than or equal to `rhs`.
]=]
local function gt_equal(lhs: vector, rhs: vector): boolean
	return lhs == rhs or gt(lhs, rhs)
end

--[=[
	Returns whether `lhs` is less than `rhs`.
]=]
local function lt(lhs: vector, rhs: vector): boolean
	local l_most, l_least = to_pair(lhs)
	local r_most, r_least = to_pair(rhs)

	if l_most == r_most then
		return l_least < r_least
	else
		return l_most < r_most
	end
end

--[=[
	Returns whether `lhs` is less than or equal to `rhs`.
]=]
local function lt_equal(lhs: vector, rhs: vector): boolean
	return lhs == rhs or lt(lhs, rhs)
end

--[=[
	Calculates the quotient of the two provided values and returns it,
	along with the remainder from that operation.

	This function operates on a single 64-bit integer and a 32-bit integer.
	For dividing two 64-bit integers, see `div`.

	Equivalent to `//` for normal integers.
	This function will error if `divisor` is 0.
]=]
local function div_32(dividend: vector, divisor: number): (vector, number)
	if divisor == 0 then
		error("cannot divide integers by zero")
	end
	local hi, lo = to_pair(dividend)
	local q_hi, q_lo = 0, 0
	local rem = 0

	for i = 31, 0, -1 do
		rem = bit32.bor(bit32.lshift(rem, 1), bit32.band(bit32.rshift(hi, i), 1))

		if rem >= divisor then
			rem -= divisor
			q_hi = bit32.bor(q_hi, bit32.lshift(1, i))
		end
	end
	for i = 31, 0, -1 do
		rem = bit32.bor(bit32.lshift(rem, 1), bit32.band(bit32.rshift(lo, i), 1))

		if rem >= divisor then
			rem -= divisor
			q_lo = bit32.bor(q_lo, bit32.lshift(1, i))
		end
	end

	return from_pair(q_hi, q_lo), rem
end

--[=[
	Calculates the sum of the two provided values. Equivalent to `+` for
	normal integers.

	If the sum is equal to or greater than `2 ^ 64`, the returned value will
	overflow rather than expanding beyond 64 bits.
]=]
local function add(augend: vector, addend: vector): vector
	local prime = augend + addend

	prime += vector.create(0, if prime.z > (2 ^ 22 - 1) then 1 else 0, 0)
	prime += vector.create(if prime.y > (2 ^ 20 - 1) then 1 else 0, 0, 0)

	return normalize(prime)
end

--[=[
	Calculates the difference of the two provided values. Equivalent to `-` for
	normal integers.

	If the difference is less than `0`, the returned value will overflow rather
	than going negative.
]=]
local function sub(minuend: vector, subtrahend: vector): vector
	local prime = minuend - subtrahend

	local adjust_z = prime.z
	local adjust_y = prime.y
	local adjust_x = prime.x

	local borrow_z = prime.z < 0
	if borrow_z then
		adjust_z += 2 ^ 22
	end

	if borrow_z then
		adjust_y -= 1
	end
	local borrow_y = adjust_y < 0
	if borrow_y then
		adjust_y += 2 ^ 20
	end

	if borrow_y then
		adjust_x -= 1
	end
	local borrow_x = adjust_x < 0
	if borrow_x then
		adjust_x += 2 ^ 22
	end

	return vector.create(adjust_x, adjust_y, adjust_z)
end

--[=[
	Calculates the product of the two provided values. Equivalent to `*` for
	normal integers.

	If the product is greater than or equal to `2 ^ 64`, the returned value will
	overflow rather than expanding beyond 64 bits.
]=]
local function mult(multiplier: vector, multiplicand: vector): vector
	-- We represent 64-bit numbers as two 32-bit ones.
	-- Multiplying them is:
	-- (A + B) * (C + D) = (A * C) + (A * D) + (B * C) + (B * D)
	--
	-- However, multiplying two 32-bit numbers might overflow. So, we need to
	-- use 16-bit numbers. This turns our math into this:
	-- (A + B + C + D) * (E + F + G + H) =
	-- 		(A * E) + (A * F) + (A * G) + (A * H) +
	-- 		(B * E) + (B * F) + (B * G) + (B * H) +
	-- 		(C * E) + (C * F) + (C * G) + (C * H) +
	-- 		(D * E) + (D * F) + (D * G) + (D * H)
	--
	-- We can skip (A * E), (A * F), (A * G), (B * E), (B * F), and (C * F)
	-- because they don't exist within the bounds of the final product.
	-- Since the numbers are built as A * 2 ^ 48 + B * 2 ^ 32 + C * 2 ^ 16 + D
	-- you end up with e.g. (A * 2 ^ 48 * E * 2 ^ 48) which is A * E * 2^96.
	-- Otherwise... Here we go.

	local a, b, c, d = to_quartet(multiplier)
	local e, f, g, h = to_quartet(multiplicand)

	local product_4 = d * h
	local product_3 = bit32.rshift(product_4, 16) + c * h
	local product_2 = bit32.rshift(product_3, 16)
	product_3 = bit32.band(product_3, 0xFFFF) + d * g

	product_2 += bit32.rshift(product_3, 16) + b * h
	local product_1 = bit32.rshift(product_2, 16)

	product_2 = bit32.band(product_2, 0xFFFF) + c * g
	product_1 += bit32.rshift(product_2, 16)
	product_2 = bit32.band(product_2, 0xFFFF) + d * f
	product_1 += bit32.rshift(product_2, 16) + a * h + b * g + c * f + d * e

	-- We skip truncating any of the products the last time since they'll never
	-- overflow and bit32 will truncate them for us.

	return from_pair(bit32.replace(product_2, product_1, 16, 16), bit32.replace(product_4, product_3, 16, 16))
end

--[=[
	Calculates the quotient of the two provided values and returns it, along
	with the remainder.

	Equivalent to `//` and `%` for normal integers.
	This function will error if `divisor` is `0`.
]=]
local function div(dividend: vector, divisor: vector): (vector, vector)
	if is_zero(divisor) then
		error("cannot divide integers by zero")
	elseif countlz(dividend) >= 11 and countlz(divisor) >= 11 then
		local l_real = to_f64(dividend)
		local r_real = to_f64(divisor)
		return from_f64(l_real // r_real), from_f64(l_real % r_real)
	end

	local quotient = ZERO
	local remainder = dividend

	-- Doing extra iterations of this is very bad!
	for i = math.abs(countlz(dividend) - countlz(divisor)), 0, -1 do
		local shifted_divisor = lshift(divisor, i)

		if gt_equal(remainder, shifted_divisor) then
			remainder = sub(remainder, shifted_divisor)
			quotient = bor(quotient, lshift(ONE, i))
		end
	end

	return quotient, remainder
end

--[=[
	Calculates the result of `base` raised to `power`. Equivalent to `^` for
	normal integers. `power` is interpreted as a 32-bit integer.

	If the result is greater than or equal to `2 ^ 64`, the returned value will
	overflow rather than expanding beyond 64 bits.

	Additionally, `0 ^ 0` is treated as being `1`.
]=]
local function pow(base: vector, power: number): vector
	-- TODO: Is there an efficient solution that isn't recursion?
	if power == 0 then
		return ONE
	elseif power % 2 == 1 then
		return mult(base, pow(base, power - 1))
	else
		local temp = pow(base, power / 2)
		return mult(temp, temp)
	end
end

--[=[
	Reads a `u64` from the provided buffer. If offset is provided, reads from
	that spot in the buffer. Otherwise, reads from the beginning.

	This function reads a little-endian value from the buffer.
]=]
local function from_buffer(buf: buffer, offset: number?): vector
	offset = offset or 0
	local least = buffer.readu32(buf, offset :: number)
	local most = buffer.readu32(buf, offset :: number + 4)

	return from_pair(most, least)
end

--[=[
	Reads a `u64` from the provided string. If offset is provided, reads from
	that point in the string. Otherwise starts at the beginning.

	This function reads a little-endian value from the string.
]=]
local function from_byte_string(str: string, offset: number?): vector
	offset = offset or 1
	-- This is deliberately backwards because we expect a little endian input
	local d, c, b, a, h, g, f, e = string.byte(str, offset, offset :: number + 7)
	local least = bit32.bor(bit32.lshift(bit32.replace(b, a, 8, 8), 16), bit32.replace(d, c, 8, 8))
	local most = bit32.bor(bit32.lshift(bit32.replace(f, e, 8, 8), 16), bit32.replace(h, g, 8, 8))

	return from_pair(most, least)
end

--[=[
	Converts the provided `u64` to a buffer.

	The integer is written as a little-endian value.
]=]
local function to_buffer(u64: vector): buffer
	local most, least = to_pair(u64)
	local buf = buffer.create(8)
	-- We use a little endian format because it doesn't matter and most
	-- systems we run on are little endian so it'll be nicer for them.
	buffer.writeu32(buf, 0, least)
	buffer.writeu32(buf, 4, most)
	return buf
end

--[=[
	Writes the provided `u64` to the provided buffer. The value is written
	to the buffer at the provided offset.

	The integer is written as a little-endian value.
]=]
local function write_to_buffer(b: buffer, u64: vector, offset: number)
	local most, least = to_pair(u64)
	buffer.writeu32(b, offset, least)
	buffer.writeu32(b, offset + 4, most)
end

--[=[
	Converts the provided `u64` to string of binary data.

	The integer is written as if it were little-endian.
]=]
local function to_byte_string(u64: vector): string
	local most, least = to_pair(u64)
	-- Previous implementation of this used `%c` but it turns out that
	-- `string.format("%c", 0)` results in a zero length string!
	return string.char(
		bit32.band(least, 0xFF),
		bit32.extract(least, 8, 8),
		bit32.extract(least, 16, 8),
		bit32.rshift(least, 24),
		bit32.band(most, 0xFF),
		bit32.extract(most, 8, 8),
		bit32.extract(most, 16, 8),
		bit32.rshift(most, 24)
	)
end

--[=[
	Converts the provided `u64` to a string of 16 hexadecimal digits.

	The returned string will always be 16 bytes and the number is formatted as
	if it were big-endian. It also always uses lowercase letters.
]=]
local function to_hex_string(u64: vector): string
	local most, least = to_pair(u64)
	return string.format("%08x%08x", most, least)
end

--[=[
	Converts the provided `u64` to a string of 64 binary digits.

	The returned string will always be 64 bytes and the number is formatted as
	if it were big-endian.
]=]
local function to_bin_string(u64: vector): string
	-- Manually implemtning a binary conversion is not any faster, and
	-- this is a lot easier.
	return (string.gsub(to_hex_string(u64), "(.)", HEX_TO_BINARY))
end

--[=[
	Returns the provided value parsed as a 64-bit integer. Expects the provided
	string to contain only the digits 0-9 and will error if it does not.

	Leading zeros are accepted and parsed appropriately.

	Due to the implementation, the resulting integer will wrap around if it is
	too large to fit in a 64-bit integer rather than erroring.
]=]
local function from_dec_string(str: string): vector
	if string.find(str, "%D") then
		local pos, digit = string.match(str, "()(%D)")
		local hex = string.format("0x%02x", string.byte(digit :: any))
		error(`unexpected character '{digit}' ({hex}) at position {pos} while parsing decimal string`)
	end
	local low, high = 0, 0
	for i = 1, #str do
		-- The ascii code for zero is `48`.
		local digit = string.byte(str, i, i) - 48

		low *= 10
		high = high * 10 + low // 2 ^ 32
		low = bit32.bor(low, 0) + digit

		if low >= 2 ^ 32 then
			low -= 2 ^ 32
			high += 1
		end
	end

	return from_pair(high, low)
end

--[=[
	Returns the provided value as a string of decimal digits.

	Equivalent to formatting normal numbers with `%u` or
	calling `tostring` on them.
]=]
local function to_dec_string(u64: vector): string
	if countlz(u64) >= 11 then
		return tostring(to_f64(u64))
	else
		local q, rem = div_32(u64, 1e9)
		local q_hi, q_lo = to_pair(q)
		if q_hi == 0 and q_lo < 1e9 then
			return string.format("%u%09u", q_lo, rem)
		else
			return string.format("%s%09u", to_dec_string(q), rem)
		end
	end
end

-- TODO: replace and extract
-- TODO: vararg versions of band, bor, bxor

return {
	--- A constant representing the 64-bit representation of `0`.
	ZERO = ZERO,
	--- A constant representating the 64-bit representation of `1`.
	ONE = ONE,
	--- A constant representating the 64-bit representation of `2`.
	TWO = TWO,
	--- A constant representing the maximum possible 64-bit value
	--- (`18446744073709551615`, or `2 ^ 64 - 1`)
	MAX = MAX,
	--- A constant representing the maximum possible 32-bit value
	--- (`4294967295`, or `2 ^ 32 - 1`).
	MAX_U32 = MAX_U32,
	--- A constant representing the maximum integer that is exactly representable
	--- by a normal Luau number (`9007199254740992` or `2 ^ 53`).
	MAX_F64 = MAX_F64,
	--- A constant representing the maximum integer that is exactly representable
	--- by a 32-bit floating point value (`16777216`, or `2 ^ 24`).
	MAX_F32 = MAX_F32,

	from_pair = from_pair,
	from_u32 = from_u32,
	from_f64 = from_f64,

	to_pair = to_pair,
	to_quartet = to_quartet,
	to_f64 = to_f64,

	lt = lt,
	lt_equal = lt_equal,
	gt = gt,
	gt_equal = gt_equal,
	is_zero = is_zero,

	band = band,
	bor = bor,
	bxor = bxor,
	btest = btest,

	bnot = bnot,
	byteswap = byteswap,
	countlz = countlz,
	countrz = countrz,

	rshift = rshift,
	arshift = arshift,
	rrotate = rrotate,
	lshift = lshift,
	lrotate = lrotate,

	add = add,
	sub = sub,
	mult = mult,
	div = div,
	pow = pow,

	to_byte_string = to_byte_string,
	from_byte_string = from_byte_string,

	to_buffer = to_buffer,
	write_to_buffer = write_to_buffer,
	from_buffer = from_buffer,

	to_hex_string = to_hex_string,
	to_bin_string = to_bin_string,

	to_dec_string = to_dec_string,
	from_dec_string = from_dec_string,
}


local fs = require("@lune/fs")
local serde = require("@lune/serde")

local wally = serde.decode("toml", fs.readFile("wally.toml"))
local npm = serde.decode("json", fs.readFile("package.json"))

local wally_name = wally.package.name
local npm_name = string.match(npm.name, "^@(%w+/%w+)$")
assert(npm_name == wally_name, "npm name does not match wally name")

local wally_version = wally.package.version
local npm_version = npm.version
assert(npm_version == wally_version, "npm version does not match wally version")

local wally_description = wally.package.description
local npm_description = npm.description
assert(npm_description == wally_description, "npm description does not match wally description")

local wally_license = wally.package.license
local npm_license = npm.license
assert(npm_license == wally_license, "npm license does not match wally license")

local function verify_files(files: { string }, manifest_name: string)
	assert(files[1] == "init.luau", `init.luau was not first in the files list of {manifest_name}`)
	assert(files[2] == "README.md", `README.md was not second in the files list of {manifest_name}`)
	assert(files[3] == "CHANGELOG.md", `CHANGELOG.md was not third in the file list of {manifest_name}`)
	assert(files[4] == "LICENSE", `LICENSE was not third in the files list of {manifest_name}`)
	assert(files[5] == manifest_name, `{manifest_name} was not fourth in the files list of {manifest_name}`)

	local count = 0
	for _ in files do
		count += 1
	end
	assert(count == 5, `{manifest_name} had {count} files in its list instead of 5`)
end

verify_files(wally.package.include, "wally.toml")
verify_files(npm.files, "package.json")


local t1 = os.clock()
require("./tests/basic")
local d1 = os.clock() - t1
print(string.format("Basic competency tests finished in %.3fs", d1))

local t2 = os.clock()
require("./tests/bitwise/standard")
require("./tests/bitwise/rshift")
require("./tests/bitwise/arshift")
require("./tests/bitwise/rrotate")
require("./tests/bitwise/lshift")
require("./tests/bitwise/lrotate")
local d2 = os.clock() - t2
print(string.format("Bitwise tests finished in %.3fs", d2))

local t3 = os.clock()
require("./tests/arithmetic/add")
require("./tests/arithmetic/sub")
require("./tests/arithmetic/mult")
require("./tests/arithmetic/div")
require("./tests/arithmetic/pow")
local d3 = os.clock() - t3
print(string.format("Arithmetic tests finished in %.3fs", d3))


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

local max = int64.from_pair(0xFFFFFFFF, 0xFFFFFFFF)
local zero = int64.from_u32(0)
local one = int64.from_u32(1)

local added_max = int64.add(max, max)
assert(int64.to_hex_string(added_max) == "fffffffffffffffe", "max value + max value was not correct")
assert(added_max.x == 0b1111111111111111111111, "x component of max value + max value was not correct")
assert(added_max.y == 0b11111111111111111111, "y component of max value + max value was not correct")
assert(added_max.z == 0b1111111111111111111110, "z component of max value + max value was not correct")

local overflow = int64.add(max, int64.from_pair(0x00000400, 0x00400001))
assert(int64.to_hex_string(overflow) == "0000040000400000", "intentional overflow of components is not correct")
assert(overflow.x == 0b0000000000000000000001, "x component of intentional overflow was not correct")
assert(overflow.y == 0b00000000000000000001, "y component of intentional overflow was not correct")
assert(overflow.z == 0b0000000000000000000000, "z component of intentional overflow was not correct")

local zero_max = int64.add(max, zero)
assert(int64.to_hex_string(zero_max) == "ffffffffffffffff", "adding 0 changed something")
assert(zero_max.x == 0b1111111111111111111111, "x component of adding zero 0 to max was not correct")
assert(zero_max.y == 0b11111111111111111111, "y component of adding zero 0 to max was not correct")
assert(zero_max.z == 0b1111111111111111111111, "z component of adding zero 0 to max was not correct")

local zero_zero = int64.add(zero, zero)
assert(int64.to_hex_string(zero_zero) == "0000000000000000", "adding 0 to 0 was wrong")
assert(zero_zero == vector.zero, "actual value of 0 + 0 was wrong")

local large = vector.create(2 ^ 21, 2 ^ 9, 2 ^ 21)
local added_large = int64.add(large, large)
assert(int64.to_hex_string(added_large) == "0000000100400000", "large but not overflowing addition does not work")
assert(added_large.x == 0b0000000000000000000000, "x component of large but not overflowing addition was not correct")
assert(added_large.y == 0b00000000010000000001, "y component of large but not overflowing addition was not correct")
assert(added_large.z == 0b0000000000000000000000, "z component of large but not overflowing addition was not correct")

for lhs = 1, 2 ^ 8 do
	for rhs = 1, 2 ^ 8 do
		local real_sum = lhs + rhs
		local fake_sum = int64.add(int64.from_u32(lhs), int64.from_u32(rhs))
		if int64.to_f64(fake_sum) ~= real_sum then
			error(`{lhs} + {rhs} did not equal {real_sum} (equaled {int64.to_f64(fake_sum)})`)
		end
	end
end

local exact_overflow = int64.add(max, one)
assert(int64.to_hex_string(exact_overflow) == "0000000000000000", "0xffffffffffffffff + 0x1 was not correct")
assert(exact_overflow.x == 0x000000, "x component of 0xffffffffffffffff + 0x1 was wrong")
assert(exact_overflow.y == 0x00000, "x component of 0xffffffffffffffff + 0x1 was wrong")
assert(exact_overflow.z == 0x000000, "x component of 0xffffffffffffffff + 0x1 was wrong")

return {}


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

local test_1_a = int64.mult(int64.from_pair(0, 3), int64.from_pair(0, 5))
assert(int64.to_hex_string(test_1_a) == "000000000000000f", "test 1 a (3 * 5) failed")
local test_1_b = int64.mult(int64.from_pair(0, 5), int64.from_pair(0, 3))
assert(int64.to_hex_string(test_1_b) == "000000000000000f", "test 1 b (5 * 3) failed")
assert(test_1_a == test_1_b, "test 1 was not associative")
assert(test_1_a.x == 0b0000000000000000000000, "test 1 x component was not correct")
assert(test_1_a.y == 0b00000000000000000000, "test 1 y component was not correct")
assert(test_1_a.z == 0b0000000000000000001111, "test 1 z component was not correct")

local test_2_a = int64.mult(int64.from_pair(0, 7), int64.from_pair(0, 11))
assert(int64.to_hex_string(test_2_a) == "000000000000004d", "test 2 a (7 * 11) failed")
local test_2_b = int64.mult(int64.from_pair(0, 11), int64.from_pair(0, 7))
assert(int64.to_hex_string(test_2_b) == "000000000000004d", "test 2 b (11 * 7) failed")
assert(test_2_a == test_2_b, "test 2 was not associative")
assert(test_2_a.x == 0b0000000000000000000000, "test 2 x component was not correct")
assert(test_2_a.y == 0b00000000000000000000, "test 2 y component was not correct")
assert(test_2_a.z == 0b0000000000000001001101, "test 2 z component was not correct")

local test_3_a = int64.mult(vector.zero, vector.zero)
assert(test_3_a == vector.zero, "test 3 a (0 * 0) failed")
-- No associative test
assert(test_3_a.x == 0b0000000000000000000000, "test 3 x component was not correct")
assert(test_3_a.y == 0b00000000000000000000, "test 3 y component was not correct")
assert(test_3_a.z == 0b0000000000000000000000, "test 3 z component was not correct")

local test_4_a = int64.mult(int64.from_pair(0, 5), vector.zero)
assert(test_4_a == vector.zero, "test 4 a (5 * 0) failed")
local test_4_b = int64.mult(vector.zero, int64.from_pair(0, 5))
assert(test_4_b == vector.zero, "test 4 b (0 * 5) failed")
assert(test_4_a == test_4_b, "test 4 was not associative")
assert(test_4_a.x == 0b0000000000000000000000, "test 4 x component was not correct")
assert(test_4_a.y == 0b00000000000000000000, "test 4 y component was not correct")
assert(test_4_a.z == 0b000000000000000000000, "test 4 z component was not correct")

local test_5_a = int64.mult(int64.from_pair(0xffffffff, 0xffffffff), vector.zero)
assert(test_5_a == vector.zero, "test 5 a (0xffffffffffffffff * 0) failed")
local test_5_b = int64.mult(vector.zero, int64.from_pair(0xffffffff, 0xffffffff))
assert(test_5_b == vector.zero, "test 5 a (0 * 0xffffffffffffffff) failed")
assert(test_5_a == test_5_b, "test 5 was not associative")
assert(test_5_a.x == 0b0000000000000000000000, "test 5 x component was not correct")
assert(test_5_a.y == 0b00000000000000000000, "test 5 y component was not correct")
assert(test_5_a.z == 0b000000000000000000000, "test 5 z component was not correct")

local test_6_a = int64.mult(int64.from_pair(0, 1), int64.from_pair(0, 1))
assert(int64.to_hex_string(test_6_a) == "0000000000000001", "test 6 a (1 * 1) failed")
-- No associative test
assert(test_6_a.x == 0b0000000000000000000000, "test 6 x component was not correct")
assert(test_6_a.y == 0b00000000000000000000, "test 6 y component was not correct")
assert(test_6_a.z == 0b000000000000000000001, "test 6 z component was not correct")

local test_7_a = int64.mult(int64.from_pair(0xabcdef98, 0x76543210), int64.from_pair(0, 1))
assert(int64.to_hex_string(test_7_a) == "abcdef9876543210", "test 7a (0xabcdef9876543210 * 1) failed")
local test_7_b = int64.mult(int64.from_pair(0, 1), int64.from_pair(0xabcdef98, 0x76543210))
assert(int64.to_hex_string(test_7_b) == "abcdef9876543210", "test 7b (1 * 0xabcdef9876543210) failed")
assert(test_7_a == test_7_b, "test 7 was not associative")
assert(test_7_a.x == 0b1010101111001101111011, "test 7 x component was not correct")
assert(test_7_a.y == 0b11100110000111011001, "test 7 y component was not correct")
assert(test_7_a.z == 0b0101000011001000010000, "test 7 z component was not correct")

local test_8_a = int64.mult(int64.from_pair(0xffffffff, 0xffffffff), int64.from_pair(0xffffffff, 0xffffffff))
assert(int64.to_hex_string(test_8_a) == "0000000000000001", "test 8 a (0xffffffffffffffff * 0xffffffffffffffff) failed")
-- No associative test

--stylua: ignore
local pow_table = {
	"0000000000000002", "0000000000000004", "0000000000000008", "0000000000000010",
	"0000000000000020", "0000000000000040", "0000000000000080", "0000000000000100",
	"0000000000000200", "0000000000000400", "0000000000000800", "0000000000001000",
	"0000000000002000", "0000000000004000", "0000000000008000", "0000000000010000",
	"0000000000020000", "0000000000040000", "0000000000080000", "0000000000100000",
	"0000000000200000", "0000000000400000", "0000000000800000", "0000000001000000",
	"0000000002000000", "0000000004000000", "0000000008000000", "0000000010000000",
	"0000000020000000", "0000000040000000", "0000000080000000", "0000000100000000",
	"0000000200000000", "0000000400000000", "0000000800000000", "0000001000000000",
	"0000002000000000", "0000004000000000", "0000008000000000", "0000010000000000",
	"0000020000000000", "0000040000000000", "0000080000000000", "0000100000000000",
	"0000200000000000", "0000400000000000", "0000800000000000", "0001000000000000",
	"0002000000000000", "0004000000000000", "0008000000000000", "0010000000000000",
	"0020000000000000", "0040000000000000", "0080000000000000", "0100000000000000",
	"0200000000000000", "0400000000000000", "0800000000000000", "1000000000000000",
	"2000000000000000", "4000000000000000", "8000000000000000",
}

assert(#pow_table == 63, "pow table was wrong length")

local last = int64.from_pair(0, 1)

for i, expected in pow_table do
	last = int64.mult(last, int64.from_pair(0, 2))
	if int64.to_hex_string(last) ~= expected then
		local truncated = string.match(expected, "^0*(%x+)$") or expected
		error(`mult by 2 cycle {i} failed (0x{truncated} * 2 was wrong)`)
	end
end

return {}


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

local max = int64.from_pair(0xFFFFFFFF, 0xFFFFFFFF)
local zero = int64.from_u32(0)
local one = int64.from_u32(1)

local subbed_max = int64.sub(max, max)
assert(int64.to_hex_string(subbed_max) == "0000000000000000", "max value - max value was not correct")
assert(subbed_max.x == 0b0000000000000000000000, "x component of max value - max value was not correct")
assert(subbed_max.y == 0b00000000000000000000, "y component of max value - max value was not correct")
assert(subbed_max.z == 0b0000000000000000000000, "z component of max value - max value was not correct")

local underflow = int64.sub(int64.from_pair(0x00000400, 0x00400001), max)
assert(int64.to_hex_string(underflow) == "0000040000400002", "intentional underflow of components is not correct")
assert(underflow.x == 0b0000000000000000000001, "x component of intentional underflow was not correct")
assert(underflow.y == 0b00000000000000000001, "y component of intentional underflow was not correct")
assert(underflow.z == 0b0000000000000000000010, "z component of intentional underflow was not correct")

local zero_min = int64.sub(zero, max)
assert(int64.to_hex_string(zero_min) == "0000000000000001", "subtracting max from 0 was wrong")
assert(zero_min.x == 0b0000000000000000000000, "x component of subtracting max from 0 was not correct")
assert(zero_min.y == 0b00000000000000000000, "y component of subtracting max from 0 was not correct")
assert(zero_min.z == 0b0000000000000000000001, "z component of subtracting max from 0 was not correct")

local zero_max = int64.sub(max, zero)
assert(int64.to_hex_string(zero_max) == "ffffffffffffffff", "subtracting 0 changed something")
assert(zero_max.x == 0b1111111111111111111111, "x component of subtracting 0 from max was not correct")
assert(zero_max.y == 0b11111111111111111111, "y component of subtracting 0 from max was not correct")
assert(zero_max.z == 0b1111111111111111111111, "z component of subtracting 0 from max was not correct")

local zero_zero = int64.sub(zero, zero)
assert(int64.to_hex_string(zero_zero) == "0000000000000000", "subtracting 0 to 0 was wrong")
assert(zero_zero.x == 0b0000000000000000000000, "x component of subtracting 0 from 0 was not correct")
assert(zero_zero.y == 0b00000000000000000000, "y component of subtracting 0 from 0 was not correct")
assert(zero_zero.z == 0b0000000000000000000000, "z component of subtracting 0 from 0 was not correct")

local large = vector.create(2 ^ 21 - 1, 2 ^ 20 - 1, 2 ^ 21 - 1)
local small = vector.create(10, 20, 30)
local subbed_large = int64.sub(large, small)
assert(int64.to_hex_string(subbed_large) == "7fffd7fffadfffe1", "large - small does not work")
assert(subbed_large.x == 0b0111111111111111110101, "x component of large - small was not correct")
assert(subbed_large.y == 0b11111111111111101011, "y component of large - small was not correct")
assert(subbed_large.z == 0b0111111111111111100001, "z component of large - small was not correct")

for lhs = 1, 2 ^ 8 do
	for rhs = 1, 2 ^ 8 do
		-- Sadly, real subtraction supports negatives to we have to improvise.
		local real_diff = string.format("%016x", lhs - rhs)
		local fake_diff = int64.sub(int64.from_u32(lhs), int64.from_u32(rhs))
		if int64.to_hex_string(fake_diff) ~= real_diff then
			error(`{lhs} - {rhs} did not equal {real_diff} (equaled {int64.to_hex_string(fake_diff)})`)
		end
	end
end

local exact_overflow = int64.sub(zero, one)
assert(int64.to_hex_string(exact_overflow) == "ffffffffffffffff", "0 - 1 was not correct")
assert(exact_overflow.x == 0x3fffff, "x component of 0 - 1 was wrong")
assert(exact_overflow.y == 0xfffff, "y component of 0 - 1 was wrong")
assert(exact_overflow.z == 0x3fffff, "z component of 0 - 1 was wrong")

return {}