<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://odden.us:443/thadar/wiki/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://odden.us:443/thadar/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AUnicode_data"/>
	<link rel="alternate" type="text/html" href="https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;action=history"/>
	<updated>2026-04-11T09:28:55Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.41.0</generator>
	<entry>
		<id>https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;diff=1511&amp;oldid=prev</id>
		<title>Oa10712: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;diff=1511&amp;oldid=prev"/>
		<updated>2022-12-27T00:46:13Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 19:46, 26 December 2022&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Oa10712</name></author>
	</entry>
	<entry>
		<id>https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;diff=1510&amp;oldid=prev</id>
		<title>en&gt;Drmccreedy: Update for Unicode v15.0</title>
		<link rel="alternate" type="text/html" href="https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;diff=1510&amp;oldid=prev"/>
		<updated>2022-09-17T03:28:33Z</updated>

		<summary type="html">&lt;p&gt;Update for Unicode v15.0&lt;/p&gt;
&lt;a href=&quot;https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;amp;diff=1510&amp;amp;oldid=1184&quot;&gt;Show changes&lt;/a&gt;</summary>
		<author><name>en&gt;Drmccreedy</name></author>
	</entry>
	<entry>
		<id>https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;diff=1184&amp;oldid=prev</id>
		<title>Oa10712: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;diff=1184&amp;oldid=prev"/>
		<updated>2022-09-06T03:34:45Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 22:34, 5 September 2022&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Oa10712</name></author>
	</entry>
	<entry>
		<id>https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;diff=1183&amp;oldid=prev</id>
		<title>en&gt;Surjection: move some data to a data submodule</title>
		<link rel="alternate" type="text/html" href="https://odden.us:443/thadar/wiki/index.php?title=Module:Unicode_data&amp;diff=1183&amp;oldid=prev"/>
		<updated>2022-03-17T15:19:20Z</updated>

		<summary type="html">&lt;p&gt;move some data to a data submodule&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;
local udata = mw.loadData(&amp;quot;Module:Unicode data/data&amp;quot;)&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,   0x9FFF, &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,  0x2A6DF, &amp;quot;CJK UNIFIED IDEOGRAPH-%04X&amp;quot; }, -- CJK Ideograph Extension B&lt;br /&gt;
	{  0x2A700,  0x2B738, &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;
-- 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 udata.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;
function export.get_entry_title(codepoint)&lt;br /&gt;
	if udata.unsupported_title[codepoint] then&lt;br /&gt;
		return udata.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>en&gt;Surjection</name></author>
	</entry>
</feed>