using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace RegexNegativePatternsForString
{
internal class Program
{
public static void Main()
{
int maxLengthNGWord = 5;
foreach (var nChar in Enumerable.Range(1, maxLengthNGWord))
{
string ngWord = new string(Enumerable.Range(0, nChar).Select(it => Convert.ToChar('a' + it)).ToArray());
string pattern = string.Format("^{0}$", RegexPatternGenerator.GenerateNegativePattern(ngWord));
Regex regex = new Regex(pattern);
TestNeg(regex, ngWord);
}
}
private static void TestNeg(Regex regex, string ngWord)
{
Console.WriteLine("==================== NG-word: {0} ====================", ngWord);
var alphabets = Enumerable.Range(0, ngWord.Length + 1).Select(it => Convert.ToChar('a' + it));
StringBuilder sb = new StringBuilder();
int nCases = 0;
foreach (var item in DirectProductEnumerator.DP(alphabets, alphabets))
{
sb.Clear();
sb.Append(item.Item1);
sb.Append(item.Item2);
string input = sb.ToString();
TestNeg(input, regex, ngWord);
nCases++;
}
Console.WriteLine("---------- any 2-length word: {0} cases", nCases);
nCases = 0;
foreach (var item in DirectProductEnumerator.DP(alphabets, alphabets, alphabets))
{
sb.Clear();
sb.Append(item.Item1);
sb.Append(item.Item2);
sb.Append(item.Item3);
string input = sb.ToString();
TestNeg(input, regex, ngWord);
nCases++;
}
Console.WriteLine("---------- any 3-length word: {0} cases", nCases);
nCases = 0;
foreach (var item in DirectProductEnumerator.DP(alphabets, alphabets, alphabets, alphabets))
{
sb.Clear();
sb.Append(item.Item1);
sb.Append(item.Item2);
sb.Append(item.Item3);
sb.Append(item.Item4);
string input = sb.ToString();
TestNeg(input, regex, ngWord);
nCases++;
}
Console.WriteLine("---------- any 4-length word: {0} cases", nCases);
nCases = 0;
foreach (var item in DirectProductEnumerator.DP(alphabets, alphabets, alphabets, alphabets, alphabets))
{
sb.Clear();
sb.Append(item.Item1);
sb.Append(item.Item2);
sb.Append(item.Item3);
sb.Append(item.Item4);
sb.Append(item.Item5);
string input = sb.ToString();
TestNeg(input, regex, ngWord);
nCases++;
}
Console.WriteLine("---------- any 5-length word: {0} cases", nCases);
nCases = 0;
foreach (var item in DirectProductEnumerator.DP(alphabets, alphabets, alphabets, alphabets, alphabets, alphabets))
{
sb.Clear();
sb.Append(item.Item1);
sb.Append(item.Item2);
sb.Append(item.Item3);
sb.Append(item.Item4);
sb.Append(item.Item5);
sb.Append(item.Item6);
string input = sb.ToString();
TestNeg(input, regex, ngWord);
nCases++;
}
Console.WriteLine("---------- any 6-length word: {0} cases", nCases);
}
private static void TestNeg(string input, Regex regex, string ng)
{
bool actual = regex.IsMatch(input);
bool expected = !input.Contains(ng);
if (actual != expected)
{
string strActual = "was " + (actual ? "accepted" : "not accepted");
string strExpected = expected ? "does not contain" : "contains";
string errMsg = string.Format("Assertion failure! '{0}' {1} but it {2} '{3}'. input: {4}, regex: {5}, NG-word: {6}", input, strActual, strExpected, ng, input, regex.ToString(), ng);
throw new Exception(errMsg);
}
}
}
internal static class RegexPatternGenerator
{
public static string GenerateNegativePattern(string ngWord)
{
if (string.IsNullOrEmpty(ngWord))
{
throw new Exception();
}
string head = ngWord[0].ToString();
if (ngWord.Length == 1)
{
return string.Format("[^{0}]*", head);
}
string part1 = Part1(ngWord);
string part2 = Part2(ngWord);
string part3 = Part3(ngWord);
string part4 = Part4(ngWord);
return string.Format("([^{0}]|{0}({1})*({2}))*(({3}){4})?", head, part1, part2, part3, part4);
}
private static string Part1(string ngWord)
{
if (ngWord.Length < 2)
{
throw new Exception();
}
char head = ngWord[0];
string result = head.ToString();
for (int i = ngWord.Length - 1; 2 <= i; i--)
{
result = string.Format("{0}|{1}({2})", head, ngWord[i - 1], result);
}
return result;
}
private static string Part2(string ngWord)
{
if (ngWord.Length < 2)
{
throw new Exception();
}
char head = ngWord[0];
string result = string.Format("[^{0}{1}]", head, ngWord[ngWord.Length - 1]);
for (int i = ngWord.Length - 1; 2 <= i; i--)
{
result = string.Format("[^{0}{1}]|{1}({2})", head, ngWord[i - 1], result);
}
return result;
}
private static string Part3(string ngWord)
{
if (ngWord.Length < 2)
{
throw new Exception();
}
else if (ngWord.Length == 1)
{
return string.Empty;
}
char head = ngWord[0];
string part1 = Part1(ngWord);
if (ngWord.Length == 2)
{
return string.Format("{0}*", part1);
}
else
{
return string.Format("{0}({1})*", head, part1);
}
}
private static string Part4(string ngWord)
{
if (ngWord.Length < 2)
{
throw new Exception();
}
else if (ngWord.Length == 2)
{
return string.Empty;
}
string result = string.Format("({0})?", ngWord[ngWord.Length - 2]);
for (int i = ngWord.Length - 1; 3 <= i; i--)
{
result = string.Format("({0}{1})?", ngWord[i - 2], result);
}
return result;
}
}
internal static class DirectProductEnumerator
{
public static IEnumerable<Tuple<T1, T2>> DP<T1, T2>(IEnumerable<T1> set1, IEnumerable<T2> set2)
{
foreach (var item1 in set1)
{
foreach (var item2 in set2)
{
yield return Tuple.Create(item1, item2);
}
}
}
public static IEnumerable<Tuple<T1, T2, T3>> DP<T1, T2, T3>(IEnumerable<T1> set1, IEnumerable<T2> set2, IEnumerable<T3> set3)
{
foreach (var item1 in set1)
{
foreach (var item2 in set2)
{
foreach (var item3 in set3)
{
yield return Tuple.Create(item1, item2, item3);
}
}
}
}
public static IEnumerable<Tuple<T1, T2, T3, T4>> DP<T1, T2, T3, T4>(IEnumerable<T1> set1, IEnumerable<T2> set2, IEnumerable<T3> set3, IEnumerable<T4> set4)
{
foreach (var item1 in set1)
{
foreach (var item2 in set2)
{
foreach (var item3 in set3)
{
foreach (var item4 in set4)
{
yield return Tuple.Create(item1, item2, item3, item4);
}
}
}
}
}
public static IEnumerable<Tuple<T1, T2, T3, T4, T5>> DP<T1, T2, T3, T4, T5>(IEnumerable<T1> set1, IEnumerable<T2> set2, IEnumerable<T3> set3, IEnumerable<T4> set4, IEnumerable<T5> set5)
{
foreach (var item1 in set1)
{
foreach (var item2 in set2)
{
foreach (var item3 in set3)
{
foreach (var item4 in set4)
{
foreach (var item5 in set5)
{
yield return Tuple.Create(item1, item2, item3, item4, item5);
}
}
}
}
}
}
public static IEnumerable<Tuple<T1, T2, T3, T4, T5, T6>> DP<T1, T2, T3, T4, T5, T6>(IEnumerable<T1> set1, IEnumerable<T2> set2, IEnumerable<T3> set3, IEnumerable<T4> set4, IEnumerable<T5> set5, IEnumerable<T6> set6)
{
foreach (var item1 in set1)
{
foreach (var item2 in set2)
{
foreach (var item3 in set3)
{
foreach (var item4 in set4)
{
foreach (var item5 in set5)
{
foreach (var item6 in set6)
{
yield return Tuple.Create(item1, item2, item3, item4, item5, item6);
}
}
}
}
}
}
}
}
}
dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYzsKdXNpbmcgU3lzdGVtLkxpbnE7CnVzaW5nIFN5c3RlbS5UZXh0Owp1c2luZyBTeXN0ZW0uVGV4dC5SZWd1bGFyRXhwcmVzc2lvbnM7CgpuYW1lc3BhY2UgUmVnZXhOZWdhdGl2ZVBhdHRlcm5zRm9yU3RyaW5nCnsKCWludGVybmFsIGNsYXNzIFByb2dyYW0KCXsKCQlwdWJsaWMgc3RhdGljIHZvaWQgTWFpbigpCgkJewoJCQlpbnQgbWF4TGVuZ3RoTkdXb3JkID0gNTsKCQkJZm9yZWFjaCAodmFyIG5DaGFyIGluIEVudW1lcmFibGUuUmFuZ2UoMSwgbWF4TGVuZ3RoTkdXb3JkKSkKCQkJewoJCQkJc3RyaW5nIG5nV29yZCA9IG5ldyBzdHJpbmcoRW51bWVyYWJsZS5SYW5nZSgwLCBuQ2hhcikuU2VsZWN0KGl0ID0+IENvbnZlcnQuVG9DaGFyKCdhJyArIGl0KSkuVG9BcnJheSgpKTsKCQkJCXN0cmluZyBwYXR0ZXJuID0gc3RyaW5nLkZvcm1hdCgiXnswfSQiLCBSZWdleFBhdHRlcm5HZW5lcmF0b3IuR2VuZXJhdGVOZWdhdGl2ZVBhdHRlcm4obmdXb3JkKSk7CgkJCQlSZWdleCByZWdleCA9IG5ldyBSZWdleChwYXR0ZXJuKTsKCQkJCVRlc3ROZWcocmVnZXgsIG5nV29yZCk7CgkJCX0KCQl9CgoJCXByaXZhdGUgc3RhdGljIHZvaWQgVGVzdE5lZyhSZWdleCByZWdleCwgc3RyaW5nIG5nV29yZCkKCQl7CgkJCUNvbnNvbGUuV3JpdGVMaW5lKCI9PT09PT09PT09PT09PT09PT09PSBORy13b3JkOiB7MH0gPT09PT09PT09PT09PT09PT09PT0iLCBuZ1dvcmQpOwoKCQkJdmFyIGFscGhhYmV0cyA9IEVudW1lcmFibGUuUmFuZ2UoMCwgbmdXb3JkLkxlbmd0aCArIDEpLlNlbGVjdChpdCA9PiBDb252ZXJ0LlRvQ2hhcignYScgKyBpdCkpOwoJCQlTdHJpbmdCdWlsZGVyIHNiID0gbmV3IFN0cmluZ0J1aWxkZXIoKTsKCQkJaW50IG5DYXNlcyA9IDA7CgoJCQlmb3JlYWNoICh2YXIgaXRlbSBpbiBEaXJlY3RQcm9kdWN0RW51bWVyYXRvci5EUChhbHBoYWJldHMsIGFscGhhYmV0cykpCgkJCXsKCQkJCXNiLkNsZWFyKCk7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtMSk7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtMik7CgoJCQkJc3RyaW5nIGlucHV0ID0gc2IuVG9TdHJpbmcoKTsKCQkJCVRlc3ROZWcoaW5wdXQsIHJlZ2V4LCBuZ1dvcmQpOwoJCQkJbkNhc2VzKys7CgkJCX0KCQkJQ29uc29sZS5Xcml0ZUxpbmUoIi0tLS0tLS0tLS0gYW55IDItbGVuZ3RoIHdvcmQ6IHswfSBjYXNlcyIsIG5DYXNlcyk7CgoJCQluQ2FzZXMgPSAwOwoJCQlmb3JlYWNoICh2YXIgaXRlbSBpbiBEaXJlY3RQcm9kdWN0RW51bWVyYXRvci5EUChhbHBoYWJldHMsIGFscGhhYmV0cywgYWxwaGFiZXRzKSkKCQkJewoJCQkJc2IuQ2xlYXIoKTsKCQkJCXNiLkFwcGVuZChpdGVtLkl0ZW0xKTsKCQkJCXNiLkFwcGVuZChpdGVtLkl0ZW0yKTsKCQkJCXNiLkFwcGVuZChpdGVtLkl0ZW0zKTsKCgkJCQlzdHJpbmcgaW5wdXQgPSBzYi5Ub1N0cmluZygpOwoJCQkJVGVzdE5lZyhpbnB1dCwgcmVnZXgsIG5nV29yZCk7CgkJCQluQ2FzZXMrKzsKCQkJfQoJCQlDb25zb2xlLldyaXRlTGluZSgiLS0tLS0tLS0tLSBhbnkgMy1sZW5ndGggd29yZDogezB9IGNhc2VzIiwgbkNhc2VzKTsKCgkJCW5DYXNlcyA9IDA7CgkJCWZvcmVhY2ggKHZhciBpdGVtIGluIERpcmVjdFByb2R1Y3RFbnVtZXJhdG9yLkRQKGFscGhhYmV0cywgYWxwaGFiZXRzLCBhbHBoYWJldHMsIGFscGhhYmV0cykpCgkJCXsKCQkJCXNiLkNsZWFyKCk7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtMSk7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtMik7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtMyk7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtNCk7CgoJCQkJc3RyaW5nIGlucHV0ID0gc2IuVG9TdHJpbmcoKTsKCQkJCVRlc3ROZWcoaW5wdXQsIHJlZ2V4LCBuZ1dvcmQpOwoJCQkJbkNhc2VzKys7CgkJCX0KCQkJQ29uc29sZS5Xcml0ZUxpbmUoIi0tLS0tLS0tLS0gYW55IDQtbGVuZ3RoIHdvcmQ6IHswfSBjYXNlcyIsIG5DYXNlcyk7CgoJCQluQ2FzZXMgPSAwOwoJCQlmb3JlYWNoICh2YXIgaXRlbSBpbiBEaXJlY3RQcm9kdWN0RW51bWVyYXRvci5EUChhbHBoYWJldHMsIGFscGhhYmV0cywgYWxwaGFiZXRzLCBhbHBoYWJldHMsIGFscGhhYmV0cykpCgkJCXsKCQkJCXNiLkNsZWFyKCk7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtMSk7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtMik7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtMyk7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtNCk7CgkJCQlzYi5BcHBlbmQoaXRlbS5JdGVtNSk7CgoJCQkJc3RyaW5nIGlucHV0ID0gc2IuVG9TdHJpbmcoKTsKCQkJCVRlc3ROZWcoaW5wdXQsIHJlZ2V4LCBuZ1dvcmQpOwoJCQkJbkNhc2VzKys7CgkJCX0KCQkJQ29uc29sZS5Xcml0ZUxpbmUoIi0tLS0tLS0tLS0gYW55IDUtbGVuZ3RoIHdvcmQ6IHswfSBjYXNlcyIsIG5DYXNlcyk7CgoJCQluQ2FzZXMgPSAwOwoJCQlmb3JlYWNoICh2YXIgaXRlbSBpbiBEaXJlY3RQcm9kdWN0RW51bWVyYXRvci5EUChhbHBoYWJldHMsIGFscGhhYmV0cywgYWxwaGFiZXRzLCBhbHBoYWJldHMsIGFscGhhYmV0cywgYWxwaGFiZXRzKSkKCQkJewoJCQkJc2IuQ2xlYXIoKTsKCQkJCXNiLkFwcGVuZChpdGVtLkl0ZW0xKTsKCQkJCXNiLkFwcGVuZChpdGVtLkl0ZW0yKTsKCQkJCXNiLkFwcGVuZChpdGVtLkl0ZW0zKTsKCQkJCXNiLkFwcGVuZChpdGVtLkl0ZW00KTsKCQkJCXNiLkFwcGVuZChpdGVtLkl0ZW01KTsKCQkJCXNiLkFwcGVuZChpdGVtLkl0ZW02KTsKCgkJCQlzdHJpbmcgaW5wdXQgPSBzYi5Ub1N0cmluZygpOwoJCQkJVGVzdE5lZyhpbnB1dCwgcmVnZXgsIG5nV29yZCk7CgkJCQluQ2FzZXMrKzsKCQkJfQoJCQlDb25zb2xlLldyaXRlTGluZSgiLS0tLS0tLS0tLSBhbnkgNi1sZW5ndGggd29yZDogezB9IGNhc2VzIiwgbkNhc2VzKTsKCQl9CgoJCXByaXZhdGUgc3RhdGljIHZvaWQgVGVzdE5lZyhzdHJpbmcgaW5wdXQsIFJlZ2V4IHJlZ2V4LCBzdHJpbmcgbmcpCgkJewoJCQlib29sIGFjdHVhbCA9IHJlZ2V4LklzTWF0Y2goaW5wdXQpOwoJCQlib29sIGV4cGVjdGVkID0gIWlucHV0LkNvbnRhaW5zKG5nKTsKCgkJCWlmIChhY3R1YWwgIT0gZXhwZWN0ZWQpCgkJCXsKCQkJCXN0cmluZyBzdHJBY3R1YWwgPSAid2FzICIgKyAoYWN0dWFsID8gImFjY2VwdGVkIiA6ICJub3QgYWNjZXB0ZWQiKTsKCQkJCXN0cmluZyBzdHJFeHBlY3RlZCA9IGV4cGVjdGVkID8gImRvZXMgbm90IGNvbnRhaW4iIDogImNvbnRhaW5zIjsKCQkJCXN0cmluZyBlcnJNc2cgPSBzdHJpbmcuRm9ybWF0KCJBc3NlcnRpb24gZmFpbHVyZSEgJ3swfScgezF9IGJ1dCBpdCB7Mn0gJ3szfScuIGlucHV0OiB7NH0sIHJlZ2V4OiB7NX0sIE5HLXdvcmQ6IHs2fSIsIGlucHV0LCBzdHJBY3R1YWwsIHN0ckV4cGVjdGVkLCBuZywgaW5wdXQsIHJlZ2V4LlRvU3RyaW5nKCksIG5nKTsKCQkJCXRocm93IG5ldyBFeGNlcHRpb24oZXJyTXNnKTsKCQkJfQoJCX0KCX0KCglpbnRlcm5hbCBzdGF0aWMgY2xhc3MgUmVnZXhQYXR0ZXJuR2VuZXJhdG9yCgl7CgkJcHVibGljIHN0YXRpYyBzdHJpbmcgR2VuZXJhdGVOZWdhdGl2ZVBhdHRlcm4oc3RyaW5nIG5nV29yZCkKCQl7CgkJCWlmIChzdHJpbmcuSXNOdWxsT3JFbXB0eShuZ1dvcmQpKQoJCQl7CgkJCQl0aHJvdyBuZXcgRXhjZXB0aW9uKCk7CgkJCX0KCgkJCXN0cmluZyBoZWFkID0gbmdXb3JkWzBdLlRvU3RyaW5nKCk7CgkJCWlmIChuZ1dvcmQuTGVuZ3RoID09IDEpCgkJCXsKCQkJCXJldHVybiBzdHJpbmcuRm9ybWF0KCJbXnswfV0qIiwgaGVhZCk7CgkJCX0KCgkJCXN0cmluZyBwYXJ0MSA9IFBhcnQxKG5nV29yZCk7CgkJCXN0cmluZyBwYXJ0MiA9IFBhcnQyKG5nV29yZCk7CgkJCXN0cmluZyBwYXJ0MyA9IFBhcnQzKG5nV29yZCk7CgkJCXN0cmluZyBwYXJ0NCA9IFBhcnQ0KG5nV29yZCk7CgkJCXJldHVybiBzdHJpbmcuRm9ybWF0KCIoW157MH1dfHswfSh7MX0pKih7Mn0pKSooKHszfSl7NH0pPyIsIGhlYWQsIHBhcnQxLCBwYXJ0MiwgcGFydDMsIHBhcnQ0KTsKCQl9CgoJCXByaXZhdGUgc3RhdGljIHN0cmluZyBQYXJ0MShzdHJpbmcgbmdXb3JkKQoJCXsKCQkJaWYgKG5nV29yZC5MZW5ndGggPCAyKQoJCQl7CgkJCQl0aHJvdyBuZXcgRXhjZXB0aW9uKCk7CgkJCX0KCgkJCWNoYXIgaGVhZCA9IG5nV29yZFswXTsKCQkJc3RyaW5nIHJlc3VsdCA9IGhlYWQuVG9TdHJpbmcoKTsKCQkJZm9yIChpbnQgaSA9IG5nV29yZC5MZW5ndGggLSAxOyAyIDw9IGk7IGktLSkKCQkJewoJCQkJcmVzdWx0ID0gc3RyaW5nLkZvcm1hdCgiezB9fHsxfSh7Mn0pIiwgaGVhZCwgbmdXb3JkW2kgLSAxXSwgcmVzdWx0KTsKCQkJfQoJCQlyZXR1cm4gcmVzdWx0OwoJCX0KCgkJcHJpdmF0ZSBzdGF0aWMgc3RyaW5nIFBhcnQyKHN0cmluZyBuZ1dvcmQpCgkJewoJCQlpZiAobmdXb3JkLkxlbmd0aCA8IDIpCgkJCXsKCQkJCXRocm93IG5ldyBFeGNlcHRpb24oKTsKCQkJfQoKCQkJY2hhciBoZWFkID0gbmdXb3JkWzBdOwoJCQlzdHJpbmcgcmVzdWx0ID0gc3RyaW5nLkZvcm1hdCgiW157MH17MX1dIiwgaGVhZCwgbmdXb3JkW25nV29yZC5MZW5ndGggLSAxXSk7CgkJCWZvciAoaW50IGkgPSBuZ1dvcmQuTGVuZ3RoIC0gMTsgMiA8PSBpOyBpLS0pCgkJCXsKCQkJCXJlc3VsdCA9IHN0cmluZy5Gb3JtYXQoIlteezB9ezF9XXx7MX0oezJ9KSIsIGhlYWQsIG5nV29yZFtpIC0gMV0sIHJlc3VsdCk7CgkJCX0KCQkJcmV0dXJuIHJlc3VsdDsKCQl9CgoJCXByaXZhdGUgc3RhdGljIHN0cmluZyBQYXJ0MyhzdHJpbmcgbmdXb3JkKQoJCXsKCQkJaWYgKG5nV29yZC5MZW5ndGggPCAyKQoJCQl7CgkJCQl0aHJvdyBuZXcgRXhjZXB0aW9uKCk7CgkJCX0KCQkJZWxzZSBpZiAobmdXb3JkLkxlbmd0aCA9PSAxKQoJCQl7CgkJCQlyZXR1cm4gc3RyaW5nLkVtcHR5OwoJCQl9CgoJCQljaGFyIGhlYWQgPSBuZ1dvcmRbMF07CgkJCXN0cmluZyBwYXJ0MSA9IFBhcnQxKG5nV29yZCk7CgkJCWlmIChuZ1dvcmQuTGVuZ3RoID09IDIpCgkJCXsKCQkJCXJldHVybiBzdHJpbmcuRm9ybWF0KCJ7MH0qIiwgcGFydDEpOwoJCQl9CgkJCWVsc2UKCQkJewoJCQkJcmV0dXJuIHN0cmluZy5Gb3JtYXQoInswfSh7MX0pKiIsIGhlYWQsIHBhcnQxKTsKCQkJfQoJCX0KCgkJcHJpdmF0ZSBzdGF0aWMgc3RyaW5nIFBhcnQ0KHN0cmluZyBuZ1dvcmQpCgkJewoJCQlpZiAobmdXb3JkLkxlbmd0aCA8IDIpCgkJCXsKCQkJCXRocm93IG5ldyBFeGNlcHRpb24oKTsKCQkJfQoJCQllbHNlIGlmIChuZ1dvcmQuTGVuZ3RoID09IDIpCgkJCXsKCQkJCXJldHVybiBzdHJpbmcuRW1wdHk7CgkJCX0KCgkJCXN0cmluZyByZXN1bHQgPSBzdHJpbmcuRm9ybWF0KCIoezB9KT8iLCBuZ1dvcmRbbmdXb3JkLkxlbmd0aCAtIDJdKTsKCQkJZm9yIChpbnQgaSA9IG5nV29yZC5MZW5ndGggLSAxOyAzIDw9IGk7IGktLSkKCQkJewoJCQkJcmVzdWx0ID0gc3RyaW5nLkZvcm1hdCgiKHswfXsxfSk/IiwgbmdXb3JkW2kgLSAyXSwgcmVzdWx0KTsKCQkJfQoJCQlyZXR1cm4gcmVzdWx0OwoJCX0KCX0KCglpbnRlcm5hbCBzdGF0aWMgY2xhc3MgRGlyZWN0UHJvZHVjdEVudW1lcmF0b3IKCXsKCQlwdWJsaWMgc3RhdGljIElFbnVtZXJhYmxlPFR1cGxlPFQxLCBUMj4+IERQPFQxLCBUMj4oSUVudW1lcmFibGU8VDE+IHNldDEsIElFbnVtZXJhYmxlPFQyPiBzZXQyKQoJCXsKCQkJZm9yZWFjaCAodmFyIGl0ZW0xIGluIHNldDEpCgkJCXsKCQkJCWZvcmVhY2ggKHZhciBpdGVtMiBpbiBzZXQyKQoJCQkJewoJCQkJCXlpZWxkIHJldHVybiBUdXBsZS5DcmVhdGUoaXRlbTEsIGl0ZW0yKTsKCQkJCX0KCQkJfQoJCX0KCgkJcHVibGljIHN0YXRpYyBJRW51bWVyYWJsZTxUdXBsZTxUMSwgVDIsIFQzPj4gRFA8VDEsIFQyLCBUMz4oSUVudW1lcmFibGU8VDE+IHNldDEsIElFbnVtZXJhYmxlPFQyPiBzZXQyLCBJRW51bWVyYWJsZTxUMz4gc2V0MykKCQl7CgkJCWZvcmVhY2ggKHZhciBpdGVtMSBpbiBzZXQxKQoJCQl7CgkJCQlmb3JlYWNoICh2YXIgaXRlbTIgaW4gc2V0MikKCQkJCXsKCQkJCQlmb3JlYWNoICh2YXIgaXRlbTMgaW4gc2V0MykKCQkJCQl7CgkJCQkJCXlpZWxkIHJldHVybiBUdXBsZS5DcmVhdGUoaXRlbTEsIGl0ZW0yLCBpdGVtMyk7CgkJCQkJfQoJCQkJfQoJCQl9CgkJfQoKCQlwdWJsaWMgc3RhdGljIElFbnVtZXJhYmxlPFR1cGxlPFQxLCBUMiwgVDMsIFQ0Pj4gRFA8VDEsIFQyLCBUMywgVDQ+KElFbnVtZXJhYmxlPFQxPiBzZXQxLCBJRW51bWVyYWJsZTxUMj4gc2V0MiwgSUVudW1lcmFibGU8VDM+IHNldDMsIElFbnVtZXJhYmxlPFQ0PiBzZXQ0KQoJCXsKCQkJZm9yZWFjaCAodmFyIGl0ZW0xIGluIHNldDEpCgkJCXsKCQkJCWZvcmVhY2ggKHZhciBpdGVtMiBpbiBzZXQyKQoJCQkJewoJCQkJCWZvcmVhY2ggKHZhciBpdGVtMyBpbiBzZXQzKQoJCQkJCXsKCQkJCQkJZm9yZWFjaCAodmFyIGl0ZW00IGluIHNldDQpCgkJCQkJCXsKCQkJCQkJCXlpZWxkIHJldHVybiBUdXBsZS5DcmVhdGUoaXRlbTEsIGl0ZW0yLCBpdGVtMywgaXRlbTQpOwoJCQkJCQl9CgkJCQkJfQoJCQkJfQoJCQl9CgkJfQoKCQlwdWJsaWMgc3RhdGljIElFbnVtZXJhYmxlPFR1cGxlPFQxLCBUMiwgVDMsIFQ0LCBUNT4+IERQPFQxLCBUMiwgVDMsIFQ0LCBUNT4oSUVudW1lcmFibGU8VDE+IHNldDEsIElFbnVtZXJhYmxlPFQyPiBzZXQyLCBJRW51bWVyYWJsZTxUMz4gc2V0MywgSUVudW1lcmFibGU8VDQ+IHNldDQsIElFbnVtZXJhYmxlPFQ1PiBzZXQ1KQoJCXsKCQkJZm9yZWFjaCAodmFyIGl0ZW0xIGluIHNldDEpCgkJCXsKCQkJCWZvcmVhY2ggKHZhciBpdGVtMiBpbiBzZXQyKQoJCQkJewoJCQkJCWZvcmVhY2ggKHZhciBpdGVtMyBpbiBzZXQzKQoJCQkJCXsKCQkJCQkJZm9yZWFjaCAodmFyIGl0ZW00IGluIHNldDQpCgkJCQkJCXsKCQkJCQkJCWZvcmVhY2ggKHZhciBpdGVtNSBpbiBzZXQ1KQoJCQkJCQkJewoJCQkJCQkJCXlpZWxkIHJldHVybiBUdXBsZS5DcmVhdGUoaXRlbTEsIGl0ZW0yLCBpdGVtMywgaXRlbTQsIGl0ZW01KTsKCQkJCQkJCX0KCQkJCQkJfQoJCQkJCX0KCQkJCX0KCQkJfQoJCX0KCgkJcHVibGljIHN0YXRpYyBJRW51bWVyYWJsZTxUdXBsZTxUMSwgVDIsIFQzLCBUNCwgVDUsIFQ2Pj4gRFA8VDEsIFQyLCBUMywgVDQsIFQ1LCBUNj4oSUVudW1lcmFibGU8VDE+IHNldDEsIElFbnVtZXJhYmxlPFQyPiBzZXQyLCBJRW51bWVyYWJsZTxUMz4gc2V0MywgSUVudW1lcmFibGU8VDQ+IHNldDQsIElFbnVtZXJhYmxlPFQ1PiBzZXQ1LCBJRW51bWVyYWJsZTxUNj4gc2V0NikKCQl7CgkJCWZvcmVhY2ggKHZhciBpdGVtMSBpbiBzZXQxKQoJCQl7CgkJCQlmb3JlYWNoICh2YXIgaXRlbTIgaW4gc2V0MikKCQkJCXsKCQkJCQlmb3JlYWNoICh2YXIgaXRlbTMgaW4gc2V0MykKCQkJCQl7CgkJCQkJCWZvcmVhY2ggKHZhciBpdGVtNCBpbiBzZXQ0KQoJCQkJCQl7CgkJCQkJCQlmb3JlYWNoICh2YXIgaXRlbTUgaW4gc2V0NSkKCQkJCQkJCXsKCQkJCQkJCQlmb3JlYWNoICh2YXIgaXRlbTYgaW4gc2V0NikKCQkJCQkJCQl7CgkJCQkJCQkJCXlpZWxkIHJldHVybiBUdXBsZS5DcmVhdGUoaXRlbTEsIGl0ZW0yLCBpdGVtMywgaXRlbTQsIGl0ZW01LCBpdGVtNik7CgkJCQkJCQkJfQoJCQkJCQkJfQoJCQkJCQl9CgkJCQkJfQoJCQkJfQoJCQl9CgkJfQoJfQp9Cg==