Lompat ke isi

Modul:relation

Ḍâri Wikikamus

Dokumentasi untuk modul ini dapat dibuat di Modul:relation/doc

local export = {}

local math_module = "Module:math"

local byte = string.byte
local less_than -- defined below
local match = string.match
local pcall = pcall
local rawequal = rawequal
local select = select
local sub = string.sub
local type = type

local function sign(...)
	sign = require(math_module).sign
	return sign(...)
end

--[==[
The equality operator {==} as a function.]==]
function export.equal(a, b)
	return a == b
end
export.eq = export.equal

--[==[
The inequality operator {~=} as a function.]==]
function export.not_equal(a, b)
	return a ~= b
end
export.ne = export.not_equal

--[==[
Returns {true} if two variables refer to the same object; roughly equivalent to the {{code|py|is}} keyword in Python.

This differs from {==} or {rawequal()}, in that it accounts for strange objects like [[w:NaN|NaN]], which always return false for equality checks. As such, it should only be used when it is absolutely necessary to distinguish objects by identity rather than value (e.g. during memoization), and is not a drop-in replacement for {==} or {rawequal()}.]==]
function export.is(a, b)
	-- rawequal() will correctly filter almost everything.
	if rawequal(a, b) then
		-- If equal, return true unless one is +0 and the other -0, as these
		-- behave differently under certain conditions: e.g. 1/(+0) is infinity,
		-- but 1/(-0) is -infinity.
		return a ~= 0 or 1 / a == 1 / b
	end
	-- If not equal, return false unless a and b are both NaN and have the same
	-- sign. NaN will return false when compared with itself, so compare with
	-- ~= (since there's no need for rawequal(), as Lua never invokes an __eq
	-- metamethod when comparing an object with itself).
	return a ~= a and b ~= b and sign(a) == sign(b)
end

do
	-- Does the work for the four relational operators <, >, <= & >=, with a
	-- fix for the issues with string comparison.
	-- `default` is the result of the standard comparison, which needs to be
	-- done to ensure that any type errors get thrown when appropriate. If `a`
	-- and `b` aren't strings, it is simply returned.
	-- `or_equal` is a boolean flag to ensure that < & > return false and <= &
	-- >= return true if the strings are equal.
	local function _less_than(a, b, default, or_equal)
		-- Only necessary to check the type of `a`, since <, >, <= and >= always
		-- throw errors if the two objects have different types, and this can't
		-- be overridden with metamethods.
		if type(a) ~= "string" then
			return default
		-- Do the equality check.
		elseif a == b then
			return or_equal
		end
		-- Byte comparison is slow, so only do it when it's really needed:
		-- iterate over both strings, grabbing a set of ASCII bytes followed by
		-- a set of non-ASCII bytes from each (either of which could be empty),
		-- and compare them with ==. If the ASCII substrings are unequal, there
		-- is no need to compare bytes at all, since the < operator will work
		-- correctly, so only start the byte comparison if unequal non-ASCII
		-- substrings have been found.
		local loc, ascii_a, nonascii_a, ascii_b, nonascii_b = 1
		repeat
			ascii_a, nonascii_a = match(a, "^([^\128-\255]*)([\128-\255]*)", loc)
			ascii_b, nonascii_b, loc = match(b, "^([^\128-\255]*)([\128-\255]*)()", loc) -- update `loc` on the second call
			-- If the ASCII parts aren't equal, use < to compare them, since
			-- the bugs don't affect comparisons of ASCII strings. The lower
			-- substring will be from the lower string *except* when it
			-- comprises the start of the other string *and* is followed by a
			-- non-ASCII character; e.g. if `ascii_a` is "abc" and `ascii_b` is
			-- "abcd", the correct result depends on whether "abc" is at the
			-- end of `a` (making `a` lower) or if it's followed by a non-ASCII
			-- character (making `b` is lower, e.g. "abcé" < "abcd" is false).
			if ascii_a ~= ascii_b then
				if ascii_a < ascii_b then
					return nonascii_a == "" or ascii_a ~= sub(ascii_b, 1, #ascii_a)
				end
				return not (nonascii_b == "" or ascii_b ~= sub(ascii_a, 1, #ascii_b))
			end
		-- If the non-ASCII parts are not equal, terminate the loop.
		until nonascii_a ~= nonascii_b
		-- If either one is the empty string, then the end of that string has
		-- been reached, making it the lower string. Note that they can't both
		-- be the empty string, because `a` and `b` aren't equal.
		if nonascii_a == "" then
			return true
		elseif nonascii_b == "" then
			return false
		end
		loc = 1
		while true do
			-- 4 bytes at a time is a balance between minimizing the number of
			-- byte() calls without grabbing unnecessary extra bytes after the
			-- difference.
			local b_a1, b_a2, b_a3, b_a4 = byte(nonascii_a, loc, loc + 3)
			if b_a1 == nil then
				return true
			end
			local b_b1, b_b2, b_b3, b_b4 = byte(nonascii_b, loc, loc + 3)
			if b_a1 ~= b_b1 then
				return b_b1 and b_a1 < b_b1
			elseif b_a2 ~= b_b2 then
				return b_a2 == nil or b_b2 and b_a2 < b_b2
			elseif b_a3 ~= b_b3 then
				return b_a3 == nil or b_b3 and b_a3 < b_b3
			elseif b_a4 ~= b_b4 then
				return b_a4 == nil or b_b4 and b_a4 < b_b4
			end
			loc = loc + 4
		end
	end

	--[==[
	The less-than operator {<} as a function, incorporating some fixes for bugs in the standard {<} operator.

	Note that this uses byte-order for strings, which fixes the issues with {<} raised in [[phab:T193096#4161287]] and [[phab:T49137#9167559]]:
	* {<} is supposed to compare UTF-8 codepoints in the two strings, but when a codepoint that is U+10000 or above is encountered in the left-hand string, {<} always returns {false}. This even occurs when the left-hand codepoint is lower than the right, when both are outside of the BMP, resulting in incorrect results.
	* Unassigned codepoints and bytes which constitute invalid UTF-8 are considered less than almost all assigned codepoints.]==]
	function export.less_than(a, b)
		return _less_than(a, b, a < b, false)
	end
	less_than = export.less_than
	export.lt = less_than

	--[==[
	The less-than-or-equal operator {<=} as a function, incorporating the same fixes implemented in {export.less_than()}.]==]
	function export.less_than_or_equal(a, b)
		return _less_than(a, b, a <= b, true)
	end
	export.le = export.less_than_or_equal

	--[==[
	The greater-than operator {>} as a function, incorporating the same fixes implemented in {export.less_than()}.]==]
	function export.greater_than(a, b)
		return _less_than(b, a, a > b, false)
	end
	export.gt = export.greater_than

	--[==[
	The greater-than-or-equal operator {>=} as a function, incorporating the same fixes implemented in {export.less_than()}.]==]
	function export.greater_than_or_equal(a, b)
		return _less_than(b, a, a >= b, true)
	end
	export.ge = export.greater_than_or_equal
end

do
	local types
	local function get_types()
		types, get_types = {
			["boolean"] = 1,
			["number"] = 2,
			["string"] = 3,
			["table"] = 4,
			["function"] = 5,
			["thread"] = 6,
			["userdata"] = 7,
		}, nil
		return types
	end

	local function _lt(a, b)
		return a < b
	end

	--[==[
	A general compare function which is able to compare two inputs of any type. It can be used as the sort function when calling {table.sort}.]==]
	function export.compare(a, b)
		if a == nil then
			return b ~= nil
		elseif b == nil then
			return false
		end
		local type_a, type_b = type(a), type(b)
		if type_a ~= type_b then
			return (types or get_types())[type_a] < types[type_b]
		elseif type_a == "string" then
			return less_than(a, b)
		elseif type_a == "number" then
			-- +NaN > +inf and -NaN < -inf.
			if a ~= a then
				-- If a is +NaN, a >= b.
				if sign(a) == 1 then
					return false
				-- Otherwise a is -NaN, so a < b if b is not NaN.
				elseif b == b then
					return true
				end
			-- -0 < +0.
			elseif a == 0 and b == 0 then
				if sign(a, true) == 1 then
					return false
				end
			-- Standard comparison.
			elseif b == b then
				return a < b
			end
			return sign(b, true) == 1
		elseif type_a == "boolean" then
			return a == true and b == false
		end
		local success, result = pcall(_lt, a, b)
		return success and result or false
	end
end

--[==[
Logical XOR (exclusive OR): true only if exactly one input argument is true. Considers {false} and {nil} as logically false (falsy), and anything else as logically true (truthy).

Note: for consistency with the in-built {and} and {or} operators, the precise return value depends on the input arguments:
* If one argument is logically true and the other logically false, returns the truthy argument.
* If both are logically true, returns {false}.
* If both are logically false, returns the (falsy) second argument.]==]
function export.xor(a, b)
	if not a then
		return b
	elseif not b then
		return a
	end
	return false
end

--[==[
Given an arbitrary number of input arguments, returns the first value which is not {nil}. Roughly equivalent to a chain of `??` operators in various languages.]==]
function export.null_coalesce(v, ...)
	if v ~= nil then
		return v
	end
	v = ...
	if v ~= nil then
		return v
	end
	for i = 2, select("#", ...) do
		local v = select(i, ...)
		if v ~= nil then
			return v
		end
	end
end

return export