<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://linguifex.com/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AUnicode_data</id>
	<title>Module:Unicode data - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://linguifex.com/w/index.php?action=history&amp;feed=atom&amp;title=Module%3AUnicode_data"/>
	<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=Module:Unicode_data&amp;action=history"/>
	<updated>2026-04-03T20:04:32Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://linguifex.com/w/index.php?title=Module:Unicode_data&amp;diff=423785&amp;oldid=prev</id>
		<title>Sware at 13:00, 11 January 2025</title>
		<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=Module:Unicode_data&amp;diff=423785&amp;oldid=prev"/>
		<updated>2025-01-11T13:00:41Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;a href=&quot;https://linguifex.com/w/index.php?title=Module:Unicode_data&amp;amp;diff=423785&amp;amp;oldid=410689&quot;&gt;Show changes&lt;/a&gt;</summary>
		<author><name>Sware</name></author>
	</entry>
	<entry>
		<id>https://linguifex.com/w/index.php?title=Module:Unicode_data&amp;diff=410689&amp;oldid=prev</id>
		<title>Sware at 21:41, 8 January 2025</title>
		<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=Module:Unicode_data&amp;diff=410689&amp;oldid=prev"/>
		<updated>2025-01-08T21:41:03Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;a href=&quot;https://linguifex.com/w/index.php?title=Module:Unicode_data&amp;amp;diff=410689&amp;amp;oldid=229733&quot;&gt;Show changes&lt;/a&gt;</summary>
		<author><name>Sware</name></author>
	</entry>
	<entry>
		<id>https://linguifex.com/w/index.php?title=Module:Unicode_data&amp;diff=229733&amp;oldid=prev</id>
		<title>Sware: Created page with &quot;local export = {}  local floor = math.floor  local function errorf(first_arg, ...) 	if type(first_arg) == &quot;number&quot; then 		return error(string.format(...), first_arg + 1) 	else...&quot;</title>
		<link rel="alternate" type="text/html" href="https://linguifex.com/w/index.php?title=Module:Unicode_data&amp;diff=229733&amp;oldid=prev"/>
		<updated>2021-07-02T14:25:32Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;local export = {}  local floor = math.floor  local function errorf(first_arg, ...) 	if type(first_arg) == &amp;quot;number&amp;quot; then 		return error(string.format(...), first_arg + 1) 	else...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local export = {}&lt;br /&gt;
&lt;br /&gt;
local floor = math.floor&lt;br /&gt;
&lt;br /&gt;
local function errorf(first_arg, ...)&lt;br /&gt;
	if type(first_arg) == &amp;quot;number&amp;quot; then&lt;br /&gt;
		return error(string.format(...), first_arg + 1)&lt;br /&gt;
	else&lt;br /&gt;
		return error(string.format(first_arg, ...), 2)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function binary_range_search(codepoint, ranges)&lt;br /&gt;
	local low, mid, high&lt;br /&gt;
	low, high = 1, ranges.length or require &amp;quot;Module:table&amp;quot;.length(ranges)&lt;br /&gt;
	while low &amp;lt;= high do&lt;br /&gt;
		mid = floor((low + high) / 2)&lt;br /&gt;
		local range = ranges[mid]&lt;br /&gt;
		if codepoint &amp;lt; range[1] then&lt;br /&gt;
			high = mid - 1&lt;br /&gt;
		elseif codepoint &amp;lt;= range[2] then&lt;br /&gt;
			return range, mid&lt;br /&gt;
		else&lt;br /&gt;
			low = mid + 1&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return nil, mid&lt;br /&gt;
end&lt;br /&gt;
export.binary_range_search = binary_range_search&lt;br /&gt;
&lt;br /&gt;
local function linear_range_search(codepoint, ranges)&lt;br /&gt;
	for i, range in ipairs(ranges) do&lt;br /&gt;
		if codepoint &amp;lt; range[1] then&lt;br /&gt;
			break&lt;br /&gt;
		elseif codepoint &amp;lt;= range[2] then&lt;br /&gt;
			return range&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Load a module by indexing &amp;quot;loader&amp;quot; with the name of the module minus the&lt;br /&gt;
-- &amp;quot;Module:Unicode data/&amp;quot; part. For instance, loader.blocks returns&lt;br /&gt;
-- [[Module:Unicode data/blocks]]. If a module cannot be loaded, false will be&lt;br /&gt;
-- returned.&lt;br /&gt;
local loader = setmetatable({}, {&lt;br /&gt;
	__index = function (self, key)&lt;br /&gt;
		local success, data = pcall(mw.loadData, &amp;quot;Module:Unicode data/&amp;quot; .. key)&lt;br /&gt;
		if not success then&lt;br /&gt;
			data = false&lt;br /&gt;
		end&lt;br /&gt;
		self[key] = data&lt;br /&gt;
		return data&lt;br /&gt;
	end&lt;br /&gt;
})&lt;br /&gt;
&lt;br /&gt;
-- For the algorithm used to generate Hangul Syllable names,&lt;br /&gt;
-- see &amp;quot;Hangul Syllable Name Generation&amp;quot; in section 3.12 of the&lt;br /&gt;
-- Unicode Specification:&lt;br /&gt;
-- https://www.unicode.org/versions/latest/ch03.pdf&lt;br /&gt;
-- For most of the name rules given here, see the subsection&lt;br /&gt;
-- &amp;quot;Unicode Name Property&amp;quot; in section 4.8 (Name) and the table 4-8&lt;br /&gt;
-- (Name Derivation Rule Prefix Strings):&lt;br /&gt;
-- https://www.unicode.org/versions/latest/ch04.pdf&lt;br /&gt;
local name_hooks = {&lt;br /&gt;
	{     0x00,     0x1F, &amp;quot;&amp;lt;control-%04X&amp;gt;&amp;quot; }, -- C0 control characters&lt;br /&gt;
	{     0x7F,     0x9F, &amp;quot;&amp;lt;control-%04X&amp;gt;&amp;quot; }, -- DEL and C1 control characters&lt;br /&gt;
	{   0x3400,   0x4DBF, &amp;quot;CJK UNIFIED IDEOGRAPH-%04X&amp;quot; }, -- CJK Ideograph Extension A&lt;br /&gt;
	{   0x4E00,   0x9FFC, &amp;quot;CJK UNIFIED IDEOGRAPH-%04X&amp;quot; }, -- CJK Ideograph&lt;br /&gt;
	{   0xAC00,   0xD7A3, function (codepoint) -- Hangul Syllables&lt;br /&gt;
		local Hangul_data = loader.Hangul&lt;br /&gt;
		local syllable_index = codepoint - 0xAC00&lt;br /&gt;
&lt;br /&gt;
		return (&amp;quot;HANGUL SYLLABLE %s%s%s&amp;quot;):format(&lt;br /&gt;
			Hangul_data.leads[floor(syllable_index / Hangul_data.final_count)],&lt;br /&gt;
			Hangul_data.vowels[floor((syllable_index % Hangul_data.final_count)&lt;br /&gt;
				/ Hangul_data.trail_count)],&lt;br /&gt;
			Hangul_data.trails[syllable_index % Hangul_data.trail_count]&lt;br /&gt;
		)&lt;br /&gt;
	end },&lt;br /&gt;
	-- High Surrogates, High Private Use Surrogates, Low Surrogates&lt;br /&gt;
	{   0xD800,   0xDFFF, &amp;quot;&amp;lt;surrogate-%04X&amp;gt;&amp;quot; },&lt;br /&gt;
	{   0xE000,   0xF8FF, &amp;quot;&amp;lt;private-use-%04X&amp;gt;&amp;quot; }, -- Private Use&lt;br /&gt;
	-- CJK Compatibility Ideographs&lt;br /&gt;
	{   0xF900,   0xFA6D, &amp;quot;CJK COMPATIBILITY IDEOGRAPH-%04X&amp;quot; },&lt;br /&gt;
	{   0xFA70,   0xFAD9, &amp;quot;CJK COMPATIBILITY IDEOGRAPH-%04X&amp;quot; },&lt;br /&gt;
	{  0x17000,  0x187F7, &amp;quot;TANGUT IDEOGRAPH-%04X&amp;quot; }, -- Tangut&lt;br /&gt;
	{  0x18800,  0x18AFF, function (codepoint)&lt;br /&gt;
		return (&amp;quot;TANGUT COMPONENT-%03d&amp;quot;):format(codepoint - 0x187FF)&lt;br /&gt;
	end },&lt;br /&gt;
	{  0x18D00,  0x18D08, &amp;quot;TANGUT IDEOGRAPH-%04X&amp;quot; }, -- Tangut&lt;br /&gt;
	{  0x18B00,  0x18CD5, &amp;quot;KHITAN SMALL SCRIPT CHARACTER-%04X&amp;quot; },&lt;br /&gt;
	{  0x1B170,  0x1B2FB, &amp;quot;NUSHU CHARACTER-%04X&amp;quot; }, -- Nushu&lt;br /&gt;
	{  0x20000,  0x2A6DD, &amp;quot;CJK UNIFIED IDEOGRAPH-%04X&amp;quot; }, -- CJK Ideograph Extension B&lt;br /&gt;
	{  0x2A700,  0x2B734, &amp;quot;CJK UNIFIED IDEOGRAPH-%04X&amp;quot; }, -- CJK Ideograph Extension C&lt;br /&gt;
	{  0x2A740,  0x2B81D, &amp;quot;CJK UNIFIED IDEOGRAPH-%04X&amp;quot; }, -- CJK Ideograph Extension D&lt;br /&gt;
	{  0x2B820,  0x2CEA1, &amp;quot;CJK UNIFIED IDEOGRAPH-%04X&amp;quot; }, -- CJK Ideograph Extension E&lt;br /&gt;
	{  0x2CEB0,  0x2EBE0, &amp;quot;CJK UNIFIED IDEOGRAPH-%04X&amp;quot; }, -- CJK Ideograph Extension F&lt;br /&gt;
	-- CJK Compatibility Ideographs Supplement (Supplementary Ideographic Plane)&lt;br /&gt;
	{  0x2F800,  0x2FA1D, &amp;quot;CJK COMPATIBILITY IDEOGRAPH-%04X&amp;quot; },&lt;br /&gt;
	{  0x30000,  0x3134A, &amp;quot;CJK UNIFIED IDEOGRAPH-%04X&amp;quot; }, -- CJK Ideograph Extension G&lt;br /&gt;
	{  0xE0100,  0xE01EF, function (codepoint) -- Variation Selectors Supplement&lt;br /&gt;
		return (&amp;quot;VARIATION SELECTOR-%d&amp;quot;):format(codepoint - 0xE0100 + 17)&lt;br /&gt;
	end},&lt;br /&gt;
	{  0xF0000,  0xFFFFD, &amp;quot;&amp;lt;private-use-%04X&amp;gt;&amp;quot; }, -- Plane 15 Private Use&lt;br /&gt;
	{ 0x100000, 0x10FFFD, &amp;quot;&amp;lt;private-use-%04X&amp;gt;&amp;quot; }  -- Plane 16 Private Use&lt;br /&gt;
}&lt;br /&gt;
name_hooks.length = #name_hooks&lt;br /&gt;
&lt;br /&gt;
local name_range_cache&lt;br /&gt;
&lt;br /&gt;
local function generate_name(data, codepoint)&lt;br /&gt;
	if type(data) == &amp;quot;string&amp;quot; then&lt;br /&gt;
		return data:format(codepoint)&lt;br /&gt;
	else&lt;br /&gt;
		return data(codepoint)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- Checks that the code point is a number and in range.&lt;br /&gt;
-- Does not check whether code point is an integer.&lt;br /&gt;
-- Not used&lt;br /&gt;
local function check_codepoint(funcName, argIdx, val)&lt;br /&gt;
	require &amp;#039;libraryUtil&amp;#039;.checkType(funcName, argIdx, val, &amp;#039;number&amp;#039;)&lt;br /&gt;
	if codepoint &amp;lt; 0 or 0x10FFFF &amp;lt; codepoint then&lt;br /&gt;
		errorf(&amp;quot;Codepoint %04X out of range&amp;quot;, codepoint)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- https://www.unicode.org/versions/latest/ch04.pdf, section 4.8&lt;br /&gt;
function export.lookup_name(codepoint)&lt;br /&gt;
	-- U+FDD0-U+FDEF and all code points ending in FFFE or FFFF are Unassigned&lt;br /&gt;
	-- (Cn) and specifically noncharacters:&lt;br /&gt;
	-- https://www.unicode.org/faq/private_use.html#nonchar4&lt;br /&gt;
	if 0xFDD0 &amp;lt;= codepoint and (codepoint &amp;lt;= 0xFDEF&lt;br /&gt;
			or floor(codepoint % 0x10000) &amp;gt;= 0xFFFE) then&lt;br /&gt;
		return (&amp;quot;&amp;lt;noncharacter-%04X&amp;gt;&amp;quot;):format(codepoint)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if name_range_cache -- Check if previously used &amp;quot;name hook&amp;quot; applies to this code point.&lt;br /&gt;
			and codepoint &amp;gt;= name_range_cache[1]&lt;br /&gt;
			and codepoint &amp;lt;= name_range_cache[2] then&lt;br /&gt;
		return generate_name(name_range_cache[3], codepoint)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local range = binary_range_search(codepoint, name_hooks)&lt;br /&gt;
	if range then&lt;br /&gt;
		name_range_cache = range&lt;br /&gt;
		return generate_name(range[3], codepoint)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local data = loader[(&amp;#039;names/%03X&amp;#039;):format(codepoint / 0x1000)]&lt;br /&gt;
	&lt;br /&gt;
	if data and data[codepoint] then&lt;br /&gt;
		return data[codepoint]&lt;br /&gt;
	&lt;br /&gt;
	-- Unassigned (Cn) consists of noncharacters and reserved characters.&lt;br /&gt;
	-- The character has been established not to be a noncharacter,&lt;br /&gt;
	-- and if it were assigned, its name would already been retrieved,&lt;br /&gt;
	-- so it must be reserved.&lt;br /&gt;
	else&lt;br /&gt;
		return (&amp;quot;&amp;lt;reserved-%04X&amp;gt;&amp;quot;):format(codepoint)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function export.lookup_image(codepoint)&lt;br /&gt;
	local data = loader[(&amp;#039;images/%03X&amp;#039;):format(codepoint / 0x1000)]&lt;br /&gt;
	&lt;br /&gt;
	if data then&lt;br /&gt;
		return data[codepoint]&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local planes = {&lt;br /&gt;
	[ 0] = &amp;quot;Basic Multilingual Plane&amp;quot;;&lt;br /&gt;
	[ 1] = &amp;quot;Supplementary Multilingual Plane&amp;quot;;&lt;br /&gt;
	[ 2] = &amp;quot;Supplementary Ideographic Plane&amp;quot;;&lt;br /&gt;
	[ 3] = &amp;quot;Tertiary Ideographic Plane&amp;quot;;&lt;br /&gt;
	[14] = &amp;quot;Supplementary Special-purpose Plane&amp;quot;;&lt;br /&gt;
	[15] = &amp;quot;Supplementary Private Use Area-A&amp;quot;;&lt;br /&gt;
	[16] = &amp;quot;Supplementary Private Use Area-B&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Load [[Module:Unicode data/blocks]] if needed and assign it to this variable.&lt;br /&gt;
local blocks&lt;br /&gt;
&lt;br /&gt;
local function block_iter(blocks, i)&lt;br /&gt;
	i = i + 1&lt;br /&gt;
	local data = blocks[i]&lt;br /&gt;
	if data then&lt;br /&gt;
		 -- Unpack doesn&amp;#039;t work on tables loaded with mw.loadData.&lt;br /&gt;
		return i, data[3], data[1], data[2]&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- An ipairs-type iterator generator for the list of blocks.&lt;br /&gt;
function export.enum_blocks()&lt;br /&gt;
	local blocks = loader.blocks&lt;br /&gt;
	return block_iter, blocks, 0&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function export.get_block_range(name)&lt;br /&gt;
	local range&lt;br /&gt;
	&lt;br /&gt;
	for i, block in ipairs(loader.blocks) do&lt;br /&gt;
		if block[3] == name then&lt;br /&gt;
			range = block&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if range then&lt;br /&gt;
		return range[1], range[2]&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function export.lookup_plane(codepoint)&lt;br /&gt;
	local i = floor(codepoint / 0x10000)&lt;br /&gt;
	return planes[i] or (&amp;quot;Plane %u&amp;quot;):format(i)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function export.lookup_block(codepoint)&lt;br /&gt;
	local blocks = loader.blocks&lt;br /&gt;
	local range = binary_range_search(codepoint, blocks)&lt;br /&gt;
	if range then&lt;br /&gt;
		return range[3]&lt;br /&gt;
	else&lt;br /&gt;
		return &amp;quot;No Block&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function export.get_block_info(name)&lt;br /&gt;
	for i, block in ipairs(loader.blocks) do&lt;br /&gt;
		if block[3] == name then&lt;br /&gt;
			return block&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function export.is_valid_pagename(pagename)&lt;br /&gt;
	local has_nonws = false&lt;br /&gt;
&lt;br /&gt;
	for cp in mw.ustring.gcodepoint(pagename) do&lt;br /&gt;
		if (cp == 0x0023) -- #&lt;br /&gt;
		or (cp == 0x005B) -- [&lt;br /&gt;
		or (cp == 0x005D) -- ]&lt;br /&gt;
		or (cp == 0x007B) -- {&lt;br /&gt;
		or (cp == 0x007C) -- |&lt;br /&gt;
		or (cp == 0x007D) -- }&lt;br /&gt;
		or (cp == 0x180E) -- MONGOLIAN VOWEL SEPARATOR&lt;br /&gt;
		or ((cp &amp;gt;= 0x2000) and (cp &amp;lt;= 0x200A)) -- spaces in General Punctuation block&lt;br /&gt;
		or (cp == 0xFFFD) -- REPLACEMENT CHARACTER&lt;br /&gt;
		then&lt;br /&gt;
			return false&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local printable, result = export.is_printable(cp)&lt;br /&gt;
		if not printable then&lt;br /&gt;
			return false&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if result ~= &amp;quot;space-separator&amp;quot; then&lt;br /&gt;
			has_nonws = true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return has_nonws&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function manual_unpack(what, from)&lt;br /&gt;
	if what[from + 1] == nil then&lt;br /&gt;
		return what[from]&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local result = {}&lt;br /&gt;
	from = from or 1&lt;br /&gt;
	for i, item in ipairs(what) do&lt;br /&gt;
		if i &amp;gt;= from then&lt;br /&gt;
			table.insert(result, item)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return unpack(result)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function compare_ranges(range1, range2)&lt;br /&gt;
	return range1[1] &amp;lt; range2[1]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Creates a function to look up data in a module that contains &amp;quot;singles&amp;quot; (a&lt;br /&gt;
-- code point-to-data map) and &amp;quot;ranges&amp;quot; (an array containing arrays that contain&lt;br /&gt;
-- the low and high code points of a range and the data associated with that&lt;br /&gt;
-- range).&lt;br /&gt;
-- &amp;quot;loader&amp;quot; loads and returns the &amp;quot;singles&amp;quot; and &amp;quot;ranges&amp;quot; tables.&lt;br /&gt;
-- &amp;quot;match_func&amp;quot; is passed the code point and either the data or the &amp;quot;dots&amp;quot;, and&lt;br /&gt;
-- generates the final result of the function.&lt;br /&gt;
-- The varargs (&amp;quot;dots&amp;quot;) describes the default data to be returned if there wasn&amp;#039;t&lt;br /&gt;
-- a match.&lt;br /&gt;
-- In case the function is used more than once, &amp;quot;cache&amp;quot; saves ranges that have&lt;br /&gt;
-- already been found to match, or a range whose data is the default if there&lt;br /&gt;
-- was no match.&lt;br /&gt;
local function memo_lookup(data_module_subpage, match_func, ...)&lt;br /&gt;
	local dots = { ... }&lt;br /&gt;
	local cache = {}&lt;br /&gt;
	local singles, ranges&lt;br /&gt;
&lt;br /&gt;
	return function (codepoint)&lt;br /&gt;
		if not singles then&lt;br /&gt;
			local data_module = loader[data_module_subpage]&lt;br /&gt;
			singles, ranges = data_module.singles, data_module.ranges&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if singles[codepoint] then&lt;br /&gt;
			return match_func(codepoint, singles[codepoint])&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		local range = binary_range_search(codepoint, cache)&lt;br /&gt;
		if range then&lt;br /&gt;
			return match_func(codepoint, manual_unpack(range, 3))&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		local range, index = binary_range_search(codepoint, ranges)&lt;br /&gt;
		if range then&lt;br /&gt;
			table.insert(cache, range)&lt;br /&gt;
			table.sort(cache, compare_ranges)&lt;br /&gt;
			return match_func(codepoint, manual_unpack(range, 3))&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if ranges[index] then&lt;br /&gt;
			local dots_range&lt;br /&gt;
			if codepoint &amp;gt; ranges[index][2] then&lt;br /&gt;
				dots_range = {&lt;br /&gt;
					ranges[index][2] + 1,&lt;br /&gt;
					ranges[index + 1] and ranges[index + 1][1] - 1 or 0x10FFFF,&lt;br /&gt;
					unpack(dots)&lt;br /&gt;
				}&lt;br /&gt;
			else -- codepoint &amp;lt; range[index][1]&lt;br /&gt;
				dots_range = {&lt;br /&gt;
					ranges[index - 1] and ranges[index - 1][2] + 1 or 0,&lt;br /&gt;
					ranges[index][1] - 1,&lt;br /&gt;
					unpack(dots)&lt;br /&gt;
				}&lt;br /&gt;
			end&lt;br /&gt;
			table.sort(cache, compare_ranges)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		return match_func(codepoint, unpack(dots))&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Get a code point&amp;#039;s combining class value in [[Module:Unicode data/combining]],&lt;br /&gt;
-- and return whether this value is not zero. Zero is assigned as the default&lt;br /&gt;
-- if the combining class value is not found in this data module.&lt;br /&gt;
-- That is, return true if character is combining, or false if it is not.&lt;br /&gt;
-- See https://www.unicode.org/reports/tr44/#Canonical_Combining_Class_Values for&lt;br /&gt;
-- more information.&lt;br /&gt;
export.is_combining = memo_lookup(&lt;br /&gt;
	&amp;quot;combining&amp;quot;,&lt;br /&gt;
	function (codepoint, combining_class)&lt;br /&gt;
		return combining_class and combining_class ~= 0 or false&lt;br /&gt;
	end,&lt;br /&gt;
	0)&lt;br /&gt;
&lt;br /&gt;
function export.add_dotted_circle(str)&lt;br /&gt;
	return (mw.ustring.gsub(str, &amp;quot;.&amp;quot;,&lt;br /&gt;
		function(char)&lt;br /&gt;
			if export.is_combining(mw.ustring.codepoint(char)) then&lt;br /&gt;
				return &amp;#039;◌&amp;#039; .. char&lt;br /&gt;
			end&lt;br /&gt;
		end))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local lookup_control = memo_lookup(&lt;br /&gt;
	&amp;quot;control&amp;quot;,&lt;br /&gt;
	function (codepoint, ccc)&lt;br /&gt;
		return ccc or &amp;quot;assigned&amp;quot;&lt;br /&gt;
	end,&lt;br /&gt;
	&amp;quot;assigned&amp;quot;)&lt;br /&gt;
export.lookup_control = lookup_control&lt;br /&gt;
&lt;br /&gt;
function export.is_assigned(codepoint)&lt;br /&gt;
	return lookup_control(codepoint) ~= &amp;quot;unassigned&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function export.is_printable(codepoint)&lt;br /&gt;
	local result = lookup_control(codepoint)&lt;br /&gt;
	return (result == &amp;quot;assigned&amp;quot;) or (result == &amp;quot;space-separator&amp;quot;), result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function export.is_whitespace(codepoint)&lt;br /&gt;
	local result = lookup_control(codepoint)&lt;br /&gt;
	return (result == &amp;quot;space-separator&amp;quot;), result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
export.lookup_category = memo_lookup(&lt;br /&gt;
	&amp;quot;category&amp;quot;,&lt;br /&gt;
	function (codepoint, category)&lt;br /&gt;
		return category&lt;br /&gt;
	end,&lt;br /&gt;
	&amp;quot;Cn&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
export.lookup_script = memo_lookup(&lt;br /&gt;
	&amp;quot;scripts&amp;quot;,&lt;br /&gt;
	function (codepoint, script)&lt;br /&gt;
		return script&lt;br /&gt;
	end,&lt;br /&gt;
	&amp;quot;Zzzz&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
local unsupported_title = {&lt;br /&gt;
	[0x0020] = &amp;quot;Unsupported titles/Space&amp;quot;;&lt;br /&gt;
	[0x0023] = &amp;quot;Unsupported titles/Number sign&amp;quot;;&lt;br /&gt;
	[0x002E] = &amp;quot;Unsupported titles/Full stop&amp;quot;;&lt;br /&gt;
	[0x003A] = &amp;quot;Unsupported titles/Colon&amp;quot;;&lt;br /&gt;
	[0x003C] = &amp;quot;Unsupported titles/Less than&amp;quot;;&lt;br /&gt;
	[0x003E] = &amp;quot;Unsupported titles/Greater than&amp;quot;;&lt;br /&gt;
	[0x005B] = &amp;quot;Unsupported titles/Left square bracket&amp;quot;;&lt;br /&gt;
	[0x005D] = &amp;quot;Unsupported titles/Right square bracket&amp;quot;;&lt;br /&gt;
	[0x005F] = &amp;quot;Unsupported titles/Low line&amp;quot;;&lt;br /&gt;
	[0x007B] = &amp;quot;Unsupported titles/Left curly bracket&amp;quot;;&lt;br /&gt;
	[0x007C] = &amp;quot;Unsupported titles/Vertical line&amp;quot;;&lt;br /&gt;
	[0x007D] = &amp;quot;Unsupported titles/Right curly bracket&amp;quot;;&lt;br /&gt;
	[0x1680] = &amp;quot;Unsupported titles/Ogham space&amp;quot;;&lt;br /&gt;
	[0xFFFD] = &amp;quot;Unsupported titles/Replacement character&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function export.get_entry_title(codepoint)&lt;br /&gt;
	if unsupported_title[codepoint] then&lt;br /&gt;
		return unsupported_title[codepoint]&lt;br /&gt;
	end&lt;br /&gt;
	if lookup_control(codepoint) ~= &amp;quot;assigned&amp;quot; then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	return mw.ustring.char(codepoint)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return export&lt;/div&gt;</summary>
		<author><name>Sware</name></author>
	</entry>
</feed>