re_m.test=check_re("\082\101\103\069\120","\116\101\115\116",function(C,l,x)return re_rawfind(C.token,to_str_arr(l,x),1,C.flags,C.verb_flags,true)end)re_m.match=check_re("\082\101\103\069\120","\109\097\116\099\104",function(C,l,x,p)local j=re_rawfind(C.token,to_str_arr(l,x),1,C.flags,C.verb_flags,false)if not j then return nil end return new_match(j,C.group_id,p,l)end)re_m.matchall=check_re("\082\101\103\069\120","\109\097\116\099\104\097\108\108",function(C,l,x,p)l=to_str_arr(l,x)local j=1 return function()local x=j<=l.n+1 and re_rawfind(C.token,l,j,C.flags,C.verb_flags,false)if not x then return nil end j=x[0][2]+(x[0][1]>=x[0][2]and 1 or 0)return new_match(x,C.group_id,p,l.s)end end)local function C(l,x,p,j)for j,v in ipairs(j)do if type(v)=="\116\097\098\108\101"then if v[1]=="\099\111\110\100\105\116\105\111\110"then if p[v[2]]then if v[3]then C(l,x,p,v[3])else table.move(x,p[v[2]][1],p[v[2]][2]-1,#l+1,l)end elseif v[4]then C(l,x,p,v[4])end else table.move(v,1,#v,#l+1,l)end elseif p[v]then table.move(x,p[v][1],p[v][2]-1,#l+1,l)end end l.n=#l return l end re_m.sub=check_re("\082\101\103\069\120","\115\117\098",function(l,x,p,j,v,b)if v~=nil and(type(v)~="\110\117\109\098\101\114"and type(v)~="\115\116\114\105\110\103")then error(string.format("\105\110\118\097\108\105\100 \097\114\103\117\109\101\110\116 \035\053 \116\111 \'\115\117\098\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",typeof(v)),3)end local I={l=false,o=false;u=false}for C in string.gmatch(v or"",utf8.charpattern)do if I[C]~=false then error("\105\110\118\097\108\105\100 \114\101\103\117\108\097\114 \101\120\112\114\101\115\115\105\111\110 \115\117\098\115\116\105\116\117\116\105\111\110 \102\108\097\103 "..C,3)end I[C]=true end local t=type(x)if t=="\110\117\109\098\101\114"then x..=""elseif t~="\115\116\114\105\110\103"and(t~="\102\117\110\099\116\105\111\110"and(not I.o or t~="\116\097\098\108\101"))then error(string.format("\105\110\118\097\108\105\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \'\115\117\098\' \040\115\116\114\105\110\103\047\102\117\110\099\116\105\111\110\037\115 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",I.o and"\047\116\097\098\108\101"or"",typeof(x)),3)end if tonumber(j)then j=tonumber(j)if j<=-1 or j~=j then j=math.huge end elseif j~=nil then error(string.format("\105\110\118\097\108\105\100 \097\114\103\117\109\101\110\116 \035\052 \116\111 \'\115\117\098\' \040\110\117\109\098\101\114 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",typeof(j)),3)else j=math.huge end if j<1 then return p,0 end local U=0 if t=="\115\116\114\105\110\103"then x=to_str_arr(x)if not I.l then local C=0 local p=table.create(3)local j=l.token.group_n local v={}while C<x.n do local b=C repeat b+=1 until not x[b]or x[b]==36 or x[b]==92 or(x[b]==58 or x[b]==125)and v[1]U+=(b-C)-1 if b-C>1 then table.insert(p,table.move(x,C+1,b-1,1,table.create((b-C)-1)))end if x[b]==58 then local C=v[1]if C[2]then error("\109\097\108\102\111\114\109\101\100 \115\117\098\115\116\105\116\117\116\105\111\110 \112\097\116\116\101\114\110",3)end C[2]=table.move(p,C[3],#p,1,table.create((#p+1)-C[3]))for C=#p,C[3],-1 do p[C]=nil end elseif x[b]==125 then local C=table.remove(v,1)local l=table.move(p,C[3],#p,1,table.create((#p+1)-C[3]))for C=#p,C[3],-1 do p[C]=nil end table.insert(p,{"\099\111\110\100\105\116\105\111\110";C[1];C[2]~=true and(C[2]or l),C[2]and l})elseif x[b]then b+=1 local C=x[b]if not C then if x[b-1]==92 then error("\114\101\112\108\097\099\101\109\101\110\116 \115\116\114\105\110\103 \109\117\115\116 \110\111\116 \101\110\100 \119\105\116\104 \097 \116\114\097\105\108\105\110\103 \098\097\099\107\115\108\097\115\104",3)end local C=p[#p]if type(C)=="\116\097\098\108\101"then table.insert(C,x[b-1])else table.insert(p,{x[b-1]})end elseif C==92 and x[b-1]==36 then local C=p[#p]if type(C)=="\116\097\098\108\101"then table.insert(C,36)else table.insert(p,{36})end b-=1 U+=1 elseif C==48 then table.insert(p,0)elseif C>48 and C<=57 then local l=b local v=C-48 while x[b+1]and(x[b+1]>=48 and x[b+1]<=57)do v..=x[b+1]-48 b+=1 end v=tonumber(v)if not I.u and v>j then error("\114\101\102\101\114\101\110\099\101 \116\111 \110\111\110-\101\120\105\115\116\101\110\116 \115\117\098\112\097\116\116\101\114\110",3)end table.insert(p,v)elseif C==123 and x[b-1]==36 then b+=1 local C=b while x[b]and(x[b]>=48 and x[b]<=57 or x[b]>=65 and x[b]<=90 or x[b]>=97 and x[b]<=122 or x[b]==95)do b+=1 end if(x[b]==125 or x[b]==58 and(x[b+1]==43 or x[b+1]==45))and b~=C then local t=utf8_sub(x.s,C,b)if x[C]>=48 and x[C]<=57 then t=tonumber(t)if not I.u and t>j then error("\114\101\102\101\114\101\110\099\101 \116\111 \110\111\110-\101\120\105\115\116\101\110\116 \115\117\098\112\097\116\116\101\114\110",3)end else t=l.group_id[t]if not I.u and(not t or t>j)then error("\114\101\102\101\114\101\110\099\101 \116\111 \110\111\110-\101\120\105\115\116\101\110\116 \115\117\098\112\097\116\116\101\114\110",3)end end if x[b]==58 then b+=1 table.insert(v,{t,x[b]==45;#p+1})else table.insert(p,t)end else error("\109\097\108\102\111\114\109\101\100 \115\117\098\115\116\105\116\117\116\105\111\110 \112\097\116\116\101\114\110",3)end else local l if x[b-1]==36 then if C~=36 then local C=p[#p]if type(C)=="\116\097\098\108\101"then table.insert(C,36)else table.insert(p,{36})end end else l=escape_chars[x[b]]if type(l)~="\110\117\109\098\101\114"then l=nil end end local j=p[#p]if type(j)=="\116\097\098\108\101"then table.insert(j,l or x[b])else table.insert(p,{l or x[b]})end U+=1 end end C=b end if v[1]then error("\109\097\108\102\111\114\109\101\100 \115\117\098\115\116\105\116\117\116\105\111\110 \112\097\116\116\101\114\110",3)end if not p[2]and(type(p[1])=="\116\097\098\108\101"and p[1][1]~="\099\111\110\100\105\116\105\111\110")then x,x.n=p[1],#p[1]else x,t=p,"\115\117\098\115\116\095\115\116\114\105\110\103"end end end p=to_str_arr(p)local d,G,D=0,1,0 while G<=(p.n+d)+1 do local v=re_rawfind(l.token,p,G,l.flags,l.verb_flags,false)if not v then break end local k if t=="\115\116\114\105\110\103"then k=x elseif t=="\115\117\098\115\116\095\115\116\114\105\110\103"then k=C(table.create(U),p,v,x)else local C local j if t=="\116\097\098\108\101"then C=utf8_sub(p.s,v[0][1],v[0][2])j=x[C]else C=new_match(v,l.group_id,b,p.s)j=x(C)end if j==C or I.o and not j then local C=v[0][2]-v[0][1]k=table.move(p,v[0][1],v[0][2]-1,1,table.create(C))k.n=C elseif type(j)=="\115\116\114\105\110\103"then k=to_str_arr(j)elseif type(j)=="\110\117\109\098\101\114"then k=to_str_arr(j.."")elseif I.o then error(string.format("\105\110\118\097\108\105\100 \114\101\112\108\097\099\101\109\101\110\116 \118\097\108\117\101 \040\097 \037\115\041",type(j)),3)else k={n=0}end end local g=v[0][2]-v[0][1]local O=math.min(k.n,g)for C=0,O-1,1 do p[v[0][1]+C]=k[C+1]end local W=v[0][1]+O G=v[0][2]if g>k.n then for C=1,g-k.n,1 do table.remove(p,W)d-=1 G-=1 end elseif k.n>g then for C=1,k.n-g,1 do table.insert(p,(W+C)-1,k[O+C])d+=1 G+=1 end end if g<=0 then G+=1 end D+=1 if j<D+1 then break end end return from_str_arr(p),D end)re_m.split=check_re("\082\101\103\069\120","\115\112\108\105\116",function(C,l,x)if tonumber(x)then x=tonumber(x)if x<=-1 or x~=x then x=math.huge end elseif x~=nil then error(string.format("\105\110\118\097\108\105\100 \097\114\103\117\109\101\110\116 \035\051 \116\111 \'\115\112\108\105\116\' \040\110\117\109\098\101\114 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",typeof(x)),3)else x=math.huge end l=to_str_arr(l)local p,j=1,0 local v={}local b=0 while p<=l.n+1 do j+=1 local I=x>=j and re_rawfind(C.token,l,p,C.flags,C.verb_flags,false)if not I then break end table.insert(v,utf8_sub(l.s,p-b,I[0][1]))b=I[0][1]>=I[0][2]and 1 or 0 p=I[0][2]+b end table.insert(v,string.sub(l.s,utf8.offset(l.s,p-b)))return v end)
--
re_m.test = check_re('RegEx', 'test', function(self, str, init)
	return re_rawfind(self.token, to_str_arr(str, init), 1, self.flags, self.verb_flags, true);
end);

re_m.match = check_re('RegEx', 'match', function(self, str, init, source)
	local span = re_rawfind(self.token, to_str_arr(str, init), 1, self.flags, self.verb_flags, false);
	if not span then
		return nil;
	end;
	return new_match(span, self.group_id, source, str);
end);

re_m.matchall = check_re('RegEx', 'matchall', function(self, str, init, source)
	str = to_str_arr(str, init);
	local i = 1;
	return function()
		local span = i <= str.n + 1 and re_rawfind(self.token, str, i, self.flags, self.verb_flags, false);
		if not span then
			return nil;
		end;
		i = span[0][2] + (span[0][1] >= span[0][2] and 1 or 0);
		return new_match(span, self.group_id, source, str.s);
	end;
end);

local function insert_tokenized_sub(repl_r, str, span, tkn)
	for _, v in ipairs(tkn) do
		if type(v) == "table" then
			if v[1] == "condition" then
				if span[v[2]] then
					if v[3] then
						insert_tokenized_sub(repl_r, str, span, v[3]);
					else
						table.move(str, span[v[2]][1], span[v[2]][2] - 1, #repl_r + 1, repl_r);
					end;
				elseif v[4] then
					insert_tokenized_sub(repl_r, str, span, v[4]);
				end;
			else
				table.move(v, 1, #v, #repl_r + 1, repl_r);
			end;
		elseif span[v] then
			table.move(str, span[v][1], span[v][2] - 1, #repl_r + 1, repl_r);
		end;
	end;
	repl_r.n = #repl_r;
	return repl_r;
end;

re_m.sub = check_re('RegEx', 'sub', function(self, repl, str, n, repl_flag_str, source)
	if repl_flag_str ~= nil and type(repl_flag_str) ~= "number" and type(repl_flag_str) ~= "string" then
		error(string.format("invalid argument #5 to 'sub' (string expected, got %s)", typeof(repl_flag_str)), 3);
	end
	local repl_flags = {
		l = false, o = false, u = false,
	};
	for f in string.gmatch(repl_flag_str or '', utf8.charpattern) do
		if repl_flags[f] ~= false then
			error("invalid regular expression substitution flag " .. f, 3);
		end;
		repl_flags[f] = true;
	end;
	local repl_type = type(repl);
	if repl_type == "number" then
		repl ..= '';
	elseif repl_type ~= "string" and repl_type ~= "function" and (not repl_flags.o or repl_type ~= "table") then
		error(string.format("invalid argument #2 to 'sub' (string/function%s expected, got %s)", repl_flags.o and "/table" or '', typeof(repl)), 3);
	end;
	if tonumber(n) then
		n = tonumber(n);
		if n <= -1 or n ~= n then
			n = math.huge;
		end;
	elseif n ~= nil then
		error(string.format("invalid argument #4 to 'sub' (number expected, got %s)", typeof(n)), 3);
	else
		n = math.huge;
	end;
	if n < 1 then
		return str, 0;
	end;
	local min_repl_n = 0;
	if repl_type == "string" then
		repl = to_str_arr(repl);
		if not repl_flags.l then
			local i1 = 0;
			local repl_r = table.create(3);
			local group_n = self.token.group_n;
			local conditional_c = { };
			while i1 < repl.n do
				local i2 = i1;
				repeat
					i2 += 1;
				until not repl[i2] or repl[i2] == 0x24 or repl[i2] == 0x5C or (repl[i2] == 0x3A or repl[i2] == 0x7D) and conditional_c[1];
				min_repl_n += i2 - i1 - 1;
				if i2 - i1 > 1 then
					table.insert(repl_r, table.move(repl, i1 + 1, i2 - 1, 1, table.create(i2 - i1 - 1)));
				end;
				if repl[i2] == 0x3A then
					local current_conditional_c = conditional_c[1];
					if current_conditional_c[2] then
						error("malformed substitution pattern", 3);
					end;
					current_conditional_c[2] = table.move(repl_r, current_conditional_c[3], #repl_r, 1, table.create(#repl_r + 1 - current_conditional_c[3]));
					for i3 = #repl_r, current_conditional_c[3], -1 do
						repl_r[i3] = nil;
					end;
				elseif repl[i2] == 0x7D then
					local current_conditional_c = table.remove(conditional_c, 1);
					local second_c = table.move(repl_r, current_conditional_c[3], #repl_r, 1, table.create(#repl_r + 1 - current_conditional_c[3]));
					for i3 = #repl_r, current_conditional_c[3], -1 do
						repl_r[i3] = nil;
					end;
					table.insert(repl_r, { "condition", current_conditional_c[1], current_conditional_c[2] ~= true and (current_conditional_c[2] or second_c), current_conditional_c[2] and second_c });
				elseif repl[i2] then
					i2 += 1;
					local subst_c = repl[i2];
					if not subst_c then
						if repl[i2 - 1] == 0x5C then
							error("replacement string must not end with a trailing backslash", 3);
						end;
						local prev_repl_f = repl_r[#repl_r];
						if type(prev_repl_f) == "table" then
							table.insert(prev_repl_f, repl[i2 - 1]);
						else
							table.insert(repl_r, { repl[i2 - 1] });
						end;
					elseif subst_c == 0x5C and repl[i2 - 1] == 0x24 then
						local prev_repl_f = repl_r[#repl_r];
						if type(prev_repl_f) == "table" then
							table.insert(prev_repl_f, 0x24);
						else
							table.insert(repl_r, { 0x24 });
						end;
						i2 -= 1;
						min_repl_n += 1;
					elseif subst_c == 0x30 then
						table.insert(repl_r, 0);
					elseif subst_c > 0x30 and subst_c <= 0x39 then
						local start_i2 = i2;
						local group_i = subst_c - 0x30;
						while repl[i2 + 1] and repl[i2 + 1] >= 0x30 and repl[i2 + 1] <= 0x39 do
							group_i ..= repl[i2 + 1] - 0x30;
							i2 += 1;
						end;
						group_i = tonumber(group_i);
						if not repl_flags.u and group_i > group_n then
							error("reference to non-existent subpattern", 3);
						end;
						table.insert(repl_r, group_i);
					elseif subst_c == 0x7B and repl[i2 - 1] == 0x24 then
						i2 += 1;
						local start_i2 = i2;
						while repl[i2] and
							(repl[i2] >= 0x30 and repl[i2] <= 0x39
								or repl[i2] >= 0x41 and repl[i2] <= 0x5A
								or repl[i2] >= 0x61 and repl[i2] <= 0x7A
								or repl[i2] == 0x5F) do
							i2 += 1;
						end;
						if (repl[i2] == 0x7D or repl[i2] == 0x3A and (repl[i2 + 1] == 0x2B or repl[i2 + 1] == 0x2D)) and i2 ~= start_i2 then
							local group_k = utf8_sub(repl.s, start_i2, i2);
							if repl[start_i2] >= 0x30 and repl[start_i2] <= 0x39 then
								group_k = tonumber(group_k);
								if not repl_flags.u and group_k > group_n then
									error("reference to non-existent subpattern", 3);
								end;
							else
								group_k = self.group_id[group_k];
								if not repl_flags.u and (not group_k or group_k > group_n) then
									error("reference to non-existent subpattern", 3);
								end;
							end;
							if repl[i2] == 0x3A then
								i2 += 1;
								table.insert(conditional_c, { group_k, repl[i2] == 0x2D, #repl_r + 1 });
							else
								table.insert(repl_r, group_k);
							end;
						else
							error("malformed substitution pattern", 3);
						end;
					else
						local c_escape_char;
						if repl[i2 - 1] == 0x24 then
							if subst_c ~= 0x24 then
								local prev_repl_f = repl_r[#repl_r];
								if type(prev_repl_f) == "table" then
									table.insert(prev_repl_f, 0x24);
								else
									table.insert(repl_r, { 0x24 });
								end;
							end;
						else
							c_escape_char = escape_chars[repl[i2]];
							if type(c_escape_char) ~= "number" then
								c_escape_char = nil;
							end;
						end;
						local prev_repl_f = repl_r[#repl_r];
						if type(prev_repl_f) == "table" then
							table.insert(prev_repl_f, c_escape_char or repl[i2]);
						else
							table.insert(repl_r, { c_escape_char or repl[i2] });
						end;
						min_repl_n += 1;
					end;
				end;
				i1 = i2;
			end;
			if conditional_c[1] then
				error("malformed substitution pattern", 3);
			end;
			if not repl_r[2] and type(repl_r[1]) == "table" and repl_r[1][1] ~= "condition" then
				repl, repl.n = repl_r[1], #repl_r[1];
			else
				repl, repl_type = repl_r, "subst_string";
			end;
		end;
	end;
	str = to_str_arr(str);
	local incr, i0, count = 0, 1, 0;
	while i0 <= str.n + incr + 1 do
		local span = re_rawfind(self.token, str, i0, self.flags, self.verb_flags, false);
		if not span then
			break;
		end;
		local repl_r;
		if repl_type == "string" then
			repl_r = repl;
		elseif repl_type == "subst_string" then
			repl_r = insert_tokenized_sub(table.create(min_repl_n), str, span, repl);
		else
			local re_match;
			local repl_c;
			if repl_type == "table" then
				re_match = utf8_sub(str.s, span[0][1], span[0][2]);
				repl_c = repl[re_match];
			else
				re_match = new_match(span, self.group_id, source, str.s);
				repl_c = repl(re_match);
			end;
			if repl_c == re_match or repl_flags.o and not repl_c then
				local repl_n = span[0][2] - span[0][1];
				repl_r = table.move(str, span[0][1], span[0][2] - 1, 1, table.create(repl_n));
				repl_r.n = repl_n;
			elseif type(repl_c) == "string" then
				repl_r = to_str_arr(repl_c);
			elseif type(repl_c) == "number" then
				repl_r = to_str_arr(repl_c .. '');
			elseif repl_flags.o then
				error(string.format("invalid replacement value (a %s)", type(repl_c)), 3);
			else
				repl_r = { n = 0 };
			end;
		end;
		local match_len = span[0][2] - span[0][1];
		local repl_len = math.min(repl_r.n, match_len);
		for i1 = 0, repl_len - 1 do
			str[span[0][1] + i1] = repl_r[i1 + 1];
		end;
		local i1 = span[0][1] + repl_len;
		i0 = span[0][2];
		if match_len > repl_r.n then
			for i2 = 1, match_len - repl_r.n do
				table.remove(str, i1);
				incr -= 1;
				i0 -= 1;
			end;
		elseif repl_r.n > match_len then
			for i2 = 1, repl_r.n - match_len do
				table.insert(str, i1 + i2 - 1, repl_r[repl_len + i2]);
				incr += 1;
				i0 += 1;
			end;
		end;
		if match_len <= 0 then
			i0 += 1;
		end;
		count += 1;
		if n < count + 1 then
			break;
		end;
	end;
	return from_str_arr(str), count;
end);

re_m.split = check_re('RegEx', 'split', function(self, str, n)
	if tonumber(n) then
		n = tonumber(n);
		if n <= -1 or n ~= n then
			n = math.huge;
		end;
	elseif n ~= nil then
		error(string.format("invalid argument #3 to 'split' (number expected, got %s)", typeof(n)), 3);
	else
		n = math.huge;
	end;
	str = to_str_arr(str);
	local i, count = 1, 0;
	local ret = { };
	local prev_empty = 0;
	while i <= str.n + 1 do
		count += 1;
		local span = n >= count and re_rawfind(self.token, str, i, self.flags, self.verb_flags, false);
		if not span then
			break;
		end;
		table.insert(ret, utf8_sub(str.s, i - prev_empty, span[0][1]));
		prev_empty = span[0][1] >= span[0][2] and 1 or 0;
		i = span[0][2] + prev_empty;
	end;
	table.insert(ret, string.sub(str.s, utf8.offset(str.s, i - prev_empty)));
	return ret;
end);
