<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://wiki-de.moshellshocker.dns64.de/index.php?action=history&amp;feed=atom&amp;title=Modul%3AIPrange</id>
	<title>Modul:IPrange - Versionsgeschichte</title>
	<link rel="self" type="application/atom+xml" href="https://wiki-de.moshellshocker.dns64.de/index.php?action=history&amp;feed=atom&amp;title=Modul%3AIPrange"/>
	<link rel="alternate" type="text/html" href="https://wiki-de.moshellshocker.dns64.de/index.php?title=Modul:IPrange&amp;action=history"/>
	<updated>2026-06-09T08:48:35Z</updated>
	<subtitle>Versionsgeschichte dieser Seite in Wikipedia (Deutsch) – Lokale Kopie</subtitle>
	<generator>MediaWiki 1.43.8</generator>
	<entry>
		<id>https://wiki-de.moshellshocker.dns64.de/index.php?title=Modul:IPrange&amp;diff=2930460&amp;oldid=prev</id>
		<title>imported&gt;Lómelinde: kl.</title>
		<link rel="alternate" type="text/html" href="https://wiki-de.moshellshocker.dns64.de/index.php?title=Modul:IPrange&amp;diff=2930460&amp;oldid=prev"/>
		<updated>2024-07-29T09:35:26Z</updated>

		<summary type="html">&lt;p&gt;kl.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Neue Seite&lt;/b&gt;&lt;/p&gt;&lt;div&gt;local IPrange = { suite  = &amp;quot;IPrange&amp;quot;,&lt;br /&gt;
                  serial = &amp;quot;dewiki 2018-02-18&amp;quot;,&lt;br /&gt;
                  item   = 21403098 }&lt;br /&gt;
--[=[&lt;br /&gt;
Calculate the minimum-sized blocks of IP addresses&lt;br /&gt;
that cover each IPv4 or IPv6 address entered in the arguments.&lt;br /&gt;
&lt;br /&gt;
**** Dieses Modul basiert auf [[:en:Module:IPblock]]&lt;br /&gt;
**** in der englischsprachigen Wikipedia&lt;br /&gt;
**** von [[:en:User:Johnuniq]]&lt;br /&gt;
**** Stand: 2017-12-16&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
------------------------------ Translation section ----------------------&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- ACHTUNG: Die Programmierung der englischsprachigen Wikipedia&lt;br /&gt;
-- ist nicht für eine Internationalisierung/Lokalisierung vorgesehen&lt;br /&gt;
-- und verwendet alle Meldungstexte hardcoded verteilt im Algorithmus&lt;br /&gt;
-- nur in englischer Sprache.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Messages - Array for easier translation&lt;br /&gt;
local messages = {&lt;br /&gt;
	invalid = &amp;#039;keine gültigen IPv4- oder IPv6-Adressen in den Parametern&amp;#039;,&lt;br /&gt;
	error = &amp;#039;Fehler: &amp;#039;,&lt;br /&gt;
	cidr_error = &amp;#039;CIDR /n akzeptiert nur n = 16 bis 32, ungültig: &amp;#039;,&lt;br /&gt;
	cidr_unsupported = &amp;#039;CIDR /n nicht akzeptiert für IPv6: &amp;#039;,&lt;br /&gt;
	invalid_base = &amp;#039;Ungültige Basis-Adresse (Host-Bits müssen Null sein): &amp;#039;,&lt;br /&gt;
	invalid_results = &amp;#039;Ungültige Angabe &amp;quot;results&amp;quot; (muss zwischen 1 und 100 liegen): &amp;#039;,&lt;br /&gt;
	invalid_allocation = &amp;#039;Ungültige Angabe &amp;quot;allocation&amp;quot; (muss zwischen 48 und 128 liegen): &amp;#039; ,&lt;br /&gt;
	no_ranges_found = &amp;#039;Keine passenden Bereiche gefunden. Nutze &amp;lt;code&amp;gt;|results=all&amp;lt;/code&amp;gt; um alle Bereiche zu sehen.&amp;#039;,&lt;br /&gt;
	omitted = &amp;#039;Warnung, als &amp;quot;ungültig&amp;quot; ausgelassen: &amp;#039;,&lt;br /&gt;
	ip_address_sorted = &amp;#039;Angegebene %s-Adresse&amp;#039;,&lt;br /&gt;
	ip_addresses_sorted = &amp;#039;Angegebene %d %s-Adressen (sortiert)&amp;#039;,&lt;br /&gt;
	duplicates = &amp;#039; (nach Auslassen einiger Duplikate)&amp;#039;,&lt;br /&gt;
	contribs = &amp;#039;Beiträge&amp;#039;,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Strings for results using plain text.&lt;br /&gt;
-- The pre tags used are html which do not provide &amp;quot;nowiki&amp;quot;,&lt;br /&gt;
-- but that is not required by the text used.&lt;br /&gt;
local plaintext = {&lt;br /&gt;
&lt;br /&gt;
header = [=[&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Total        Betroffen    Angegeben   Range]=],&lt;br /&gt;
&lt;br /&gt;
footer = &amp;#039;&amp;lt;/pre&amp;gt;&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
sumfirst = [=[&lt;br /&gt;
----------------------------------------------------------&lt;br /&gt;
%s%-12s %-12s %-11d %s%s]=],&lt;br /&gt;
-- %s = empty string (dummy for compatibility)&lt;br /&gt;
-- %s = total affected&lt;br /&gt;
-- %s = affected&lt;br /&gt;
-- %d = given (number of addresses given in input covered by this range)&lt;br /&gt;
-- %s = IP address range&lt;br /&gt;
-- %s = empty string&lt;br /&gt;
&lt;br /&gt;
sumnext = [=[&lt;br /&gt;
             %-12s %-11d %s%s]=],&lt;br /&gt;
-- %s = affected&lt;br /&gt;
-- %d = given&lt;br /&gt;
-- %s = IP address range&lt;br /&gt;
-- %s = empty string&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-- Strings for results using a table in wikitext.&lt;br /&gt;
local wikitable = {&lt;br /&gt;
&lt;br /&gt;
header = [=[&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Gesamtanzahl&amp;lt;br /&amp;gt;Adressen!! Betroffene&amp;lt;br /&amp;gt;Adressen !! Angegebene&amp;lt;br /&amp;gt;Adressen !! Range !! Beiträge]=],&lt;br /&gt;
&lt;br /&gt;
footer = &amp;#039;|}&amp;#039;,&lt;br /&gt;
&lt;br /&gt;
sumfirst = [=[&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;5&amp;quot; style=&amp;quot;border: 2px solid darkgray; padding:0;&amp;quot;|&lt;br /&gt;
|- style=&amp;quot;vertical-align: top;&amp;quot;&lt;br /&gt;
|rowspan=&amp;quot;%s&amp;quot; |%s ||%s ||%d ||%s ||%s]=],&lt;br /&gt;
-- %s = string of number of ranges in summary (number of rows)&lt;br /&gt;
-- %s = total affected&lt;br /&gt;
-- %s = affected&lt;br /&gt;
-- %d = given&lt;br /&gt;
-- %s = IP address range&lt;br /&gt;
-- %s = contributions link&lt;br /&gt;
&lt;br /&gt;
sumnext = [=[&lt;br /&gt;
|-&lt;br /&gt;
|%s ||%d ||%s ||%s]=],&lt;br /&gt;
-- %s = affected&lt;br /&gt;
-- %d = given&lt;br /&gt;
-- %s = IP address range&lt;br /&gt;
-- %s = contributions link&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
-------------------------- Code section ---------------------------------&lt;br /&gt;
&lt;br /&gt;
local bit32 = require(&amp;#039;bit32&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
local Collection  -- a table to hold items&lt;br /&gt;
Collection = {&lt;br /&gt;
	add = function (self, item)&lt;br /&gt;
		if item ~= nil then&lt;br /&gt;
			self.n = self.n + 1&lt;br /&gt;
			self[self.n] = item&lt;br /&gt;
		end&lt;br /&gt;
	end,&lt;br /&gt;
	join = function (self, sep)&lt;br /&gt;
		return table.concat(self, sep)&lt;br /&gt;
	end,&lt;br /&gt;
	remove = function (self, pos)&lt;br /&gt;
		if self.n &amp;gt; 0 and (pos == nil or (0 &amp;lt; pos and pos &amp;lt;= self.n)) then&lt;br /&gt;
			self.n = self.n - 1&lt;br /&gt;
			return table.remove(self, pos)&lt;br /&gt;
		end&lt;br /&gt;
	end,&lt;br /&gt;
	sort = function (self, comp)&lt;br /&gt;
		table.sort(self, comp)&lt;br /&gt;
	end,&lt;br /&gt;
	new = function ()&lt;br /&gt;
		return setmetatable({n = 0}, Collection)&lt;br /&gt;
	end&lt;br /&gt;
}&lt;br /&gt;
Collection.__index = Collection&lt;br /&gt;
&lt;br /&gt;
local function empty(text)&lt;br /&gt;
	-- Return true if text is nil or empty (assuming a string).&lt;br /&gt;
	return text == nil or text == &amp;#039;&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function describe_total(total, isalloc)&lt;br /&gt;
	-- Return text describing given number of addresses or /64 allocations.&lt;br /&gt;
	if total &amp;lt;= 9999 then&lt;br /&gt;
		-- Can have fractions if total is the number of /64 allocations.&lt;br /&gt;
		if total &amp;lt; 9 then&lt;br /&gt;
			return (string.format(&amp;#039;%.1f&amp;#039;, total):gsub(&amp;#039;%.0$&amp;#039;, &amp;#039;&amp;#039;))&lt;br /&gt;
		end&lt;br /&gt;
		return string.format(&amp;#039;%.0f&amp;#039;, total)&lt;br /&gt;
	end&lt;br /&gt;
	if not isalloc then&lt;br /&gt;
		local alloc = 2^64&lt;br /&gt;
		if total &amp;gt;= alloc then&lt;br /&gt;
			return describe_total(total / alloc, true) .. &amp;#039; /64&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	total = total/1024&lt;br /&gt;
	local suffix = &amp;#039;K&amp;#039;&lt;br /&gt;
	if total &amp;gt;= 1024 then&lt;br /&gt;
		total = total/1024&lt;br /&gt;
		suffix = &amp;#039;M&amp;#039;&lt;br /&gt;
		if total &amp;gt;= 1024 then&lt;br /&gt;
			total = total/1024&lt;br /&gt;
			suffix = &amp;#039;G&amp;#039;&lt;br /&gt;
			if total &amp;gt; 64 then&lt;br /&gt;
				return &amp;#039;&amp;gt;64G&amp;#039;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return string.format(&amp;#039;%.0f&amp;#039;, total) .. suffix&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function describe_size(ipsize, size)&lt;br /&gt;
	-- Return text describing how many IPs are in a range with size = prefix length.&lt;br /&gt;
	local function numtext(n)&lt;br /&gt;
		if n &amp;lt;= 16 then&lt;br /&gt;
			return tostring(2^n)&lt;br /&gt;
		end&lt;br /&gt;
		if n &amp;lt;= 19 then&lt;br /&gt;
			return tostring(2^(n - 10)) .. &amp;#039;K&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
		if n &amp;lt;= 29 then&lt;br /&gt;
			return tostring(2^(n - 20)) .. &amp;#039;M&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
		if n &amp;lt;= 36 then&lt;br /&gt;
			return tostring(2^(n - 30)) .. &amp;#039;G&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
		return &amp;#039;&amp;gt;64G&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	local host = ipsize - size&lt;br /&gt;
	if host &amp;lt;= 32 then&lt;br /&gt;
		-- IPv4 or IPv6.&lt;br /&gt;
		return numtext(host)&lt;br /&gt;
	end&lt;br /&gt;
	-- Must be IPv6.&lt;br /&gt;
	if host &amp;lt;= 64 then&lt;br /&gt;
		local s = ({&lt;br /&gt;
			[64] = &amp;#039;1&amp;#039;,  [63] = &amp;#039;50%&amp;#039;, [62] = &amp;#039;25%&amp;#039;, [61] = &amp;#039;12%&amp;#039;,&lt;br /&gt;
			[60] = &amp;#039;6%&amp;#039;, [59] = &amp;#039;3%&amp;#039;,  [58] = &amp;#039;2%&amp;#039;&lt;br /&gt;
		})[host] or &amp;#039;&amp;lt;1%&amp;#039;&lt;br /&gt;
		return s .. &amp;#039; /64&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
	-- IPv6 with size &amp;lt; 64.&lt;br /&gt;
	return numtext(host - 64) .. &amp;#039; /64&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ipv6_string(ip)&lt;br /&gt;
	-- Return a string equivalent to the given IPv6 address.&lt;br /&gt;
	local z1, z2  -- indices of run of zeros to be displayed as &amp;quot;::&amp;quot;&lt;br /&gt;
	local zstart, zcount&lt;br /&gt;
	for i = 1, 9 do&lt;br /&gt;
		-- Find left-most occurrence of longest run of two or more zeros.&lt;br /&gt;
		if i &amp;lt; 9 and ip[i] == 0 then&lt;br /&gt;
			if zstart then&lt;br /&gt;
				zcount = zcount + 1&lt;br /&gt;
			else&lt;br /&gt;
				zstart = i&lt;br /&gt;
				zcount = 1&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			if zcount and zcount &amp;gt; 1 then&lt;br /&gt;
				if not z1 or zcount &amp;gt; z2 - z1 + 1 then&lt;br /&gt;
					z1 = zstart&lt;br /&gt;
					z2 = zstart + zcount - 1&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			zstart = nil&lt;br /&gt;
			zcount = nil&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local parts = Collection.new()&lt;br /&gt;
	for i = 1, 8 do&lt;br /&gt;
		if z1 and z1 &amp;lt;= i and i &amp;lt;= z2 then&lt;br /&gt;
			if i == z1 then&lt;br /&gt;
				if z1 == 1 or z2 == 8 then&lt;br /&gt;
					if z1 == 1 and z2 == 8 then&lt;br /&gt;
						return &amp;#039;::&amp;#039;&lt;br /&gt;
					end&lt;br /&gt;
					parts:add(&amp;#039;:&amp;#039;)&lt;br /&gt;
				else&lt;br /&gt;
					parts:add(&amp;#039;&amp;#039;)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			parts:add(string.format(&amp;#039;%x&amp;#039;, ip[i]))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return parts:join(&amp;#039;:&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ip_string(ip)&lt;br /&gt;
	-- Return a string equivalent to given IP address (IPv4 or IPv6).&lt;br /&gt;
	if ip.n == 2 then&lt;br /&gt;
		-- IPv4.&lt;br /&gt;
		local parts = {}&lt;br /&gt;
		for i = 1, 2 do&lt;br /&gt;
			local w = ip[i]&lt;br /&gt;
			local q = i == 1 and 1 or 3&lt;br /&gt;
			parts[q] = math.floor(w / 256)&lt;br /&gt;
			parts[q+1] = w % 256&lt;br /&gt;
		end&lt;br /&gt;
		return table.concat(parts, &amp;#039;.&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	return ipv6_string(ip)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Metatable for some operations on IP addresses.&lt;br /&gt;
local ipmt = {&lt;br /&gt;
	__eq = function (lhs, rhs)&lt;br /&gt;
		-- Return true if values in numbered tables match.&lt;br /&gt;
		if lhs.n == rhs.n then&lt;br /&gt;
			for i = 1, lhs.n do&lt;br /&gt;
				if lhs[i] ~= rhs[i] then&lt;br /&gt;
					return false&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
		return false&lt;br /&gt;
	end,&lt;br /&gt;
	__lt = function (lhs, rhs)&lt;br /&gt;
		-- Return true if lhs &amp;lt; rhs; for sort.&lt;br /&gt;
		if lhs.n == rhs.n then&lt;br /&gt;
			for i = 1, lhs.n do&lt;br /&gt;
				if lhs[i] ~= rhs[i] then&lt;br /&gt;
					return lhs[i] &amp;lt; rhs[i]&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return false&lt;br /&gt;
		end&lt;br /&gt;
		return lhs.n &amp;lt; rhs.n  -- sort IPv4 before IPv6, although not needed&lt;br /&gt;
	end,&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function ipv4_address(ip_str)&lt;br /&gt;
	-- Return a collection of two 16-bit words (numbers) equivalent&lt;br /&gt;
	-- to the IPv4 address given as a quad-dotted string, or&lt;br /&gt;
	-- return nil if invalid.&lt;br /&gt;
	-- This representation is for compatibility with IPv6 addresses.&lt;br /&gt;
	local parts = Collection.new()&lt;br /&gt;
	local s = ip_str:match(&amp;#039;^%s*(.-)%s*$&amp;#039;) .. &amp;#039;.&amp;#039;&lt;br /&gt;
	for item in s:gmatch(&amp;#039;(.-)%.&amp;#039;) do&lt;br /&gt;
		parts:add(item)&lt;br /&gt;
	end&lt;br /&gt;
	if parts.n == 4 then&lt;br /&gt;
		for i, s in ipairs(parts) do&lt;br /&gt;
			if s:match(&amp;#039;^%d+$&amp;#039;) then&lt;br /&gt;
				local num = tonumber(s)&lt;br /&gt;
				if 0 &amp;lt;= num and num &amp;lt;= 255 then&lt;br /&gt;
					if num &amp;gt; 0 and s:match(&amp;#039;^0&amp;#039;) then&lt;br /&gt;
						-- A redundant leading zero is an error because it is for an IP in octal.&lt;br /&gt;
						return nil&lt;br /&gt;
					end&lt;br /&gt;
					parts[i] = num&lt;br /&gt;
				else&lt;br /&gt;
					return nil&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				return nil&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		local result = Collection.new()&lt;br /&gt;
		for i = 1, 3, 2 do&lt;br /&gt;
			result:add(parts[i] * 256 + parts[i+1])&lt;br /&gt;
		end&lt;br /&gt;
		return setmetatable(result, ipmt)&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ipv6_address(ip_str)&lt;br /&gt;
	-- Return a collection of eight 16-bit words (numbers) equivalent&lt;br /&gt;
	-- to the IPv6 address given as a colon-delimited string, or&lt;br /&gt;
	-- return nil if invalid.&lt;br /&gt;
	local _, n = ip_str:gsub(&amp;#039;:&amp;#039;, &amp;#039;:&amp;#039;)&lt;br /&gt;
	if n &amp;lt; 7 then&lt;br /&gt;
		ip_str, n = ip_str:gsub(&amp;#039;::&amp;#039;, string.rep(&amp;#039;:&amp;#039;, 9 - n))&lt;br /&gt;
	end&lt;br /&gt;
	local parts = Collection.new()&lt;br /&gt;
	for item in (ip_str .. &amp;#039;:&amp;#039;):gmatch(&amp;#039;(.-):&amp;#039;) do&lt;br /&gt;
		parts:add(item)&lt;br /&gt;
	end&lt;br /&gt;
	if parts.n == 8 then&lt;br /&gt;
		for i, s in ipairs(parts) do&lt;br /&gt;
			if s == &amp;#039;&amp;#039; then&lt;br /&gt;
				parts[i] = 0&lt;br /&gt;
			else&lt;br /&gt;
				local num = tonumber(&amp;#039;0x&amp;#039; .. s)&lt;br /&gt;
				if num and 0 &amp;lt;= num and num &amp;lt;= 65535 then&lt;br /&gt;
					parts[i] = num&lt;br /&gt;
				else&lt;br /&gt;
					return nil&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		return setmetatable(parts, ipmt)&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function common_length(num1, num2, nr_bits)&lt;br /&gt;
	-- Return number of prefix bits that two integers have in common.&lt;br /&gt;
	-- Number of bits in each number is nr_bits = 16, 8, 4, 2 or 1.&lt;br /&gt;
	if nr_bits &amp;lt;= 1 then&lt;br /&gt;
		return num1 == num2 and 1 or 0&lt;br /&gt;
	end&lt;br /&gt;
	local half = nr_bits / 2&lt;br /&gt;
	local splitter = 2^half&lt;br /&gt;
	local upper1, lower1 = math.modf(num1 / splitter)&lt;br /&gt;
	local upper2, lower2 = math.modf(num2 / splitter)&lt;br /&gt;
	if upper1 == upper2 then&lt;br /&gt;
		lower1 = math.floor(lower1 * splitter + 0.5)&lt;br /&gt;
		lower2 = math.floor(lower2 * splitter + 0.5)&lt;br /&gt;
		return half + common_length(lower1, lower2, half)&lt;br /&gt;
	end&lt;br /&gt;
	return common_length(upper1, upper2, half)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function common_prefix_length(ip1, ip2)&lt;br /&gt;
	-- Return number of prefix bits that two IPs have in common.&lt;br /&gt;
	-- Caller ensures that both IPs are IPv4 or both are IPv6.&lt;br /&gt;
	local size = 0&lt;br /&gt;
	for i = 1, ip1.n do&lt;br /&gt;
		local w1, w2 = ip1[i], ip2[i]&lt;br /&gt;
		if w1 == w2 then&lt;br /&gt;
			size = size + 16&lt;br /&gt;
		else&lt;br /&gt;
			return size + common_length(w1, w2, 16)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return size&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ip_prefix(ip, length)&lt;br /&gt;
	-- Return a copy of ip masked to contain only the prefix of given length.&lt;br /&gt;
	local result = { n = ip.n }&lt;br /&gt;
	for i = 1, ip.n do&lt;br /&gt;
		if length &amp;gt; 0 then&lt;br /&gt;
			if length &amp;gt;= 16 then&lt;br /&gt;
				result[i] = ip[i]&lt;br /&gt;
				length = length - 16&lt;br /&gt;
			else&lt;br /&gt;
				result[i] = bit32.band(ip[i], bit32.arshift(0xffff8000, length - 1))&lt;br /&gt;
				length = 0&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			result[i] = 0&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return setmetatable(result, ipmt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function ip_incremented(ip)&lt;br /&gt;
	-- Return a new IP equal to ip + 1.&lt;br /&gt;
	-- Will wraparound (255.255.255.255 + 1 = 0.0.0.0)!&lt;br /&gt;
	local result = { n = ip.n }&lt;br /&gt;
	local carry = 1&lt;br /&gt;
	for i = ip.n, 1, -1 do&lt;br /&gt;
		local sum = ip[i] + carry&lt;br /&gt;
		if sum &amp;gt;= 0x10000 then&lt;br /&gt;
			carry = 1&lt;br /&gt;
			sum = sum - 0x10000&lt;br /&gt;
		else&lt;br /&gt;
			carry = 0&lt;br /&gt;
		end&lt;br /&gt;
		result[i] = sum&lt;br /&gt;
	end&lt;br /&gt;
	return setmetatable(result, ipmt)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function is_next_ip(ip1, ip2)&lt;br /&gt;
	-- Return true if ip2 is the next IP after ip1 (ip2 == ip1 + 1).&lt;br /&gt;
	-- IPs are sorted and unique so ip1 &amp;lt; ip2 and can ignore wrapping to zero.&lt;br /&gt;
	-- This is lower overhead than making a new incremented IP then comparing.&lt;br /&gt;
	if ip1 and ip2 then&lt;br /&gt;
		local carry = 1&lt;br /&gt;
		for i = ip1.n, 1, -1 do&lt;br /&gt;
			local sum = ip1[i] + carry&lt;br /&gt;
			if sum &amp;gt;= 0x10000 then&lt;br /&gt;
				carry = 1&lt;br /&gt;
				sum = sum - 0x10000&lt;br /&gt;
			else&lt;br /&gt;
				carry = 0&lt;br /&gt;
			end&lt;br /&gt;
			if sum ~= ip2[i] then&lt;br /&gt;
				return false&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Each IP in a range except for the last IP has a &amp;#039;common&amp;#039; field which is&lt;br /&gt;
-- a number specifying how many bits are common between the prefixes of this&lt;br /&gt;
-- IP and the next IP (0 if this IP starts with 0 and the next starts with 1).&lt;br /&gt;
-- Each non-empty range has exactly one &amp;quot;minimum common&amp;quot;, that is, its value&lt;br /&gt;
-- of common is smaller than all others. That there is only one minimum common&lt;br /&gt;
-- follows from the fact that the IPs are unique and sorted.&lt;br /&gt;
local function make_range(iplist, ifirst, ilast)&lt;br /&gt;
	-- Return a table for the range of IPs from iplist[ifirst] to iplist[ilast] inclusive.&lt;br /&gt;
	local imin, vmin, done&lt;br /&gt;
	if ifirst &amp;lt; ilast then&lt;br /&gt;
		for i = ifirst, ilast - 1 do&lt;br /&gt;
			-- Find the (unique) minimum of common lengths.&lt;br /&gt;
			local common = iplist[i].common&lt;br /&gt;
			if vmin then&lt;br /&gt;
				if vmin &amp;gt; common then&lt;br /&gt;
					vmin = common&lt;br /&gt;
					imin = i&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				vmin = common&lt;br /&gt;
				imin = i&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		vmin = iplist.ipsize&lt;br /&gt;
		imin = ifirst&lt;br /&gt;
		done = true&lt;br /&gt;
	end&lt;br /&gt;
	if vmin &amp;gt; iplist.allocation then&lt;br /&gt;
		-- For IPv6, the default allocation is /64 and there is no point having&lt;br /&gt;
		-- more precise ranges as they add unnecessary complexity.&lt;br /&gt;
		-- However, using results=all sets allocation = 128 so vmin is not changed.&lt;br /&gt;
		vmin = iplist.allocation&lt;br /&gt;
	end&lt;br /&gt;
	return {&lt;br /&gt;
		ifirst = ifirst,  -- index of first IP&lt;br /&gt;
		ilast = ilast,    -- index of last IP&lt;br /&gt;
		imin = imin,      -- index of IP with minimum common&lt;br /&gt;
		size = vmin,      -- number of common bits in prefix (the minimum)&lt;br /&gt;
		prefix = ip_prefix(iplist[imin], vmin),  -- IP table of the base IP&lt;br /&gt;
		done = done,      -- true if know that this range cannot be improved&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function split_range(iplist, range, depth)&lt;br /&gt;
	-- Return a table of two or more ranges that more precisely target&lt;br /&gt;
	-- the IPs in range, or return nothing if unable to improve range.&lt;br /&gt;
	depth = depth and depth + 1 or 0&lt;br /&gt;
	if depth &amp;lt;= 20 and  -- 20 examines 1M contiguous addresses down to individual IPs&lt;br /&gt;
			not range.done and&lt;br /&gt;
			range.size &amp;lt; iplist.allocation and&lt;br /&gt;
			range.ifirst &amp;lt; range.ilast&lt;br /&gt;
	then&lt;br /&gt;
		local imin = range.imin&lt;br /&gt;
		assert(imin and range.ifirst &amp;lt;= imin and imin &amp;lt; range.ilast)&lt;br /&gt;
		local r1 = make_range(iplist, range.ifirst, range.imin)&lt;br /&gt;
		local r2 = make_range(iplist, range.imin + 1, range.ilast)&lt;br /&gt;
		local pointless = range.size + 1&lt;br /&gt;
		if r1.size &amp;gt; pointless or r2.size &amp;gt; pointless then&lt;br /&gt;
			return { r1, r2 }&lt;br /&gt;
		end&lt;br /&gt;
		local result = Collection.new()&lt;br /&gt;
		local function store_split(range)&lt;br /&gt;
			local split = split_range(iplist, range, depth)&lt;br /&gt;
			if split then&lt;br /&gt;
				for _, r in ipairs(split) do&lt;br /&gt;
					result:add(r)&lt;br /&gt;
				end&lt;br /&gt;
				return true&lt;br /&gt;
			else&lt;br /&gt;
				result:add(range)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		local improved1 = store_split(r1)&lt;br /&gt;
		local improved2 = store_split(r2)&lt;br /&gt;
		if improved1 or improved2 then&lt;br /&gt;
			return result&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	range.done = true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function better_summary(iplist, summary)&lt;br /&gt;
	-- Return a better summary that more precisely targets the specified IPs,&lt;br /&gt;
	-- or return nil if unable to improve the summary.&lt;br /&gt;
	local better = Collection.new()&lt;br /&gt;
	local improved&lt;br /&gt;
	for _, range in ipairs(summary) do&lt;br /&gt;
		local split = split_range(iplist, range)&lt;br /&gt;
		if split then&lt;br /&gt;
			improved = true&lt;br /&gt;
			for _, r in ipairs(split) do&lt;br /&gt;
				better:add(r)&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			better:add(range)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return improved and better&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function make_summaries(iplist)&lt;br /&gt;
	-- Return a collection where each item is a summary.&lt;br /&gt;
	-- A summary is a table of one or more ranges.&lt;br /&gt;
	-- A summary covers all the given IPs and probably more.&lt;br /&gt;
	-- A range is a table representing a CIDR block such as 1.2.248.0/21.&lt;br /&gt;
	-- The first summary found is a single range; each subsequent summary&lt;br /&gt;
	-- (if any) uses more ranges to better target the given IPs.&lt;br /&gt;
	-- The result omits any summary with a range size that is too small (too many IPs).&lt;br /&gt;
	local function good_size(summary)&lt;br /&gt;
		for _, range in ipairs(summary) do&lt;br /&gt;
			if range.size &amp;lt; iplist.minsize then&lt;br /&gt;
				return false&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
	local summaries = Collection.new()&lt;br /&gt;
	if iplist.n &amp;gt; 0 then&lt;br /&gt;
		for i = 1, iplist.n - 1 do&lt;br /&gt;
			-- Set length of prefixes common between each pair of IPs.&lt;br /&gt;
			iplist[i].common = common_prefix_length(iplist[i], iplist[i+1])&lt;br /&gt;
		end&lt;br /&gt;
		local summary = { make_range(iplist, 1, iplist.n) }&lt;br /&gt;
		while summary and summaries.n &amp;lt; iplist.maxresults do&lt;br /&gt;
			if good_size(summary) then&lt;br /&gt;
				summaries:add(summary)&lt;br /&gt;
			end&lt;br /&gt;
			summary = better_summary(iplist, summary)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return summaries&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function extract_ipv4(result, omitted, line)&lt;br /&gt;
	-- Extract any IPv4 addresses from given line or throw error.&lt;br /&gt;
	-- Accept CIDR /n to specify a range (only accept 16 to 32).&lt;br /&gt;
	-- Addresses must be delimited with whitespace to reduce false positives.&lt;br /&gt;
	local function store(hit)&lt;br /&gt;
		local n = 32&lt;br /&gt;
		local lhs, rhs = hit:match(&amp;#039;^(.-)/(%d+)$&amp;#039;)&lt;br /&gt;
		if lhs then&lt;br /&gt;
			hit = lhs&lt;br /&gt;
			n = tonumber(rhs)&lt;br /&gt;
			if not (n and 16 &amp;lt;= n and n &amp;lt;= 32) then&lt;br /&gt;
				error(messages.cidr_error .. lhs .. &amp;#039;/&amp;#039; .. rhs, 0)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		local ip = ipv4_address(hit)&lt;br /&gt;
		if ip then&lt;br /&gt;
			if n == 32 then&lt;br /&gt;
				result:add(ip)&lt;br /&gt;
			else&lt;br /&gt;
				if ip ~= ip_prefix(ip, n) then&lt;br /&gt;
					error(messages.invalid_base .. hit, 0)&lt;br /&gt;
				end&lt;br /&gt;
				for _ = 1, 2^(32 - n) do&lt;br /&gt;
					result:add(ip)&lt;br /&gt;
					ip = ip_incremented(ip)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			omitted:add(hit)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	line = line:gsub(&amp;#039;:&amp;#039;, &amp;#039; &amp;#039;)  -- so wikitext indents or other colons don&amp;#039;t obscure an IVp4 address&lt;br /&gt;
	for hit in line:gmatch(&amp;#039;%S+&amp;#039;) do&lt;br /&gt;
		if hit:match(&amp;#039;^%d+%.%d+[%.%d/]+$&amp;#039;) then&lt;br /&gt;
			local _, n = hit:gsub(&amp;#039;%.&amp;#039;, &amp;#039;.&amp;#039;)&lt;br /&gt;
			if n &amp;gt;= 3 then&lt;br /&gt;
				store(hit)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function extract_ipv6(result, omitted, line)&lt;br /&gt;
	-- Extract any IPv6 addresses from given line or throw error.&lt;br /&gt;
	-- Addresses must be delimited with whitespace to reduce false positives.&lt;br /&gt;
	-- Want to accept all valid IPv6 despite the fact that contributors will&lt;br /&gt;
	-- not have an address starting with &amp;#039;:&amp;#039;.&lt;br /&gt;
	-- Also want to be able to parse arbitrary wikitext which might use colons&lt;br /&gt;
	-- for indenting. To achieve that, if an address at the start of a line&lt;br /&gt;
	-- is valid, use it; otherwise strip any leading colons and try again.&lt;br /&gt;
	for pos, hit in line:gmatch(&amp;#039;()(%S+)&amp;#039;) do&lt;br /&gt;
		local ipstr, length = hit:match(&amp;#039;^([:%x]+)(/?%d*)$&amp;#039;)&lt;br /&gt;
		if ipstr then&lt;br /&gt;
			local _, n = ipstr:gsub(&amp;#039;:&amp;#039;, &amp;#039;:&amp;#039;)&lt;br /&gt;
			if n &amp;gt;= 2 then&lt;br /&gt;
				local ip = ipv6_address(ipstr)&lt;br /&gt;
				if not ip and pos == 1 then&lt;br /&gt;
					ipstr, n = ipstr:gsub(&amp;#039;^:+&amp;#039;, &amp;#039;&amp;#039;)&lt;br /&gt;
					if n &amp;gt; 0 then&lt;br /&gt;
						ip = ipv6_address(ipstr)&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				if ip then&lt;br /&gt;
					if length and #length &amp;gt; 0 then&lt;br /&gt;
						error(messages.cidr_unsupported .. hit, 0)&lt;br /&gt;
					end&lt;br /&gt;
					result:add(ip)&lt;br /&gt;
				else&lt;br /&gt;
					omitted:add(hit)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function contribs(address, strings, ipbase, size)&lt;br /&gt;
	-- Return a URL or wikilink to list the contributions for an IP or IP range,&lt;br /&gt;
	-- or return an empty string if cannot do anything useful.&lt;br /&gt;
	-- The given address is a string of either a single IP or a CIDR range.&lt;br /&gt;
	local encoded, count = address:gsub(&amp;#039;/&amp;#039;, &amp;#039;%%2F&amp;#039;)&lt;br /&gt;
	return &amp;#039;[[Special:Contributions/&amp;#039; .. address .. &amp;#039;|&amp;#039;..messages.contribs..&amp;#039;]]&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function show_summary(lines, strings, iplist, summary)&lt;br /&gt;
	-- Show the summary by adding table wikitext or plain text to lines.&lt;br /&gt;
	local want_plain = iplist.want_plain&lt;br /&gt;
	local total = 0&lt;br /&gt;
	for _, range in ipairs(summary) do&lt;br /&gt;
		-- A number is a double which easily handles 2^128 = 3.4e38.&lt;br /&gt;
		total = total + 2^(iplist.ipsize - range.size)&lt;br /&gt;
	end&lt;br /&gt;
	for i, range in ipairs(summary) do&lt;br /&gt;
		local prefix = ip_string(range.prefix)&lt;br /&gt;
		local size = range.size&lt;br /&gt;
		local affected = describe_size(iplist.ipsize, size)&lt;br /&gt;
		local given = range.ilast - range.ifirst + 1&lt;br /&gt;
		local address&lt;br /&gt;
		local link = &amp;#039;&amp;#039;&lt;br /&gt;
		if size == iplist.ipsize then&lt;br /&gt;
			address = prefix&lt;br /&gt;
			if not want_plain then&lt;br /&gt;
				link = contribs(address, strings)&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			address = prefix .. &amp;#039;/&amp;#039; .. size&lt;br /&gt;
			if not want_plain then&lt;br /&gt;
				link = contribs(address, strings, range.prefix, size)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		local s&lt;br /&gt;
		if i == 1 then&lt;br /&gt;
			s = string.format(strings.sumfirst,&lt;br /&gt;
					want_plain and &amp;#039;&amp;#039; or tostring(#summary),&lt;br /&gt;
					describe_total(total),&lt;br /&gt;
					affected, given, address, link)&lt;br /&gt;
		else&lt;br /&gt;
			s = string.format(strings.sumnext,&lt;br /&gt;
					affected, given, address, link)&lt;br /&gt;
		end&lt;br /&gt;
		-- Pre tags returned by a module are html tags, not like wikitext &amp;lt;pre&amp;gt;...&amp;lt;/pre&amp;gt;.&lt;br /&gt;
		lines:add(want_plain and mw.text.nowiki(s) or s)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function process_ips(lines, iplist, omitted)&lt;br /&gt;
	-- Process a list of IP addresses, adding text of results to lines.&lt;br /&gt;
	-- The list should contain either all IPv4 addresses, or all IPv6 (not a mixture).&lt;br /&gt;
	local seq1, seq2, seqmany&lt;br /&gt;
	local function show_sequence()&lt;br /&gt;
		if seq1 and seq2 then&lt;br /&gt;
			local text = ip_string(seq1)&lt;br /&gt;
			if seqmany then&lt;br /&gt;
				seqmany = false&lt;br /&gt;
				text = text .. &amp;#039; – &amp;#039; .. ip_string(seq2)&lt;br /&gt;
			end&lt;br /&gt;
			seq1 = nil&lt;br /&gt;
			seq2 = nil&lt;br /&gt;
			local markup = text:sub(1, 1) == &amp;#039;:&amp;#039; and &amp;#039;:&amp;lt;nowiki/&amp;gt;&amp;#039; or &amp;#039;:&amp;#039;&lt;br /&gt;
			lines:add(markup .. text)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local function show_ip(ip)&lt;br /&gt;
		-- Show IP or record it to be included in a &amp;quot;from to&amp;quot; sequence of IPs.&lt;br /&gt;
		if is_next_ip(seq2, ip) then&lt;br /&gt;
			seq2 = ip&lt;br /&gt;
			seqmany = true&lt;br /&gt;
		else&lt;br /&gt;
			show_sequence()&lt;br /&gt;
			seq1 = ip&lt;br /&gt;
			seq2 = ip&lt;br /&gt;
			seqmany = false&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if iplist.n &amp;lt; 1 then&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
	if lines.n &amp;gt; 0 then&lt;br /&gt;
		lines:add(&amp;#039;&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	if omitted.n &amp;gt; 0 then&lt;br /&gt;
		lines:add(messages.omitted .. omitted:join(&amp;#039; &amp;#039;))&lt;br /&gt;
		lines:add(&amp;#039;&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
	local heading_line&lt;br /&gt;
	if not iplist.nolist then&lt;br /&gt;
		lines:add(&amp;#039;&amp;#039;)  -- this blank line is replaced with a heading&lt;br /&gt;
		heading_line = lines.n&lt;br /&gt;
	end&lt;br /&gt;
	local duplicates = Collection.new()&lt;br /&gt;
	local previous&lt;br /&gt;
	iplist:sort()&lt;br /&gt;
	-- Check for duplicates which can interfere with method to get ranges.&lt;br /&gt;
	for i, ip in ipairs(iplist) do&lt;br /&gt;
		if previous == ip then&lt;br /&gt;
			duplicates:add(i)  -- index to omit duplicate later&lt;br /&gt;
		elseif not iplist.nolist then&lt;br /&gt;
			show_ip(ip)&lt;br /&gt;
		end&lt;br /&gt;
		previous = ip&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	show_sequence()&lt;br /&gt;
	local duplicate_text = &amp;#039;&amp;#039;&lt;br /&gt;
	if duplicates.n &amp;gt; 0 then&lt;br /&gt;
		duplicate_text = messages.duplicates&lt;br /&gt;
		for i = duplicates.n, 1, -1 do&lt;br /&gt;
			iplist:remove(duplicates[i])&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
  local heading_text = &amp;#039;&amp;#039;&lt;br /&gt;
  if iplist.n == 1 then&lt;br /&gt;
    heading_text = string.format(messages.ip_address_sorted,iplist.ipname)&lt;br /&gt;
  else&lt;br /&gt;
    heading_text = string.format(messages.ip_addresses_sorted,iplist.n,iplist.ipname)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
	if heading_line then&lt;br /&gt;
		lines[heading_line] = heading_text .. duplicate_text .. &amp;#039;:&amp;#039;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local strings = iplist.want_plain and plaintext or wikitable&lt;br /&gt;
	lines:add(strings.header)&lt;br /&gt;
	local upto = lines.n&lt;br /&gt;
	for _, summary in ipairs(make_summaries(iplist)) do&lt;br /&gt;
		show_summary(lines, strings, iplist, summary)&lt;br /&gt;
	end&lt;br /&gt;
	lines:add(strings.footer)&lt;br /&gt;
	if upto + 1 == lines.n then&lt;br /&gt;
		-- Show message in the very unlikely event that no results are found.&lt;br /&gt;
		lines:add(&amp;#039;----&amp;#039;)&lt;br /&gt;
		lines:add(messages.no_ranges_found)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function make_options(args)&lt;br /&gt;
	-- Return table of options from validated args or throw error.&lt;br /&gt;
	local options = {}&lt;br /&gt;
	if not empty(args.comment) then&lt;br /&gt;
		options.comment = args.comment&lt;br /&gt;
	end&lt;br /&gt;
	local allocation&lt;br /&gt;
	if not empty(args.allocation) then&lt;br /&gt;
		allocation = tonumber(args.allocation)&lt;br /&gt;
		if not (allocation and 48 &amp;lt;= allocation and allocation &amp;lt;= 128) then&lt;br /&gt;
			error(messages.invalid_allocation .. args.allocation, 0)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local maxresults&lt;br /&gt;
	if not empty(args.results) then&lt;br /&gt;
		if args.results == &amp;#039;all&amp;#039; then&lt;br /&gt;
			options.all = true&lt;br /&gt;
			allocation = allocation or 128&lt;br /&gt;
			maxresults = 1000&lt;br /&gt;
		else&lt;br /&gt;
			maxresults = tonumber(args.results)&lt;br /&gt;
			if not (maxresults and 1 &amp;lt;= maxresults and maxresults &amp;lt;= 100) then&lt;br /&gt;
				error(messages.invalid_results .. args.results, 0)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	options.allocation = allocation or 64&lt;br /&gt;
	options.maxresults = maxresults or 10&lt;br /&gt;
	local keywords = {&lt;br /&gt;
		-- Table of k, v strings.&lt;br /&gt;
		-- If an argument matches k, an option named v is set to true.&lt;br /&gt;
		nolist = &amp;#039;nolist&amp;#039;,&lt;br /&gt;
		text = &amp;#039;text&amp;#039;,&lt;br /&gt;
	}&lt;br /&gt;
	for i, arg in ipairs(args) do&lt;br /&gt;
		local flag = keywords[arg:match(&amp;#039;^%s*(%w+)%s*$&amp;#039;)]&lt;br /&gt;
		if flag then&lt;br /&gt;
			options[i] = &amp;#039;skip&amp;#039;&lt;br /&gt;
			options[flag] = true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return options&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
IPrange.textSurvey = function ( args )&lt;br /&gt;
	-- Process given args&lt;br /&gt;
	-- Throw an error if need to report a problem.&lt;br /&gt;
	local options = make_options(args)&lt;br /&gt;
	local v4list, v4omitted = Collection.new(), Collection.new()&lt;br /&gt;
	local v6list, v6omitted = Collection.new(), Collection.new()&lt;br /&gt;
	v4list.ipsize = 32&lt;br /&gt;
	v4list.ipname = &amp;#039;IPv4&amp;#039;&lt;br /&gt;
	v6list.ipsize = 128&lt;br /&gt;
	v6list.ipname = &amp;#039;IPv6&amp;#039;&lt;br /&gt;
	v4list.allocation = 32&lt;br /&gt;
	v6list.allocation = options.allocation&lt;br /&gt;
	if options.all then&lt;br /&gt;
		v4list.minsize = 0&lt;br /&gt;
		v6list.minsize = 0&lt;br /&gt;
	else&lt;br /&gt;
		v4list.minsize = 16  -- cannot block more IPs than /16 for IPv4&lt;br /&gt;
		v6list.minsize = 19  -- or /19 for IPv6 ($wgBlockCIDRLimit)&lt;br /&gt;
	end&lt;br /&gt;
	for _, k in ipairs({&amp;#039;maxresults&amp;#039;, &amp;#039;nolist&amp;#039;}) do&lt;br /&gt;
		v4list[k] = options[k]&lt;br /&gt;
		v6list[k] = options[k]&lt;br /&gt;
	end&lt;br /&gt;
	if options.text then&lt;br /&gt;
		v4list.want_plain = true&lt;br /&gt;
		v6list.want_plain = true&lt;br /&gt;
	end&lt;br /&gt;
	for i, arg in ipairs(args) do&lt;br /&gt;
		if options[i] ~= &amp;#039;skip&amp;#039; then&lt;br /&gt;
			for line in string.gmatch(arg .. &amp;#039;\n&amp;#039;, &amp;#039;[\t ]*(.-)[\t\r ]*\n&amp;#039;) do&lt;br /&gt;
				-- Skip line if is empty or a comment.&lt;br /&gt;
				if line ~= &amp;#039;&amp;#039; then&lt;br /&gt;
					local comment = options.comment&lt;br /&gt;
					if not (comment and line:sub(1, #comment) == comment) then&lt;br /&gt;
						-- Replace accepted delimiters with a space.&lt;br /&gt;
						line = line:gsub(&amp;#039;[!&amp;quot;#&amp;amp;\&amp;#039;()+,%-;&amp;lt;=&amp;gt;?[%]_{|}]&amp;#039;, &amp;#039; &amp;#039;)&lt;br /&gt;
						extract_ipv4(v4list, v4omitted, line)&lt;br /&gt;
						extract_ipv6(v6list, v6omitted, line)&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if v4list.n &amp;lt; 1 and v6list.n &amp;lt; 1 then&lt;br /&gt;
		error(messages.invalid, 0)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local lines = Collection.new()&lt;br /&gt;
	process_ips(lines, v4list, v4omitted)&lt;br /&gt;
	process_ips(lines, v6list, v6omitted)&lt;br /&gt;
	return lines:join(&amp;#039;\n&amp;#039;)&lt;br /&gt;
end -- IPrange.textSurvey()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
IPrange.failsafe = function ()&lt;br /&gt;
    return IPrange.serial&lt;br /&gt;
end -- IPrange.failsafe()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
-- Provide template access and expose URIutil table to require()&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
p.f = function ( frame )&lt;br /&gt;
 	 -- Return wikitext to display&lt;br /&gt;
    -- the smallest IPv4 or IPv6 CIDR range that covers each address&lt;br /&gt;
 	 -- given in the arguments, or return error text.&lt;br /&gt;
 	 -- Input can have any mixture of IPs;&lt;br /&gt;
    -- IPv4 and IPv6 are processed separately.&lt;br /&gt;
 	 local ok, r = pcall( IPrange.textSurvey, frame:getParent().args )&lt;br /&gt;
 	 if not ok then&lt;br /&gt;
        r = tostring( mw.html.create( &amp;quot;span&amp;quot; )&lt;br /&gt;
                             :addClass( &amp;quot;error&amp;quot; )&lt;br /&gt;
                             :wikitext( messages[ &amp;quot;error&amp;quot; ] )&lt;br /&gt;
                             :wikitext( r ) )&lt;br /&gt;
 	 end&lt;br /&gt;
    return r&lt;br /&gt;
end -- p.f()&lt;br /&gt;
&lt;br /&gt;
p.failsafe = function ()&lt;br /&gt;
    return IPrange.failsafe()&lt;br /&gt;
end -- p.failsafe()&lt;br /&gt;
&lt;br /&gt;
p.IPrange = function ()&lt;br /&gt;
    -- Module interface&lt;br /&gt;
    return IPrange&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>imported&gt;Lómelinde</name></author>
	</entry>
</feed>