using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Diagnostics;
namespace ConsoleApp1 {
class Program {
private abstract class Board {
public enum Direction {
Left,
Right,
Up,
Down,
DownLeft,
UpLeft
}
protected String String;
public class Cell {
public HashSet<Char> Set;
public Char Current;
public Cell(String String) {
Set = new HashSet<Char>(String.ToArray());
Current = '\0';
}
}
public class Location {
public Int32 Row;
public Int32 Column;
public Location(Int32 Row, Int32 Column) {
this.Row = Row;
this.Column = Column;
}
public Location(Location From) {
Row = From.Row;
Column = From.Column;
}
}
protected Cell[][] Cells;
public abstract Location GetLocation(Direction Direction, Int32 Distance, Location Locaton);
protected abstract void MakeCells(Object Parameter);
public Board(Object Parameter, String String) {
this.String = String;
MakeCells(Parameter);
var Characters = String.ToArray();
foreach(var Row in Cells) {
foreach(var Cell in Row) {
Cell.Set = new HashSet<Char>(Characters);
}
}
}
protected Cell[] MakeCells(Int32 Size) {
var Cells = new Cell[Size];
for(var Index = 0; Index < Cells.Length; Index++) {
Cells[Index] = new Cell(String);
}
return Cells;
}
public IEnumerable<string> Strings(Char [] Characters, Condition Condition, int Distance) {
if(Distance < Condition.NumberOfCells) {
foreach(var Character in this[GetLocation(Condition.Direction, Distance, Condition.Start)].Set) {
Characters[Distance] = Character;
foreach(var String in Strings(Characters, Condition, Distance + 1)){
yield return String;
}
}
} else {
yield return string.Join("", Characters);
}
}
private IEnumerable<String> Strings(Condition Condition) {
var Characters = new Char[Condition.NumberOfCells];
for(var Index = 0; Index < Characters.Length; Index++) {
Characters[Index] = '\0';
}
foreach(var String in Strings(Characters, Condition, 0)) {
yield return String;
}
}
public Cell this[Location Location]{
get {
return Cells[Location.Row][Location.Column];
}
}
public long Reduce(Condition[] Conditions) {
var Count = 1L;
foreach(var Condition in Conditions){
var Characters = new Char[Condition.NumberOfCells];
var Sets = new HashSet<Char>[Condition.NumberOfCells];
for(var Index = 0; Index < Characters.Length; Index++) {
Characters[Index] = '\0';
Sets[Index] = new HashSet<char>();
}
var RegularExpression = new Regex($"^{Condition.RegularExpression}$", RegexOptions.Compiled);
foreach(var String in Strings(Characters, Condition, 0)) {
if(RegularExpression.IsMatch(String)){
for(var Index = 0; Index < Characters.Length; Index++) {
Sets[Index].Add(String[Index]);
}
}
}
long Count1 = 1;
for(var Index = 0; Index < Sets.Length; Index++) {
this[GetLocation(Condition.Direction, Index, Condition.Start)].Set = Sets[Index];
Count1 *= Sets[Index].Count;
}
Console.WriteLine($"{Condition.NumberOfCells:D}: {Count1:###,###,##0}: {Condition.RegularExpression}");
Count *= Count1;
}
return Count;
}
private IEnumerable<Char[][]> Resolve(Condition[] Conditions, Int32 ConditionIndex) {
if(ConditionIndex < Conditions.Length) {
var Condition = Conditions[ConditionIndex];
var RegularExpression = new Regex($"^{Condition.RegularExpression}$", RegexOptions.Compiled);
foreach(var String in Strings(Condition)) {
if(RegularExpression.IsMatch(String)){
var SaveSets = new HashSet<Char>[Condition.NumberOfCells];
for(var Index = 0; Index < SaveSets.Length; Index++) {
var Cell = this[GetLocation(Condition.Direction, Index, Condition.Start)];
SaveSets[Index] = Cell.Set;
Cell.Set = new HashSet<char>();
Cell.Set.Add(String[Index]);
}
foreach(var Result in Resolve(Conditions, ConditionIndex + 1)) {
yield return Result;
}
for(var Index = 0; Index < SaveSets.Length; Index++) {
var Cell = this[GetLocation(Condition.Direction, Index, Condition.Start)];
Cell.Set = SaveSets[Index];
}
}
}
} else {
var Cells = new Char[this.Cells.Length][];
for(var Row = 0; Row < this.Cells.Length; Row++) {
Cells[Row] = new Char[this.Cells[Row].Length];
for(var Column = 0; Column < this.Cells[Row].Length; Column++) {
Cells[Row][Column] = this.Cells[Row][Column].Set.Single();
}
}
yield return Cells;
}
}
public IEnumerable<Char[][]> Resolve(Condition[] Conditions) {
foreach(var Result in Resolve(Conditions, 0)){
yield return Result;
}
}
}
private class HoneycombBoard: Board {
private Int32 Size;
public override Location GetLocation(Direction Direction, int Distance, Location From) {
var To = new Location(From);
switch(Direction) {
case Direction.Right:
break;
case Direction.DownLeft:
To.Row += Distance;
break;
case Direction.UpLeft:
To.Row -= Distance;
break;
default:
Debug.Fail($"{nameof(GetLocation)}: Row: {Direction.ToString()}");
break;
}
switch(Direction) {
case Direction.Right:
To.Column += Distance;
break;
case Direction.DownLeft:
if(Size - 1 < To.Row) {
To.Column -= To.Row - (Size - 1);
}
break;
case Direction.UpLeft:
if(To.Row < Size - 1) {
To.Column -= Size - 1 - To.Row;
}
break;
default:
Debug.Fail($"{nameof(GetLocation)}: Column: {Direction.ToString()}");
break;
}
return To;
}
protected override void MakeCells(Object Parameter) {
Size = (Int32)Parameter;
Cells = new Cell[2 * Size - 1][];
for(var Row = 0; Row < Size - 1; Row++) {
Cells[Row] = MakeCells(Size + Row);
Cells[2 * Size - 1 - Row - 1] = MakeCells(Size + Row);
}
Cells[Size - 1] = MakeCells(2 * Size - 1);
}
public HoneycombBoard(Int32 Size, String String): base(Size, String) {
}
}
private class Condition {
public readonly String RegularExpression;
public readonly Int32 NumberOfCells;
public readonly Board.Location Start;
public readonly Board.Direction Direction;
public Condition(
String RegularExpression, Int32 NumberOfCells, Int32 Row, Int32 Column,
Board.Direction Direction
) {
this.RegularExpression = RegularExpression;
this.NumberOfCells = NumberOfCells;
Start = new Board.Location(Row, Column);
this.Direction = Direction;
}
}
static void Main(string[] args) {
var StartTime = DateTime.Now;
var Board = new HoneycombBoard(5, "ABCDEFGHMNORXYZ");
var Conditions = new Condition[]{
new Condition(@"(X|Y|Z).*\1.*\1", 5, 0, 0, Program.Board.Direction.Right),
new Condition(@"[^XYZ]*[XYZ]*", 6, 1, 0, Program.Board.Direction.Right),
new Condition(@"(.).*(.).*\2.*\1", 7, 2, 0, Program.Board.Direction.Right),
new Condition(@"[^X]*X+[^X]*", 8, 3, 0, Program.Board.Direction.Right),
new Condition(@"([^X]|XXY)*", 9, 4, 0, Program.Board.Direction.Right),
new Condition(@"[ABCD]*YO[EFGH]*", 8, 5, 0, Program.Board.Direction.Right),
new Condition(@"[^RX](R|XX)*", 7, 6, 0, Program.Board.Direction.Right),
new Condition(@"(...?)\1*", 6, 7, 0, Program.Board.Direction.Right),
new Condition(@".*Y.*Z+", 5, 8, 0, Program.Board.Direction.Right),
new Condition(@"[^A]B[^C]D[^E]", 5, 0, 0, Program.Board.Direction.DownLeft),
new Condition(@"[^DB]*DB[^DB]*", 6, 0, 1, Program.Board.Direction.DownLeft),
new Condition(@"[BMX]*", 7, 0, 2, Program.Board.Direction.DownLeft),
new Condition(@"(.)(.).*\1.*\2.*\2\1", 8, 0, 3, Program.Board.Direction.DownLeft),
new Condition(@"(XX|YY|ZZ)*[XYZ]{2}.", 9, 0, 4, Program.Board.Direction.DownLeft),
new Condition(@"Y.*N.*", 8, 1, 5, Program.Board.Direction.DownLeft),
new Condition(@"X*F.*G.*H.*", 7, 2, 6, Program.Board.Direction.DownLeft),
new Condition(@"(HY|RM|FX|EH)*", 6, 3, 7, Program.Board.Direction.DownLeft),
new Condition(@".*RZ.*", 5, 4, 8, Program.Board.Direction.DownLeft),
new Condition(@"[^ABE]*BE[^ABE]*Z.*", 5, 8, 0, Program.Board.Direction.UpLeft),
new Condition(@".*[BD]+", 6, 8, 1, Program.Board.Direction.UpLeft),
new Condition(@"[^A]*A[^A]*", 7, 8, 2, Program.Board.Direction.UpLeft),
new Condition(@".*MM.*", 8, 8, 3, Program.Board.Direction.UpLeft),
new Condition(@"[^XO]*XO[^XO]*OX[^XO]*(.)\1", 9, 8, 4, Program.Board.Direction.UpLeft),
new Condition(@"(.+).*\1", 8, 7, 5, Program.Board.Direction.UpLeft),
new Condition(@".*X.X.X.*", 7, 6, 6, Program.Board.Direction.UpLeft),
new Condition(@"H+(..)\1.*", 6, 5, 7, Program.Board.Direction.UpLeft),
new Condition(@".*(MR|CH|RF)[^X]*", 5, 4, 8, Program.Board.Direction.UpLeft)
};
Array.Sort(
Conditions,
(X, Y) => { return X.NumberOfCells.CompareTo(Y.NumberOfCells); }
);
var PreviouseCount = 1L;
for(;;) {
var Count = Board.Reduce(Conditions);
Console.WriteLine($"---- {PreviouseCount:###,###,##0} ⇒ {Count:###,###,##0} --------------------");
if(PreviouseCount == Count){
break;
}
PreviouseCount = Count;
}
foreach(var Cells in Board.Resolve(Conditions)) {
Console.WriteLine("---------------------------------------");
for(var Row = 0; Row < Cells.Length; Row++) {
Console.WriteLine($"{Row:D}: {String.Join("", Cells[Row])}");
}
}
Console.WriteLine("---------------------------------------");
Console.WriteLine($"Time: {(DateTime.Now - StartTime).TotalSeconds:F}");
}
}
}
dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYzsKdXNpbmcgU3lzdGVtLkxpbnE7CnVzaW5nIFN5c3RlbS5UZXh0LlJlZ3VsYXJFeHByZXNzaW9uczsKdXNpbmcgU3lzdGVtLkRpYWdub3N0aWNzOwoKbmFtZXNwYWNlIENvbnNvbGVBcHAxIHsKCWNsYXNzIFByb2dyYW0gewoJCXByaXZhdGUgYWJzdHJhY3QgY2xhc3MgQm9hcmQgewoJCQlwdWJsaWMgZW51bSBEaXJlY3Rpb24gewoJCQkJTGVmdCwKCQkJCVJpZ2h0LAoJCQkJVXAsCgkJCQlEb3duLAoJCQkJRG93bkxlZnQsCgkJCQlVcExlZnQKCQkJfQoJCQlwcm90ZWN0ZWQgU3RyaW5nIFN0cmluZzsKCQkJcHVibGljIGNsYXNzIENlbGwgewoJCQkJcHVibGljIEhhc2hTZXQ8Q2hhcj4gU2V0OwoJCQkJcHVibGljIENoYXIgQ3VycmVudDsKCQkJCXB1YmxpYyBDZWxsKFN0cmluZyBTdHJpbmcpIHsKCQkJCQlTZXQgPSBuZXcgSGFzaFNldDxDaGFyPihTdHJpbmcuVG9BcnJheSgpKTsKCQkJCQlDdXJyZW50ID0gJ1wwJzsKCQkJCX0KCQkJfQoJCQlwdWJsaWMgY2xhc3MgTG9jYXRpb24gewoJCQkJcHVibGljIEludDMyIFJvdzsKCQkJCXB1YmxpYyBJbnQzMiBDb2x1bW47CgkJCQlwdWJsaWMgTG9jYXRpb24oSW50MzIgUm93LCBJbnQzMiBDb2x1bW4pIHsKCQkJCQl0aGlzLlJvdyA9IFJvdzsKCQkJCQl0aGlzLkNvbHVtbiA9IENvbHVtbjsKCQkJCX0KCQkJCXB1YmxpYyBMb2NhdGlvbihMb2NhdGlvbiBGcm9tKSB7CgkJCQkJUm93ID0gRnJvbS5Sb3c7CgkJCQkJQ29sdW1uID0gRnJvbS5Db2x1bW47CgkJCQl9CgkJCX0KCQkJcHJvdGVjdGVkIENlbGxbXVtdIENlbGxzOwoJCQlwdWJsaWMgYWJzdHJhY3QgTG9jYXRpb24gR2V0TG9jYXRpb24oRGlyZWN0aW9uIERpcmVjdGlvbiwgSW50MzIgRGlzdGFuY2UsIExvY2F0aW9uIExvY2F0b24pOwoJCQlwcm90ZWN0ZWQgYWJzdHJhY3Qgdm9pZCBNYWtlQ2VsbHMoT2JqZWN0IFBhcmFtZXRlcik7CgkJCXB1YmxpYyBCb2FyZChPYmplY3QgUGFyYW1ldGVyLCBTdHJpbmcgU3RyaW5nKSB7CgkJCQl0aGlzLlN0cmluZyA9IFN0cmluZzsKCQkJCU1ha2VDZWxscyhQYXJhbWV0ZXIpOwoJCQkJdmFyIENoYXJhY3RlcnMgPSBTdHJpbmcuVG9BcnJheSgpOwoJCQkJZm9yZWFjaCh2YXIgUm93IGluIENlbGxzKSB7CgkJCQkJZm9yZWFjaCh2YXIgQ2VsbCBpbiBSb3cpIHsKCQkJCQkJQ2VsbC5TZXQgPSBuZXcgSGFzaFNldDxDaGFyPihDaGFyYWN0ZXJzKTsKCQkJCQl9CgkJCQl9CgkJCX0KCQkJcHJvdGVjdGVkIENlbGxbXSBNYWtlQ2VsbHMoSW50MzIgU2l6ZSkgewoJCQkJdmFyIENlbGxzID0gbmV3IENlbGxbU2l6ZV07CgkJCQlmb3IodmFyIEluZGV4ID0gMDsgSW5kZXggPCBDZWxscy5MZW5ndGg7IEluZGV4KyspIHsKCQkJCQlDZWxsc1tJbmRleF0gPSBuZXcgQ2VsbChTdHJpbmcpOwoJCQkJfQoJCQkJcmV0dXJuIENlbGxzOwoJCQl9CgkJCXB1YmxpYyBJRW51bWVyYWJsZTxzdHJpbmc+IFN0cmluZ3MoQ2hhciBbXSBDaGFyYWN0ZXJzLCBDb25kaXRpb24gQ29uZGl0aW9uLCBpbnQgRGlzdGFuY2UpIHsKCQkJCWlmKERpc3RhbmNlIDwgQ29uZGl0aW9uLk51bWJlck9mQ2VsbHMpIHsKCQkJCQlmb3JlYWNoKHZhciBDaGFyYWN0ZXIgaW4gdGhpc1tHZXRMb2NhdGlvbihDb25kaXRpb24uRGlyZWN0aW9uLCBEaXN0YW5jZSwgQ29uZGl0aW9uLlN0YXJ0KV0uU2V0KSB7CgkJCQkJCUNoYXJhY3RlcnNbRGlzdGFuY2VdID0gQ2hhcmFjdGVyOwoJCQkJCQlmb3JlYWNoKHZhciBTdHJpbmcgaW4gU3RyaW5ncyhDaGFyYWN0ZXJzLCBDb25kaXRpb24sIERpc3RhbmNlICsgMSkpewoJCQkJCQkJeWllbGQgcmV0dXJuIFN0cmluZzsKCQkJCQkJfQoJCQkJCX0KCQkJCX0gZWxzZSB7CgkJCQkJeWllbGQgcmV0dXJuIHN0cmluZy5Kb2luKCIiLCBDaGFyYWN0ZXJzKTsKCQkJCX0KCQkJfQoJCQlwcml2YXRlIElFbnVtZXJhYmxlPFN0cmluZz4gU3RyaW5ncyhDb25kaXRpb24gQ29uZGl0aW9uKSB7CgkJCQl2YXIgQ2hhcmFjdGVycyA9IG5ldyBDaGFyW0NvbmRpdGlvbi5OdW1iZXJPZkNlbGxzXTsKCQkJCWZvcih2YXIgSW5kZXggPSAwOyBJbmRleCA8IENoYXJhY3RlcnMuTGVuZ3RoOyBJbmRleCsrKSB7CgkJCQkJQ2hhcmFjdGVyc1tJbmRleF0gPSAnXDAnOwoJCQkJfQoJCQkJZm9yZWFjaCh2YXIgU3RyaW5nIGluIFN0cmluZ3MoQ2hhcmFjdGVycywgQ29uZGl0aW9uLCAwKSkgewoJCQkJCXlpZWxkIHJldHVybiBTdHJpbmc7CgkJCQl9CgkJCX0KCQkJcHVibGljIENlbGwgdGhpc1tMb2NhdGlvbiBMb2NhdGlvbl17CgkJCQlnZXQgewoJCQkJCXJldHVybiBDZWxsc1tMb2NhdGlvbi5Sb3ddW0xvY2F0aW9uLkNvbHVtbl07CgkJCQl9CgkJCX0KCQkJcHVibGljIGxvbmcgUmVkdWNlKENvbmRpdGlvbltdIENvbmRpdGlvbnMpIHsKCQkJCXZhciBDb3VudCA9IDFMOwoJCQkJZm9yZWFjaCh2YXIgQ29uZGl0aW9uIGluIENvbmRpdGlvbnMpewoJCQkJCXZhciBDaGFyYWN0ZXJzID0gbmV3IENoYXJbQ29uZGl0aW9uLk51bWJlck9mQ2VsbHNdOwoJCQkJCXZhciBTZXRzID0gbmV3IEhhc2hTZXQ8Q2hhcj5bQ29uZGl0aW9uLk51bWJlck9mQ2VsbHNdOwoJCQkJCWZvcih2YXIgSW5kZXggPSAwOyBJbmRleCA8IENoYXJhY3RlcnMuTGVuZ3RoOyBJbmRleCsrKSB7CgkJCQkJCUNoYXJhY3RlcnNbSW5kZXhdID0gJ1wwJzsKCQkJCQkJU2V0c1tJbmRleF0gPSBuZXcgSGFzaFNldDxjaGFyPigpOwoJCQkJCX0KCQkJCQl2YXIgUmVndWxhckV4cHJlc3Npb24gPSBuZXcgUmVnZXgoJCJee0NvbmRpdGlvbi5SZWd1bGFyRXhwcmVzc2lvbn0kIiwgUmVnZXhPcHRpb25zLkNvbXBpbGVkKTsKCQkJCQlmb3JlYWNoKHZhciBTdHJpbmcgaW4gU3RyaW5ncyhDaGFyYWN0ZXJzLCBDb25kaXRpb24sIDApKSB7CgkJCQkJCWlmKFJlZ3VsYXJFeHByZXNzaW9uLklzTWF0Y2goU3RyaW5nKSl7CgkJCQkJCQlmb3IodmFyIEluZGV4ID0gMDsgSW5kZXggPCBDaGFyYWN0ZXJzLkxlbmd0aDsgSW5kZXgrKykgewoJCQkJCQkJCVNldHNbSW5kZXhdLkFkZChTdHJpbmdbSW5kZXhdKTsKCQkJCQkJCX0KCQkJCQkJfQoJCQkJCX0KCQkJCQlsb25nIENvdW50MSA9IDE7CgkJCQkJZm9yKHZhciBJbmRleCA9IDA7IEluZGV4IDwgU2V0cy5MZW5ndGg7IEluZGV4KyspIHsKCQkJCQkJdGhpc1tHZXRMb2NhdGlvbihDb25kaXRpb24uRGlyZWN0aW9uLCBJbmRleCwgQ29uZGl0aW9uLlN0YXJ0KV0uU2V0ID0gU2V0c1tJbmRleF07CgkJCQkJCUNvdW50MSAqPSBTZXRzW0luZGV4XS5Db3VudDsKCQkJCQl9CgkJCQkJQ29uc29sZS5Xcml0ZUxpbmUoJCJ7Q29uZGl0aW9uLk51bWJlck9mQ2VsbHM6RH06IHtDb3VudDE6IyMjLCMjIywjIzB9OiB7Q29uZGl0aW9uLlJlZ3VsYXJFeHByZXNzaW9ufSIpOwoJCQkJCUNvdW50ICo9IENvdW50MTsKCQkJCX0KCQkJCXJldHVybiBDb3VudDsKCQkJfQoJCQlwcml2YXRlIElFbnVtZXJhYmxlPENoYXJbXVtdPiBSZXNvbHZlKENvbmRpdGlvbltdIENvbmRpdGlvbnMsIEludDMyIENvbmRpdGlvbkluZGV4KSB7CgkJCQlpZihDb25kaXRpb25JbmRleCA8IENvbmRpdGlvbnMuTGVuZ3RoKSB7CgkJCQkJdmFyIENvbmRpdGlvbiA9IENvbmRpdGlvbnNbQ29uZGl0aW9uSW5kZXhdOwoJCQkJCXZhciBSZWd1bGFyRXhwcmVzc2lvbiA9IG5ldyBSZWdleCgkIl57Q29uZGl0aW9uLlJlZ3VsYXJFeHByZXNzaW9ufSQiLCBSZWdleE9wdGlvbnMuQ29tcGlsZWQpOwoJCQkJCWZvcmVhY2godmFyIFN0cmluZyBpbiBTdHJpbmdzKENvbmRpdGlvbikpIHsKCQkJCQkJaWYoUmVndWxhckV4cHJlc3Npb24uSXNNYXRjaChTdHJpbmcpKXsKCQkJCQkJCXZhciBTYXZlU2V0cyA9IG5ldyBIYXNoU2V0PENoYXI+W0NvbmRpdGlvbi5OdW1iZXJPZkNlbGxzXTsKCQkJCQkJCWZvcih2YXIgSW5kZXggPSAwOyBJbmRleCA8IFNhdmVTZXRzLkxlbmd0aDsgSW5kZXgrKykgewoJCQkJCQkJCXZhciBDZWxsID0gdGhpc1tHZXRMb2NhdGlvbihDb25kaXRpb24uRGlyZWN0aW9uLCBJbmRleCwgQ29uZGl0aW9uLlN0YXJ0KV07CgkJCQkJCQkJU2F2ZVNldHNbSW5kZXhdID0gQ2VsbC5TZXQ7CgkJCQkJCQkJQ2VsbC5TZXQgPSBuZXcgSGFzaFNldDxjaGFyPigpOwoJCQkJCQkJCUNlbGwuU2V0LkFkZChTdHJpbmdbSW5kZXhdKTsKCQkJCQkJCX0KCQkJCQkJCWZvcmVhY2godmFyIFJlc3VsdCBpbiBSZXNvbHZlKENvbmRpdGlvbnMsIENvbmRpdGlvbkluZGV4ICsgMSkpIHsKCQkJCQkJCQl5aWVsZCByZXR1cm4gUmVzdWx0OwoJCQkJCQkJfQoJCQkJCQkJZm9yKHZhciBJbmRleCA9IDA7IEluZGV4IDwgU2F2ZVNldHMuTGVuZ3RoOyBJbmRleCsrKSB7CgkJCQkJCQkJdmFyIENlbGwgPSB0aGlzW0dldExvY2F0aW9uKENvbmRpdGlvbi5EaXJlY3Rpb24sIEluZGV4LCBDb25kaXRpb24uU3RhcnQpXTsKCQkJCQkJCQlDZWxsLlNldCA9IFNhdmVTZXRzW0luZGV4XTsKCQkJCQkJCX0KCQkJCQkJfQoJCQkJCX0KCQkJCX0gZWxzZSB7CgkJCQkJdmFyIENlbGxzID0gbmV3IENoYXJbdGhpcy5DZWxscy5MZW5ndGhdW107CgkJCQkJZm9yKHZhciBSb3cgPSAwOyBSb3cgPCB0aGlzLkNlbGxzLkxlbmd0aDsgUm93KyspIHsKCQkJCQkJQ2VsbHNbUm93XSA9IG5ldyBDaGFyW3RoaXMuQ2VsbHNbUm93XS5MZW5ndGhdOwoJCQkJCQlmb3IodmFyIENvbHVtbiA9IDA7IENvbHVtbiA8IHRoaXMuQ2VsbHNbUm93XS5MZW5ndGg7IENvbHVtbisrKSB7CgkJCQkJCQlDZWxsc1tSb3ddW0NvbHVtbl0gPSB0aGlzLkNlbGxzW1Jvd11bQ29sdW1uXS5TZXQuU2luZ2xlKCk7CgkJCQkJCX0KCQkJCQl9CgkJCQkJeWllbGQgcmV0dXJuIENlbGxzOwoJCQkJfQoJCQl9CgkJCXB1YmxpYyBJRW51bWVyYWJsZTxDaGFyW11bXT4gUmVzb2x2ZShDb25kaXRpb25bXSBDb25kaXRpb25zKSB7CgkJCQlmb3JlYWNoKHZhciBSZXN1bHQgaW4gUmVzb2x2ZShDb25kaXRpb25zLCAwKSl7CgkJCQkJeWllbGQgcmV0dXJuIFJlc3VsdDsKCQkJCX0KCQkJfQoJCX0KCQlwcml2YXRlIGNsYXNzIEhvbmV5Y29tYkJvYXJkOiBCb2FyZCB7CgkJCXByaXZhdGUgSW50MzIgU2l6ZTsKCQkJcHVibGljIG92ZXJyaWRlIExvY2F0aW9uIEdldExvY2F0aW9uKERpcmVjdGlvbiBEaXJlY3Rpb24sIGludCBEaXN0YW5jZSwgTG9jYXRpb24gRnJvbSkgewoJCQkJdmFyIFRvID0gbmV3IExvY2F0aW9uKEZyb20pOwoJCQkJc3dpdGNoKERpcmVjdGlvbikgewoJCQkJY2FzZSBEaXJlY3Rpb24uUmlnaHQ6CgkJCQkJYnJlYWs7CgkJCQljYXNlIERpcmVjdGlvbi5Eb3duTGVmdDoKCQkJCQlUby5Sb3cgKz0gRGlzdGFuY2U7CgkJCQkJYnJlYWs7CgkJCQljYXNlIERpcmVjdGlvbi5VcExlZnQ6CgkJCQkJVG8uUm93IC09IERpc3RhbmNlOwoJCQkJCWJyZWFrOwoJCQkJZGVmYXVsdDoKCQkJCQlEZWJ1Zy5GYWlsKCQie25hbWVvZihHZXRMb2NhdGlvbil9OiBSb3c6IHtEaXJlY3Rpb24uVG9TdHJpbmcoKX0iKTsKCQkJCQlicmVhazsKCQkJCX0KCQkJCXN3aXRjaChEaXJlY3Rpb24pIHsKCQkJCWNhc2UgRGlyZWN0aW9uLlJpZ2h0OgoJCQkJCVRvLkNvbHVtbiArPSBEaXN0YW5jZTsKCQkJCQlicmVhazsKCQkJCWNhc2UgRGlyZWN0aW9uLkRvd25MZWZ0OgoJCQkJCWlmKFNpemUgLSAxIDwgVG8uUm93KSB7CgkJCQkJCVRvLkNvbHVtbiAtPSBUby5Sb3cgLSAoU2l6ZSAtIDEpOwoJCQkJCX0KCQkJCQlicmVhazsKCQkJCWNhc2UgRGlyZWN0aW9uLlVwTGVmdDoKCQkJCQlpZihUby5Sb3cgPCBTaXplIC0gMSkgewoJCQkJCQlUby5Db2x1bW4gLT0gU2l6ZSAtIDEgLSBUby5Sb3c7CgkJCQkJfQoJCQkJCWJyZWFrOwoJCQkJZGVmYXVsdDoKCQkJCQlEZWJ1Zy5GYWlsKCQie25hbWVvZihHZXRMb2NhdGlvbil9OiBDb2x1bW46IHtEaXJlY3Rpb24uVG9TdHJpbmcoKX0iKTsKCQkJCQlicmVhazsKCQkJCX0KCQkJCXJldHVybiBUbzsKCQkJfQoJCQlwcm90ZWN0ZWQgb3ZlcnJpZGUgdm9pZCBNYWtlQ2VsbHMoT2JqZWN0IFBhcmFtZXRlcikgewoJCQkJU2l6ZSA9IChJbnQzMilQYXJhbWV0ZXI7CgkJCQlDZWxscyA9IG5ldyBDZWxsWzIgKiBTaXplIC0gMV1bXTsKCQkJCWZvcih2YXIgUm93ID0gMDsgUm93IDwgU2l6ZSAtIDE7IFJvdysrKSB7CgkJCQkJQ2VsbHNbUm93XSA9IE1ha2VDZWxscyhTaXplICsgUm93KTsKCQkJCQlDZWxsc1syICogU2l6ZSAtIDEgLSBSb3cgLSAxXSA9IE1ha2VDZWxscyhTaXplICsgUm93KTsKCQkJCX0KCQkJCUNlbGxzW1NpemUgLSAxXSA9IE1ha2VDZWxscygyICogU2l6ZSAtIDEpOwoJCQl9CgkJCXB1YmxpYyBIb25leWNvbWJCb2FyZChJbnQzMiBTaXplLCBTdHJpbmcgU3RyaW5nKTogYmFzZShTaXplLCBTdHJpbmcpIHsKCQkJfQoJCX0KCQlwcml2YXRlIGNsYXNzIENvbmRpdGlvbiB7CgkJCXB1YmxpYyByZWFkb25seSBTdHJpbmcgUmVndWxhckV4cHJlc3Npb247CgkJCXB1YmxpYyByZWFkb25seSBJbnQzMiBOdW1iZXJPZkNlbGxzOwoJCQlwdWJsaWMgcmVhZG9ubHkgQm9hcmQuTG9jYXRpb24gU3RhcnQ7CgkJCXB1YmxpYyByZWFkb25seSBCb2FyZC5EaXJlY3Rpb24gRGlyZWN0aW9uOwoJCQlwdWJsaWMgQ29uZGl0aW9uKAoJCQkJU3RyaW5nIFJlZ3VsYXJFeHByZXNzaW9uLCBJbnQzMiBOdW1iZXJPZkNlbGxzLCBJbnQzMiBSb3csIEludDMyIENvbHVtbiwKCQkJCUJvYXJkLkRpcmVjdGlvbiBEaXJlY3Rpb24KCQkJKSB7CgkJCQl0aGlzLlJlZ3VsYXJFeHByZXNzaW9uID0gUmVndWxhckV4cHJlc3Npb247CgkJCQl0aGlzLk51bWJlck9mQ2VsbHMgPSBOdW1iZXJPZkNlbGxzOwoJCQkJU3RhcnQgPSBuZXcgQm9hcmQuTG9jYXRpb24oUm93LCBDb2x1bW4pOwoJCQkJdGhpcy5EaXJlY3Rpb24gPSBEaXJlY3Rpb247CgkJCX0KCQl9CgkJc3RhdGljIHZvaWQgTWFpbihzdHJpbmdbXSBhcmdzKSB7CgkJCXZhciBTdGFydFRpbWUgPSBEYXRlVGltZS5Ob3c7CgkJCXZhciBCb2FyZCA9IG5ldyBIb25leWNvbWJCb2FyZCg1LCAiQUJDREVGR0hNTk9SWFlaIik7CgkJCXZhciBDb25kaXRpb25zID0gbmV3IENvbmRpdGlvbltdewoJCQkJbmV3IENvbmRpdGlvbihAIihYfFl8WikuKlwxLipcMSIsIDUsIDAsIDAsIFByb2dyYW0uQm9hcmQuRGlyZWN0aW9uLlJpZ2h0KSwKCQkJCW5ldyBDb25kaXRpb24oQCJbXlhZWl0qW1hZWl0qIiwgNiwgMSwgMCwgUHJvZ3JhbS5Cb2FyZC5EaXJlY3Rpb24uUmlnaHQpLAoJCQkJbmV3IENvbmRpdGlvbihAIiguKS4qKC4pLipcMi4qXDEiLCA3LCAyLCAwLCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5SaWdodCksCgkJCQluZXcgQ29uZGl0aW9uKEAiW15YXSpYK1teWF0qIiwgOCwgMywgMCwgUHJvZ3JhbS5Cb2FyZC5EaXJlY3Rpb24uUmlnaHQpLAoJCQkJbmV3IENvbmRpdGlvbihAIihbXlhdfFhYWSkqIiwgOSwgNCwgMCwgUHJvZ3JhbS5Cb2FyZC5EaXJlY3Rpb24uUmlnaHQpLAoJCQkJbmV3IENvbmRpdGlvbihAIltBQkNEXSpZT1tFRkdIXSoiLCA4LCA1LCAwLCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5SaWdodCksCgkJCQluZXcgQ29uZGl0aW9uKEAiW15SWF0oUnxYWCkqIiwgNywgNiwgMCwgUHJvZ3JhbS5Cb2FyZC5EaXJlY3Rpb24uUmlnaHQpLAoJCQkJbmV3IENvbmRpdGlvbihAIiguLi4/KVwxKiIsIDYsIDcsIDAsIFByb2dyYW0uQm9hcmQuRGlyZWN0aW9uLlJpZ2h0KSwKCQkJCW5ldyBDb25kaXRpb24oQCIuKlkuKlorIiwgNSwgOCwgMCwgUHJvZ3JhbS5Cb2FyZC5EaXJlY3Rpb24uUmlnaHQpLAoJCQkJbmV3IENvbmRpdGlvbihAIlteQV1CW15DXURbXkVdIiwgNSwgMCwgMCwgUHJvZ3JhbS5Cb2FyZC5EaXJlY3Rpb24uRG93bkxlZnQpLAoJCQkJbmV3IENvbmRpdGlvbihAIlteREJdKkRCW15EQl0qIiwgNiwgMCwgMSwgUHJvZ3JhbS5Cb2FyZC5EaXJlY3Rpb24uRG93bkxlZnQpLAoJCQkJbmV3IENvbmRpdGlvbihAIltCTVhdKiIsIDcsIDAsIDIsIFByb2dyYW0uQm9hcmQuRGlyZWN0aW9uLkRvd25MZWZ0KSwKCQkJCW5ldyBDb25kaXRpb24oQCIoLikoLikuKlwxLipcMi4qXDJcMSIsIDgsIDAsIDMsIFByb2dyYW0uQm9hcmQuRGlyZWN0aW9uLkRvd25MZWZ0KSwKCQkJCW5ldyBDb25kaXRpb24oQCIoWFh8WVl8WlopKltYWVpdezJ9LiIsIDksIDAsIDQsIFByb2dyYW0uQm9hcmQuRGlyZWN0aW9uLkRvd25MZWZ0KSwKCQkJCW5ldyBDb25kaXRpb24oQCJZLipOLioiLCA4LCAxLCA1LCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5Eb3duTGVmdCksCgkJCQluZXcgQ29uZGl0aW9uKEAiWCpGLipHLipILioiLCA3LCAyLCA2LCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5Eb3duTGVmdCksCgkJCQluZXcgQ29uZGl0aW9uKEAiKEhZfFJNfEZYfEVIKSoiLCA2LCAzLCA3LCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5Eb3duTGVmdCksCgkJCQluZXcgQ29uZGl0aW9uKEAiLipSWi4qIiwgNSwgNCwgOCwgUHJvZ3JhbS5Cb2FyZC5EaXJlY3Rpb24uRG93bkxlZnQpLAoJCQkJbmV3IENvbmRpdGlvbihAIlteQUJFXSpCRVteQUJFXSpaLioiLCA1LCA4LCAwLCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5VcExlZnQpLAoJCQkJbmV3IENvbmRpdGlvbihAIi4qW0JEXSsiLCA2LCA4LCAxLCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5VcExlZnQpLAoJCQkJbmV3IENvbmRpdGlvbihAIlteQV0qQVteQV0qIiwgNywgOCwgMiwgUHJvZ3JhbS5Cb2FyZC5EaXJlY3Rpb24uVXBMZWZ0KSwKCQkJCW5ldyBDb25kaXRpb24oQCIuKk1NLioiLCA4LCA4LCAzLCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5VcExlZnQpLAoJCQkJbmV3IENvbmRpdGlvbihAIlteWE9dKlhPW15YT10qT1hbXlhPXSooLilcMSIsIDksIDgsIDQsIFByb2dyYW0uQm9hcmQuRGlyZWN0aW9uLlVwTGVmdCksCgkJCQluZXcgQ29uZGl0aW9uKEAiKC4rKS4qXDEiLCA4LCA3LCA1LCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5VcExlZnQpLAoJCQkJbmV3IENvbmRpdGlvbihAIi4qWC5YLlguKiIsIDcsIDYsIDYsIFByb2dyYW0uQm9hcmQuRGlyZWN0aW9uLlVwTGVmdCksCgkJCQluZXcgQ29uZGl0aW9uKEAiSCsoLi4pXDEuKiIsIDYsIDUsIDcsIFByb2dyYW0uQm9hcmQuRGlyZWN0aW9uLlVwTGVmdCksCgkJCQluZXcgQ29uZGl0aW9uKEAiLiooTVJ8Q0h8UkYpW15YXSoiLCA1LCA0LCA4LCBQcm9ncmFtLkJvYXJkLkRpcmVjdGlvbi5VcExlZnQpCgkJCX07CgkJCUFycmF5LlNvcnQoCgkJCQlDb25kaXRpb25zLAoJCQkJKFgsIFkpID0+IHsgcmV0dXJuIFguTnVtYmVyT2ZDZWxscy5Db21wYXJlVG8oWS5OdW1iZXJPZkNlbGxzKTsgfQoJCQkpOwoJCQl2YXIgUHJldmlvdXNlQ291bnQgPSAxTDsKCQkJZm9yKDs7KSB7CgkJCQl2YXIgQ291bnQgPSBCb2FyZC5SZWR1Y2UoQ29uZGl0aW9ucyk7CgkJCQlDb25zb2xlLldyaXRlTGluZSgkIi0tLS0ge1ByZXZpb3VzZUNvdW50OiMjIywjIyMsIyMwfSDih5Ige0NvdW50OiMjIywjIyMsIyMwfSAtLS0tLS0tLS0tLS0tLS0tLS0tLSIpOwoJCQkJaWYoUHJldmlvdXNlQ291bnQgPT0gQ291bnQpewoJCQkJCWJyZWFrOwoJCQkJfQoJCQkJUHJldmlvdXNlQ291bnQgPSBDb3VudDsKCQkJfQoJCQlmb3JlYWNoKHZhciBDZWxscyBpbiBCb2FyZC5SZXNvbHZlKENvbmRpdGlvbnMpKSB7CgkJCQlDb25zb2xlLldyaXRlTGluZSgiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIik7CgkJCQlmb3IodmFyIFJvdyA9IDA7IFJvdyA8IENlbGxzLkxlbmd0aDsgUm93KyspIHsKCQkJCQlDb25zb2xlLldyaXRlTGluZSgkIntSb3c6RH06IHtTdHJpbmcuSm9pbigiIiwgQ2VsbHNbUm93XSl9Iik7CgkJCQl9CgkJCX0KCQkJQ29uc29sZS5Xcml0ZUxpbmUoIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSIpOwoJCQlDb25zb2xlLldyaXRlTGluZSgkIlRpbWU6IHsoRGF0ZVRpbWUuTm93IC0gU3RhcnRUaW1lKS5Ub3RhbFNlY29uZHM6Rn0iKTsKCQl9Cgl9Cn0=