-- lx-bundle:true

--- Plural resolvers
-- Based on Language.cpp by Liqube Audio
-- @module plural

local plural = {}

-- ==========================================================================================
-- plural category resolver functions
-- returns a category string
-- ==========================================================================================

--- Returns "one" for n == 1, otherwise "other"
-- Used by most languages with a simple singular/plural split
-- @param n number
-- @return string plural category ("one" or "other")
function plural.one_other_n1(n)
	return (n == 1) and "one" or "other"
end

--- Returns "one" for n == 0 or n == 1, otherwise "other"
-- Used by languages that treat 0 like singular in many contexts (per original mapping)
-- @param n number
-- @return string plural category ("one" or "other")
function plural.one_other_gt1(n)
	return (n == 0 or n == 1) and "one" or "other"
end

--- Returns "one" for 1, "two" for 2, otherwise "other"
-- @param n number
-- @return string plural category ("one"|"two"|"other")
function plural.one_two_other(n)
	if n == 1 then
		return "one"
	end
	if n == 2 then
		return "two"
	end
	return "other"
end

--- Returns "zero" for 0, "one" for 1, otherwise "other"
-- @param n number
-- @return string plural category ("zero"|"one"|"other")
function plural.zero_one_other(n)
	if n == 0 then
		return "zero"
	end
	if n == 1 then
		return "one"
	end
	return "other"
end

--- Icelandic-style rule: "one" when n%10==1 and n%100!=11, otherwise "other"
-- @param n number
-- @return string plural category ("one"|"other")
function plural.one_other_icelandic(n)
	if (n % 10 == 1) and (n % 100 ~= 11) then
		return "one"
	end
	return "other"
end

--- Czech/Slovak-style rule: one (1), few (2..4), other
-- @param n number
-- @return string plural category ("one"|"few"|"other")
function plural.one_few_other_slovak(n)
	if n == 1 then
		return "one"
	end
	if n >= 2 and n <= 4 then
		return "few"
	end
	return "other"
end

--- Russian/Ukrainian/Belarusian/Serbian/Croatian-style: one, few, other
-- @param n number
-- @return string plural category ("one"|"few"|"other")
function plural.one_few_other_russian(n)
	local mod10 = n % 10
	local mod100 = n % 100

	if mod10 == 1 and mod100 ~= 11 then
		return "one"
	end
	if mod10 >= 2 and mod10 <= 4 and (mod100 < 12 or mod100 > 14) then
		return "few"
	end
	return "other"
end

--- Romanian/Moldavian-style: one, few (including 0 and 1..19 mod100), other
-- @param n number
-- @return string plural category ("one"|"few"|"other")
function plural.one_few_other_romanian(n)
	if n == 1 then
		return "one"
	end
	local mod100 = n % 100

	if (n == 0) or (mod100 >= 1 and mod100 <= 19) then
		return "few"
	end
	return "other"
end

--- Polish-style: one, few, other
-- @param n number
-- @return string plural category ("one"|"few"|"other")
function plural.one_few_other_polish(n)
	if n == 1 then
		return "one"
	end
	local mod10 = n % 10
	local mod100 = n % 100

	if mod10 >= 2 and mod10 <= 4 and (mod100 < 12 or mod100 > 14) then
		return "few"
	end
	return "other"
end

--- Sorbian/Slovenian-style: one (..01), two (..02), few (..03/..04), other
-- @param n number
-- @return string plural category ("one"|"two"|"few"|"other")
function plural.one_two_few_other_sorbian(n)
	local m100 = n % 100
	if m100 == 1 then
		return "one"
	end
	if m100 == 2 then
		return "two"
	end
	if m100 == 3 or m100 == 4 then
		return "few"
	end
	return "other"
end

--- Tagalog/Filipino mapping used by the original table is non-CLDR-ish; keep semantics:
-- Treat certain numbers as "one" vs "other" (2 forms)
-- @param n number
-- @return string plural category ("one"|"other")
function plural.one_other_tagalog(n)
	local special = (n ~= 1) and (n ~= 2) and (n ~= 3) and ((n % 10) == 4 or (n % 10) == 6 or (n % 10) == 9)
	return special and "other" or "one"
end

--- Hebrew: one, two, many (n>10 and n%10==0), other
-- @param n number
-- @return string plural category ("one"|"two"|"many"|"other")
function plural.one_two_many_other_hebrew(n)
	if n == 1 then
		return "one"
	end
	if n == 2 then
		return "two"
	end
	if n > 10 and (n % 10) == 0 then
		return "many"
	end
	return "other"
end

--- Latvian: zero (10==0 or 11..19), one (10==1 and not 11), other
-- @param n number
-- @return string plural category ("zero"|"one"|"other")
function plural.zero_one_other_latvian(n)
	local mod10 = n % 10
	local mod100 = n % 100

	if (mod10 == 0) or (mod100 >= 11 and mod100 <= 19) then
		return "zero"
	end
	if (mod10 == 1) and (mod100 ~= 11) then
		return "one"
	end
	return "other"
end

--- Arabic: zero, one, two, few (3..10 mod100), many (11..99 mod100), other
-- @param n number
-- @return string plural category ("zero"|"one"|"two"|"few"|"many"|"other")
function plural.arabic(n)
	if n == 0 then
		return "zero"
	end
	if n == 1 then
		return "one"
	end
	if n == 2 then
		return "two"
	end
	local m100 = n % 100
	if m100 >= 3 and m100 <= 10 then
		return "few"
	end
	if m100 >= 11 and m100 <= 99 then
		return "many"
	end
	return "other"
end

--- Breton: maps to one, two, few, many, other (5 forms)
-- This keeps the original resolver logic but returns category labels
-- @param n number
-- @return string plural category ("one"|"two"|"few"|"many"|"other")
function plural.breton(n)
	local mod10 = n % 10
	local mod100 = n % 100

	if mod10 == 1 and mod100 ~= 11 and mod100 ~= 71 and mod100 ~= 91 then
		return "one"
	end
	if mod10 == 2 and mod100 ~= 12 and mod100 ~= 72 and mod100 ~= 92 then
		return "two"
	end
	local in10_19 = (mod100 >= 10 and mod100 <= 19)
	local in70_79 = (mod100 >= 70 and mod100 <= 79)
	local in90_99 = (mod100 >= 90 and mod100 <= 99)

	if ((mod10 == 3 or mod10 == 4) or mod10 == 9) and not in10_19 and not in70_79 and not in90_99 then
		return "few"
	end
	if n ~= 0 and (n % 1000000) == 0 then
		return "many"
	end
	return "other"
end

--- Lithuanian: one, few, other
-- @param n number
-- @return string plural category ("one"|"few"|"other")
function plural.one_few_other_lithuanian(n)
	local m10 = n % 10
	local m100 = n % 100

	if m10 == 1 and (m100 < 11 or m100 > 19) then
		return "one"
	end
	if m10 >= 2 and m10 <= 9 and (m100 < 11 or m100 > 19) then
		return "few"
	end
	return "other"
end

--- Maltese: one, few, many, other
-- @param n number
-- @return string plural category ("one"|"few"|"many"|"other")
function plural.one_few_many_other_maltese(n)
	if n == 1 then
		return "one"
	end
	local m100 = n % 100

	if n == 0 or (m100 >= 2 and m100 <= 10) then
		return "few"
	end
	if m100 >= 11 and m100 <= 19 then
		return "many"
	end
	return "other"
end

--- Macedonian: one when n%10==1, otherwise other (2 forms)
-- @param n number
-- @return string plural category ("one"|"other")
function plural.one_other_macedonian(n)
	return ((n % 10) == 1) and "one" or "other"
end

--- Manx: one/two/few/other mapping from original resolver (4 forms)
-- @param n number
-- @return string plural category ("one"|"two"|"few"|"other")
function plural.one_two_few_other_manx(n)
	if (n % 10) == 1 then
		return "one"
	end
	if (n % 10) == 2 then
		return "two"
	end
	local m100 = n % 100

	if m100 == 0 or m100 == 20 or m100 == 40 or m100 == 60 or m100 == 80 then
		return "few"
	end
	return "other"
end

--- Irish: one, two, few, many, other (5 forms) mapped from original
-- @param n number
-- @return string plural category ("one"|"two"|"few"|"many"|"other")
function plural.irish(n)
	if n == 1 then
		return "one"
	end
	if n == 2 then
		return "two"
	end
	if n >= 3 and n <= 6 then
		return "few"
	end
	if n >= 7 and n <= 10 then
		return "many"
	end
	return "other"
end

--- Scottish Gaelic: one, two, few, other (4 forms) mapped from original
-- @param n number
-- @return string plural category ("one"|"two"|"few"|"other")
function plural.scottish(n)
	if n == 1 or n == 11 then
		return "one"
	end
	if n == 2 or n == 12 then
		return "two"
	end
	if (n >= 3 and n <= 10) or (n >= 13 and n <= 19) then
		return "few"
	end
	return "other"
end

--- Welsh: zero, one, two, few, many, other (6 forms) mapped from original
-- @param n number
-- @return string plural category ("zero"|"one"|"two"|"few"|"many"|"other")
function plural.welsh(n)
	if n == 0 then
		return "zero"
	end
	if n == 1 then
		return "one"
	end
	if n == 2 then
		return "two"
	end
	if n == 3 then
		return "few"
	end
	if n == 6 then
		return "many"
	end
	return "other"
end

--- Tachelhit: one (0 or 1), few (2..10), other
-- @param n number
-- @return string plural category ("one"|"few"|"other")
function plural.one_few_other_tachelhit(n)
	if n == 0 or n == 1 then
		return "one"
	end
	if n >= 2 and n <= 10 then
		return "few"
	end
	return "other"
end

--- Tamazight: one vs other based on the original boolean resolver
-- @param n number
-- @return string plural category ("one"|"other")
function plural.one_other_tamazight(n)
	return ((n >= 2) and (n < 11 or n > 99)) and "other" or "one"
end

return plural
