import java.lang.Math;
import java.util.*;
public class SaveIntegers
{
public static int[][] timeArray;
public static void main
(String[] args
) {
// Test Case 1
//int[][] times = {{0, 1, 1, 1, 1}, {1, 0, 1, 1, 1}, {1, 1, 0, 1, 1}, {1, 1, 1, 0, 1}, {1, 1, 1, 1, 0}};
//int time_limit = 3;
// Test Case 2
//int[][] times = {{0, 2, 2, 2, -1}, {9, 0, 2, 2, -1}, {9, 3, 0, 2, -1}, {9, 3, 2, 0, -1}, {9, 3, 2, 2, 0}};
//int time_limit = 1;
// Infinite Loop Test 1
//int[][] times = {{0, 2, 2, 2, -1}, {9, -1, 2, 2, -1}, {9, 3, 0, 2, -1}, {9, 3, 2, 0, -1}, {9, 3, 2, 2, 0}};
//int time_limit = 3;
// Infinite Loop Test 2
//int[][] times = {{0, 9, 9, 9, 3}, {9, 0, 9, 1, 9}, {1, 9, 0, 9, 9}, {9, 9, -5, 0, 9}, {9, -2, 9, 9, 0}};
//int time_limit = 3;
// lots of looping
int[][] times = {{0, 0, 0, 0, 0}, {1, 0, 9, 9, 9}, {1, 9, 0, 9, 9}, {1, 9, 9, 0, 9}, {0, 9, 9, 9, 0}};
int time_limit = 8;
timeArray = times;
List<Integer> savedBunnies = new ArrayList<Integer>();
//Search for time-gaining infinite loops. Return all if found.
if(findTimeGainingInfiniteLoop())
{
System.
out.
println("Infinite loops!"); //add all bunnies (none at start or bulkhead)
for (int i = 0; i < timeArray.length - 2; i++)
{
savedBunnies.add(i);
}
}
else
{
//TODO: can this get integrated into findBestPath?
//List<List<Integer>> escapePaths = findAllMinLengthPaths(0, timeArray.length - 1, new ArrayList<Integer>());
//if (getDistanceFromPath(escapePaths.get(0)) <= time_limit)
//{
//Can't even save yourself!
//System.out.println("Can't even save yourself!");
//}
List<Integer> bestPath = findBestPath(0, timeArray.length - 1, time_limit, new ArrayList<Integer>());
System.
out.
println("Best path: " + Arrays.
toString(bestPath.
toArray())); savedBunnies = getSavedBunniesFromPath(bestPath);
}
System.
out.
println("Saved bunnies: " + Arrays.
toString(savedBunnies.
toArray())); }
public static Boolean findTimeGainingInfiniteLoop
() {
for (int i = 0; i < timeArray.length; i++)
{
for (int j = 0; j < timeArray[i].length; j++)
{
if (i == j && timeArray[i][j] < 0)
{
//System.out.println("location " + i + " can loop itself for infinite time!");
return true;
}
if (timeArray[i][j] < 0)
{
// Going from location i to location j adds to the clock!
// Can we get from j to i in one less time than is added?
return findPathWithinTime(j, i, (timeArray[i][j] * -1) - 1);
}
}
}
return false;
}
public static Boolean findPathWithinTime
(int start,
int finish,
int time
) {
//System.out.println("Looking for path from " + start + " to " + finish + " below " + time + " excluding " + Arrays.toString(history.toArray()));
if (timeArray[start][finish] <= time)
{
//System.out.println("Found path from " + start + " to " + finish + " under " + time);
return true;
}
if (start == finish)
{
//System.out.println("Find path to self: " + start);
if (time >= 0)
{
return true;
}
else
{
return false;
}
}
List<List<Integer>> minPaths = findAllMinLengthPaths(start, finish, new ArrayList<Integer>());
return getDistanceFromPath(minPaths.get(0)) <= time;
}
public static List<List<Integer>> findAllMinLengthPaths(int start, int finish, List<Integer> history)
{
List<List<Integer>> listOfPaths = new ArrayList<List<Integer>>();
//System.out.println("Finding paths from " + start + " to " + finish + " excluding " + Arrays.toString(history.toArray()));
history.add(start);
List<Integer> directPath = new ArrayList<Integer>(history);
//Might be able to remove this if/check and always add finish to directPath
if (!directPath.contains(finish))
{
directPath.add(finish);
}
listOfPaths.add(directPath);
//for each possible next step, get the min length paths and update listOfPaths
for (int j = 0; j < timeArray[start].length; j++)
{
if (!history.contains(j) && j != finish)
{
List<List<Integer>> nextPaths = findAllMinLengthPaths(j, finish, new ArrayList<Integer>(history));
int nextPathLength = getDistanceFromPath(nextPaths.get(0));
int currentPathLength = getDistanceFromPath(listOfPaths.get(0));
if (currentPathLength > nextPathLength)
{
//System.out.println("Shorter path from " + start + " to " + finish + ": " + Arrays.toString(nextPaths.get(0).toArray()));
listOfPaths.clear();
listOfPaths.addAll(nextPaths);
}
if (currentPathLength == nextPathLength)
{
listOfPaths.addAll(nextPaths);
}
}
}
if (listOfPaths.size() > 0)
{
//System.out.println("Found paths from " + start + " to " + finish + " including " + Arrays.toString(listOfPaths.get(0).toArray()));
}
else
{
System.
out.
println("Found no paths from " + start
+ " to " + finish
+ " shorter than " + Arrays.
toString(history.
toArray())); }
return listOfPaths;
}
public static List<Integer> findBestPath(int start, int finish, int time_limit, List<Integer> history)
{
List
<Integer
> targetList
= new ArrayList
<Integer
>(Arrays.
asList(0,
1,
0,
2,
0,
3,
0)); history.add(start);
if (history.equals(targetList))
{
System.
out.
println("Found solution: " + Arrays.
toString(history.
toArray())); }
List<Integer> workingPath = new ArrayList<Integer>(history);
if (workingPath.get(workingPath.size() - 1) != finish)
{
workingPath.add(finish);
}
//for each possible next step, get the best paths and update workingPath
for (int j = 0; j < timeArray[start].length; j++)
{
if (validMove(j, history))
{
if (history.equals(targetList))
{
System.
out.
println("Calling findBestPath for " + Arrays.
toString(history.
toArray()) + " to " + j
); }
List<Integer> nextPath = findBestPath(j, finish, time_limit, new ArrayList<Integer>(history));
targetList.add(4);
if (nextPath.equals(targetList))
{
System.
out.
println("Now looking at answer " + Arrays.
toString(nextPath.
toArray()) + ", j: " + j
+ " history: " + Arrays.
toString(history.
toArray())); }
int nextPathLength = getDistanceFromPath(nextPath);
if (nextPath.size() > history.size() + 2)
{
System.
out.
println("Size of " + Arrays.
toString(nextPath.
toArray()) + " is > 2 more than " + Arrays.
toString(history.
toArray())); }
if (time_limit >= nextPathLength)
{
if (nextPath.equals(targetList))
{
System.
out.
println("Answer valid for " + Arrays.
toString(history.
toArray()) + " with j: " + j
); }
//System.out.println("Length " + nextPathLength + " of " + Arrays.toString(nextPath.toArray()) + " is under " + time_limit);
List<Integer> currentPathSaved = getSavedBunniesFromPath(workingPath);
List<Integer> nextPathSaved = getSavedBunniesFromPath(nextPath);
if (isBetterListOfSavedBunnies(nextPathSaved, currentPathSaved))
{
System.
out.
println("Replacing path " + Arrays.
toString(workingPath.
toArray()) + " with " + Arrays.
toString(nextPath.
toArray())); workingPath = nextPath;
}
//else
//{
// System.out.println("List " + Arrays.toString(nextPathSaved.toArray()) + " isn't better than " + Arrays.toString(currentPathSaved.toArray()));
//}
}
//else
//{
// System.out.println("Length " + nextPathLength + " of " + Arrays.toString(nextPath.toArray()) + " is over " + time_limit);
//}
}
//else
//{
// System.out.println("Invalid move " + j + " on " + Arrays.toString(history.toArray()));
//}
//if (j == timeArray[start].length - 1)
//{
// System.out.println("Finishing " + Arrays.toString(history.toArray()) + " and workingPath is " + Arrays.toString(workingPath.toArray()));
//}
}
if (history.equals(targetList))
{
System.
out.
println("Returning answer: " + Arrays.
toString(workingPath.
toArray())); }
return workingPath;
}
public static Boolean validMove
(int move, List
<Integer
> history
) {
//We've already found infinite loops by standing still. Don't allow others.
if (move == history.get(history.size() - 1))
{
return false;
}
//Moving to the bulkhead or start are almost always allowed
if (move == timeArray.length - 1 || move == 0)
{
//Don't allow start-bulkhead infinite loops
if (history.size() >= 2 &&
history.get(history.size() - 1) == 0 &&
history.get(history.size() - 2) == timeArray.length - 1)
{
//System.out.println("Rejecting loop: " + Arrays.toString(history.toArray()) + ", move " + move);
return false;
}
return true;
}
//Otherwise it's only valid if we haven't already grabbed the bunny
return !history.contains(move);
}
public static int getDistanceFromPath(List<Integer> history)
{
//System.out.print("Distance of " + Arrays.toString(history.toArray()) + " is ");
if (history.size() <= 1)
{
//System.out.print(0);
//System.out.println();
return 0;
}
//int historyHash = history.hashCode();
//if (hmap.containsKey(historyHash))
//{
// System.out.print("hashed as " + hmap.get(historyHash));
// System.out.println();
// return hmap.get(historyHash);
//}
Iterator<Integer> distanceIter = history.iterator();
int distance = 0, start = distanceIter.next();
while (distanceIter.hasNext())
{
int next = distanceIter.next();
distance += timeArray[start][next];
start = next;
}
//System.out.print(distance);
// System.out.println();
//hmap.put(historyHash, distance);
return distance;
}
public static List<Integer> getSavedBunniesFromPath(List<Integer> history)
{
List<Integer> savedBunnies = new ArrayList<Integer>();
Iterator<Integer> saveIter = history.iterator();
while (saveIter.hasNext())
{
int move = saveIter.next();
if (move != 0 && move != timeArray.length - 1)
{
savedBunnies.add(move-1);
}
}
//System.out.println("Saved " + Arrays.toString(savedBunnies.toArray()) + " from " + Arrays.toString(history.toArray()));
return savedBunnies;
}
public static Boolean isBetterListOfSavedBunnies
(List
<Integer
> firstBunnyList, List
<Integer
> secondBunnyList
) {
if (firstBunnyList.size() == secondBunnyList.size())
{
for (int i = 0; i < firstBunnyList.size(); i++)
{
if (firstBunnyList.get(i) < secondBunnyList.get(i))
{
System.
out.
println("Better " + Arrays.
toString(firstBunnyList.
toArray()) + " than " + Arrays.
toString(secondBunnyList.
toArray())); return true;
}
else if (firstBunnyList.get(i) > secondBunnyList.get(i))
{
System.
out.
println("Worse " + Arrays.
toString(firstBunnyList.
toArray()) + " than " + Arrays.
toString(secondBunnyList.
toArray())); return false;
}
}
}
else
{
if (firstBunnyList.size() > secondBunnyList.size())
{
System.
out.
println("Better size " + Arrays.
toString(firstBunnyList.
toArray()) + " than " + Arrays.
toString(secondBunnyList.
toArray())); }
else
{
System.
out.
println("Worse size " + Arrays.
toString(firstBunnyList.
toArray()) + " than " + Arrays.
toString(secondBunnyList.
toArray())); }
return firstBunnyList.size() > secondBunnyList.size();
}
//System.out.println("Equal " + Arrays.toString(firstBunnyList.toArray()) + " and " + Arrays.toString(secondBunnyList.toArray()));
// equal lists
return false;
}
}
aW1wb3J0IGphdmEubGFuZy5NYXRoOwppbXBvcnQgamF2YS51dGlsLio7CgpwdWJsaWMgY2xhc3MgU2F2ZUludGVnZXJzCnsKICBwdWJsaWMgc3RhdGljIGludFtdW10gdGltZUFycmF5OwogIAogIHB1YmxpYyBzdGF0aWMgdm9pZCBtYWluKFN0cmluZ1tdIGFyZ3MpCiAgewogICAgLy8gVGVzdCBDYXNlIDEKICAgIC8vaW50W11bXSB0aW1lcyA9IHt7MCwgMSwgMSwgMSwgMX0sIHsxLCAwLCAxLCAxLCAxfSwgezEsIDEsIDAsIDEsIDF9LCB7MSwgMSwgMSwgMCwgMX0sIHsxLCAxLCAxLCAxLCAwfX07CiAgICAvL2ludCB0aW1lX2xpbWl0ID0gMzsKICAgIAogICAgLy8gVGVzdCBDYXNlIDIKICAgIC8vaW50W11bXSB0aW1lcyA9IHt7MCwgMiwgMiwgMiwgLTF9LCB7OSwgMCwgMiwgMiwgLTF9LCB7OSwgMywgMCwgMiwgLTF9LCB7OSwgMywgMiwgMCwgLTF9LCB7OSwgMywgMiwgMiwgMH19OwogICAgLy9pbnQgdGltZV9saW1pdCA9IDE7CiAgICAKICAgIC8vIEluZmluaXRlIExvb3AgVGVzdCAxCiAgICAvL2ludFtdW10gdGltZXMgPSB7ezAsIDIsIDIsIDIsIC0xfSwgezksIC0xLCAyLCAyLCAtMX0sIHs5LCAzLCAwLCAyLCAtMX0sIHs5LCAzLCAyLCAwLCAtMX0sIHs5LCAzLCAyLCAyLCAwfX07CiAgICAvL2ludCB0aW1lX2xpbWl0ID0gMzsKICAgIAogICAgLy8gSW5maW5pdGUgTG9vcCBUZXN0IDIKICAgIC8vaW50W11bXSB0aW1lcyA9IHt7MCwgOSwgOSwgOSwgM30sIHs5LCAwLCA5LCAxLCA5fSwgezEsIDksIDAsIDksIDl9LCB7OSwgOSwgLTUsIDAsIDl9LCB7OSwgLTIsIDksIDksIDB9fTsKICAgIC8vaW50IHRpbWVfbGltaXQgPSAzOwogICAgCiAgICAvLyBsb3RzIG9mIGxvb3BpbmcKICAgIGludFtdW10gdGltZXMgPSB7ezAsIDAsIDAsIDAsIDB9LCB7MSwgMCwgOSwgOSwgOX0sIHsxLCA5LCAwLCA5LCA5fSwgezEsIDksIDksIDAsIDl9LCB7MCwgOSwgOSwgOSwgMH19OwogICAgaW50IHRpbWVfbGltaXQgPSA4OwogICAgCiAgICB0aW1lQXJyYXkgPSB0aW1lczsKICAgIExpc3Q8SW50ZWdlcj4gc2F2ZWRCdW5uaWVzID0gbmV3IEFycmF5TGlzdDxJbnRlZ2VyPigpOwoKICAgIC8vU2VhcmNoIGZvciB0aW1lLWdhaW5pbmcgaW5maW5pdGUgbG9vcHMuIFJldHVybiBhbGwgaWYgZm91bmQuCiAgICBpZihmaW5kVGltZUdhaW5pbmdJbmZpbml0ZUxvb3AoKSkKICAgIHsKICAgICAgU3lzdGVtLm91dC5wcmludGxuKCJJbmZpbml0ZSBsb29wcyEiKTsKICAgICAgLy9hZGQgYWxsIGJ1bm5pZXMgKG5vbmUgYXQgc3RhcnQgb3IgYnVsa2hlYWQpCiAgICAgIGZvciAoaW50IGkgPSAwOyBpIDwgdGltZUFycmF5Lmxlbmd0aCAtIDI7IGkrKykKICAgICAgewogICAgICAgIHNhdmVkQnVubmllcy5hZGQoaSk7CiAgICAgIH0KICAgIH0KICAgIGVsc2UKICAgIHsKICAgICAgCiAgICAgIC8vVE9ETzogY2FuIHRoaXMgZ2V0IGludGVncmF0ZWQgaW50byBmaW5kQmVzdFBhdGg/CiAgICAgIC8vTGlzdDxMaXN0PEludGVnZXI+PiBlc2NhcGVQYXRocyA9IGZpbmRBbGxNaW5MZW5ndGhQYXRocygwLCB0aW1lQXJyYXkubGVuZ3RoIC0gMSwgbmV3IEFycmF5TGlzdDxJbnRlZ2VyPigpKTsKICAgICAgLy9pZiAoZ2V0RGlzdGFuY2VGcm9tUGF0aChlc2NhcGVQYXRocy5nZXQoMCkpIDw9IHRpbWVfbGltaXQpCiAgICAgIC8vewogICAgICAgIC8vQ2FuJ3QgZXZlbiBzYXZlIHlvdXJzZWxmIQogICAgICAgIC8vU3lzdGVtLm91dC5wcmludGxuKCJDYW4ndCBldmVuIHNhdmUgeW91cnNlbGYhIik7CiAgICAgIC8vfQoKICAgICAgTGlzdDxJbnRlZ2VyPiBiZXN0UGF0aCA9IGZpbmRCZXN0UGF0aCgwLCB0aW1lQXJyYXkubGVuZ3RoIC0gMSwgdGltZV9saW1pdCwgbmV3IEFycmF5TGlzdDxJbnRlZ2VyPigpKTsKICAgICAgU3lzdGVtLm91dC5wcmludGxuKCJCZXN0IHBhdGg6ICIgKyBBcnJheXMudG9TdHJpbmcoYmVzdFBhdGgudG9BcnJheSgpKSk7CiAgICAgIHNhdmVkQnVubmllcyA9IGdldFNhdmVkQnVubmllc0Zyb21QYXRoKGJlc3RQYXRoKTsKICAgIH0KICAgIAogICAgU3lzdGVtLm91dC5wcmludGxuKCJTYXZlZCBidW5uaWVzOiAiICsgQXJyYXlzLnRvU3RyaW5nKHNhdmVkQnVubmllcy50b0FycmF5KCkpKTsKICB9CiAgCiAgcHVibGljIHN0YXRpYyBCb29sZWFuIGZpbmRUaW1lR2FpbmluZ0luZmluaXRlTG9vcCgpCiAgewogICAgZm9yIChpbnQgaSA9IDA7IGkgPCB0aW1lQXJyYXkubGVuZ3RoOyBpKyspCiAgICB7CiAgICAgIGZvciAoaW50IGogPSAwOyBqIDwgdGltZUFycmF5W2ldLmxlbmd0aDsgaisrKQogICAgICB7CiAgICAgICAgaWYgKGkgPT0gaiAmJiB0aW1lQXJyYXlbaV1bal0gPCAwKQogICAgICAgIHsKICAgICAgICAgIC8vU3lzdGVtLm91dC5wcmludGxuKCJsb2NhdGlvbiAiICsgaSArICIgY2FuIGxvb3AgaXRzZWxmIGZvciBpbmZpbml0ZSB0aW1lISIpOwogICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQogICAgICAgIGlmICh0aW1lQXJyYXlbaV1bal0gPCAwKQogICAgICAgIHsKICAgICAgICAgIC8vIEdvaW5nIGZyb20gbG9jYXRpb24gaSB0byBsb2NhdGlvbiBqIGFkZHMgdG8gdGhlIGNsb2NrIQogICAgICAgICAgLy8gQ2FuIHdlIGdldCBmcm9tIGogdG8gaSBpbiBvbmUgbGVzcyB0aW1lIHRoYW4gaXMgYWRkZWQ/CiAgICAgICAgICByZXR1cm4gZmluZFBhdGhXaXRoaW5UaW1lKGosIGksICh0aW1lQXJyYXlbaV1bal0gKiAtMSkgLSAxKTsKICAgICAgICB9CiAgICAgIH0KICAgICAgCiAgICB9CiAgICByZXR1cm4gZmFsc2U7CiAgfQogIAogIHB1YmxpYyBzdGF0aWMgQm9vbGVhbiBmaW5kUGF0aFdpdGhpblRpbWUoaW50IHN0YXJ0LCBpbnQgZmluaXNoLCBpbnQgdGltZSkKICB7CiAgICAvL1N5c3RlbS5vdXQucHJpbnRsbigiTG9va2luZyBmb3IgcGF0aCBmcm9tICIgKyBzdGFydCArICIgdG8gIiArIGZpbmlzaCArICIgYmVsb3cgIiArIHRpbWUgKyAiIGV4Y2x1ZGluZyAiICsgQXJyYXlzLnRvU3RyaW5nKGhpc3RvcnkudG9BcnJheSgpKSk7CiAgICBpZiAodGltZUFycmF5W3N0YXJ0XVtmaW5pc2hdIDw9IHRpbWUpCiAgICB7CiAgICAgIC8vU3lzdGVtLm91dC5wcmludGxuKCJGb3VuZCBwYXRoIGZyb20gIiArIHN0YXJ0ICsgIiB0byAiICsgZmluaXNoICsgIiB1bmRlciAiICsgdGltZSk7CiAgICAgIHJldHVybiB0cnVlOwogICAgfQogICAgaWYgKHN0YXJ0ID09IGZpbmlzaCkKICAgIHsKICAgICAgLy9TeXN0ZW0ub3V0LnByaW50bG4oIkZpbmQgcGF0aCB0byBzZWxmOiAiICsgc3RhcnQpOwogICAgICBpZiAodGltZSA+PSAwKQogICAgICB7CiAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgIH0KICAgICAgZWxzZQogICAgICB7CiAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICB9CiAgICB9CiAgICBMaXN0PExpc3Q8SW50ZWdlcj4+IG1pblBhdGhzID0gZmluZEFsbE1pbkxlbmd0aFBhdGhzKHN0YXJ0LCBmaW5pc2gsIG5ldyBBcnJheUxpc3Q8SW50ZWdlcj4oKSk7CgogICAgcmV0dXJuIGdldERpc3RhbmNlRnJvbVBhdGgobWluUGF0aHMuZ2V0KDApKSA8PSB0aW1lOwogIH0KICAKICBwdWJsaWMgc3RhdGljIExpc3Q8TGlzdDxJbnRlZ2VyPj4gZmluZEFsbE1pbkxlbmd0aFBhdGhzKGludCBzdGFydCwgaW50IGZpbmlzaCwgTGlzdDxJbnRlZ2VyPiBoaXN0b3J5KQogIHsKICAgIExpc3Q8TGlzdDxJbnRlZ2VyPj4gbGlzdE9mUGF0aHMgPSBuZXcgQXJyYXlMaXN0PExpc3Q8SW50ZWdlcj4+KCk7CiAgICAvL1N5c3RlbS5vdXQucHJpbnRsbigiRmluZGluZyBwYXRocyBmcm9tICIgKyBzdGFydCArICIgdG8gIiArIGZpbmlzaCArICIgZXhjbHVkaW5nICIgKyBBcnJheXMudG9TdHJpbmcoaGlzdG9yeS50b0FycmF5KCkpKTsKICAgIGhpc3RvcnkuYWRkKHN0YXJ0KTsKICAgIExpc3Q8SW50ZWdlcj4gZGlyZWN0UGF0aCA9IG5ldyBBcnJheUxpc3Q8SW50ZWdlcj4oaGlzdG9yeSk7CiAgICAvL01pZ2h0IGJlIGFibGUgdG8gcmVtb3ZlIHRoaXMgaWYvY2hlY2sgYW5kIGFsd2F5cyBhZGQgZmluaXNoIHRvIGRpcmVjdFBhdGgKICAgIGlmICghZGlyZWN0UGF0aC5jb250YWlucyhmaW5pc2gpKQogICAgewogICAgICBkaXJlY3RQYXRoLmFkZChmaW5pc2gpOwogICAgfQogICAgbGlzdE9mUGF0aHMuYWRkKGRpcmVjdFBhdGgpOwogICAgCiAgICAvL2ZvciBlYWNoIHBvc3NpYmxlIG5leHQgc3RlcCwgZ2V0IHRoZSBtaW4gbGVuZ3RoIHBhdGhzIGFuZCB1cGRhdGUgbGlzdE9mUGF0aHMKICAgIGZvciAoaW50IGogPSAwOyBqIDwgdGltZUFycmF5W3N0YXJ0XS5sZW5ndGg7IGorKykKICAgIHsKICAgICAgaWYgKCFoaXN0b3J5LmNvbnRhaW5zKGopICYmIGogIT0gZmluaXNoKQogICAgICB7CiAgICAgICAgTGlzdDxMaXN0PEludGVnZXI+PiBuZXh0UGF0aHMgPSBmaW5kQWxsTWluTGVuZ3RoUGF0aHMoaiwgZmluaXNoLCBuZXcgQXJyYXlMaXN0PEludGVnZXI+KGhpc3RvcnkpKTsKICAgICAgICBpbnQgbmV4dFBhdGhMZW5ndGggPSBnZXREaXN0YW5jZUZyb21QYXRoKG5leHRQYXRocy5nZXQoMCkpOwogICAgICAgIGludCBjdXJyZW50UGF0aExlbmd0aCA9IGdldERpc3RhbmNlRnJvbVBhdGgobGlzdE9mUGF0aHMuZ2V0KDApKTsKICAgICAgICBpZiAoY3VycmVudFBhdGhMZW5ndGggPiBuZXh0UGF0aExlbmd0aCkKICAgICAgICB7CiAgICAgICAgICAvL1N5c3RlbS5vdXQucHJpbnRsbigiU2hvcnRlciBwYXRoIGZyb20gIiArIHN0YXJ0ICsgIiB0byAiICsgZmluaXNoICsgIjogIiArIEFycmF5cy50b1N0cmluZyhuZXh0UGF0aHMuZ2V0KDApLnRvQXJyYXkoKSkpOwogICAgICAgICAgbGlzdE9mUGF0aHMuY2xlYXIoKTsKICAgICAgICAgIGxpc3RPZlBhdGhzLmFkZEFsbChuZXh0UGF0aHMpOwogICAgICAgIH0KICAgICAgICBpZiAoY3VycmVudFBhdGhMZW5ndGggPT0gbmV4dFBhdGhMZW5ndGgpCiAgICAgICAgewogICAgICAgICAgbGlzdE9mUGF0aHMuYWRkQWxsKG5leHRQYXRocyk7CiAgICAgICAgfQogICAgICB9ICAgICAgICAgICAKICAgIH0KCiAgICBpZiAobGlzdE9mUGF0aHMuc2l6ZSgpID4gMCkKICAgIHsKCQkvL1N5c3RlbS5vdXQucHJpbnRsbigiRm91bmQgcGF0aHMgZnJvbSAiICsgc3RhcnQgKyAiIHRvICIgKyBmaW5pc2ggKyAiIGluY2x1ZGluZyAiICsgQXJyYXlzLnRvU3RyaW5nKGxpc3RPZlBhdGhzLmdldCgwKS50b0FycmF5KCkpKTsKICAgIH0KICAgIGVsc2UKICAgIHsKICAgICAgU3lzdGVtLm91dC5wcmludGxuKCJGb3VuZCBubyBwYXRocyBmcm9tICIgKyBzdGFydCArICIgdG8gIiArIGZpbmlzaCArICIgc2hvcnRlciB0aGFuICIgKyBBcnJheXMudG9TdHJpbmcoaGlzdG9yeS50b0FycmF5KCkpKTsKICAgIH0KICAgIHJldHVybiBsaXN0T2ZQYXRoczsKICB9CiAgCiAgcHVibGljIHN0YXRpYyBMaXN0PEludGVnZXI+IGZpbmRCZXN0UGF0aChpbnQgc3RhcnQsIGludCBmaW5pc2gsIGludCB0aW1lX2xpbWl0LCBMaXN0PEludGVnZXI+IGhpc3RvcnkpCiAgewogICAgTGlzdDxJbnRlZ2VyPiB0YXJnZXRMaXN0ID0gbmV3IEFycmF5TGlzdDxJbnRlZ2VyPihBcnJheXMuYXNMaXN0KDAsIDEsIDAsIDIsIDAsIDMsIDApKTsKICAgIGhpc3RvcnkuYWRkKHN0YXJ0KTsKICAgIGlmIChoaXN0b3J5LmVxdWFscyh0YXJnZXRMaXN0KSkKICAgIHsKICAgICAgU3lzdGVtLm91dC5wcmludGxuKCJGb3VuZCBzb2x1dGlvbjogIiArIEFycmF5cy50b1N0cmluZyhoaXN0b3J5LnRvQXJyYXkoKSkpOwogICAgfQogICAgTGlzdDxJbnRlZ2VyPiB3b3JraW5nUGF0aCA9IG5ldyBBcnJheUxpc3Q8SW50ZWdlcj4oaGlzdG9yeSk7CiAgICBpZiAod29ya2luZ1BhdGguZ2V0KHdvcmtpbmdQYXRoLnNpemUoKSAtIDEpICE9IGZpbmlzaCkKICAgIHsKICAgICAgd29ya2luZ1BhdGguYWRkKGZpbmlzaCk7CiAgICB9CiAgICAvL2ZvciBlYWNoIHBvc3NpYmxlIG5leHQgc3RlcCwgZ2V0IHRoZSBiZXN0IHBhdGhzIGFuZCB1cGRhdGUgd29ya2luZ1BhdGgKICAgIGZvciAoaW50IGogPSAwOyBqIDwgdGltZUFycmF5W3N0YXJ0XS5sZW5ndGg7IGorKykKICAgIHsKICAgICAgaWYgKHZhbGlkTW92ZShqLCBoaXN0b3J5KSkKICAgICAgewogICAgICAgIGlmIChoaXN0b3J5LmVxdWFscyh0YXJnZXRMaXN0KSkKICAgICAgICB7CiAgICAgICAgCVN5c3RlbS5vdXQucHJpbnRsbigiQ2FsbGluZyBmaW5kQmVzdFBhdGggZm9yICIgKyBBcnJheXMudG9TdHJpbmcoaGlzdG9yeS50b0FycmF5KCkpICsgIiB0byAiICsgaik7CiAgICAgICAgfQogICAgICAgIExpc3Q8SW50ZWdlcj4gbmV4dFBhdGggPSBmaW5kQmVzdFBhdGgoaiwgZmluaXNoLCB0aW1lX2xpbWl0LCBuZXcgQXJyYXlMaXN0PEludGVnZXI+KGhpc3RvcnkpKTsKICAgICAgICB0YXJnZXRMaXN0LmFkZCg0KTsKICAgICAgICBpZiAobmV4dFBhdGguZXF1YWxzKHRhcmdldExpc3QpKQogICAgICAgIHsKICAgICAgICAJU3lzdGVtLm91dC5wcmludGxuKCJOb3cgbG9va2luZyBhdCBhbnN3ZXIgIiArIEFycmF5cy50b1N0cmluZyhuZXh0UGF0aC50b0FycmF5KCkpICsgIiwgajogIiArIGogKyAiIGhpc3Rvcnk6ICIgKyBBcnJheXMudG9TdHJpbmcoaGlzdG9yeS50b0FycmF5KCkpKTsKICAgICAgICB9CiAgICAgICAgaW50IG5leHRQYXRoTGVuZ3RoID0gZ2V0RGlzdGFuY2VGcm9tUGF0aChuZXh0UGF0aCk7CiAgICAgICAgaWYgKG5leHRQYXRoLnNpemUoKSA+IGhpc3Rvcnkuc2l6ZSgpICsgMikKICAgICAgICB7CiAgICAgICAgICBTeXN0ZW0ub3V0LnByaW50bG4oIlNpemUgb2YgIiArIEFycmF5cy50b1N0cmluZyhuZXh0UGF0aC50b0FycmF5KCkpICsgIiBpcyA+IDIgbW9yZSB0aGFuICIgKyBBcnJheXMudG9TdHJpbmcoaGlzdG9yeS50b0FycmF5KCkpKTsKICAgICAgICB9CiAgICAgICAgaWYgKHRpbWVfbGltaXQgPj0gbmV4dFBhdGhMZW5ndGgpCiAgICAgICAgewogICAgICAgICAgaWYgKG5leHRQYXRoLmVxdWFscyh0YXJnZXRMaXN0KSkKICAgICAgICAgIHsKICAgICAgICAgICAgU3lzdGVtLm91dC5wcmludGxuKCJBbnN3ZXIgdmFsaWQgZm9yICIgKyBBcnJheXMudG9TdHJpbmcoaGlzdG9yeS50b0FycmF5KCkpICsgIiB3aXRoIGo6ICIgKyBqKTsKICAgICAgICAgIH0KICAgICAgICAgIC8vU3lzdGVtLm91dC5wcmludGxuKCJMZW5ndGggIiArIG5leHRQYXRoTGVuZ3RoICsgIiBvZiAiICsgQXJyYXlzLnRvU3RyaW5nKG5leHRQYXRoLnRvQXJyYXkoKSkgKyAiIGlzIHVuZGVyICIgKyB0aW1lX2xpbWl0KTsKICAgICAgICAgIExpc3Q8SW50ZWdlcj4gY3VycmVudFBhdGhTYXZlZCA9IGdldFNhdmVkQnVubmllc0Zyb21QYXRoKHdvcmtpbmdQYXRoKTsKICAgICAgICAgIExpc3Q8SW50ZWdlcj4gbmV4dFBhdGhTYXZlZCA9IGdldFNhdmVkQnVubmllc0Zyb21QYXRoKG5leHRQYXRoKTsKICAgICAgICAgIGlmIChpc0JldHRlckxpc3RPZlNhdmVkQnVubmllcyhuZXh0UGF0aFNhdmVkLCBjdXJyZW50UGF0aFNhdmVkKSkKICAgICAgICAgIHsKICAgICAgICAgICAgU3lzdGVtLm91dC5wcmludGxuKCJSZXBsYWNpbmcgcGF0aCAiICsgQXJyYXlzLnRvU3RyaW5nKHdvcmtpbmdQYXRoLnRvQXJyYXkoKSkgKyAiIHdpdGggIiArIEFycmF5cy50b1N0cmluZyhuZXh0UGF0aC50b0FycmF5KCkpKTsKICAgICAgICAgICAgd29ya2luZ1BhdGggPSBuZXh0UGF0aDsKICAgICAgICAgIH0KICAgICAgICAgIC8vZWxzZQogICAgICAgICAgLy97CiAgICAgICAgICAvLyAgU3lzdGVtLm91dC5wcmludGxuKCJMaXN0ICIgKyBBcnJheXMudG9TdHJpbmcobmV4dFBhdGhTYXZlZC50b0FycmF5KCkpICsgIiBpc24ndCBiZXR0ZXIgdGhhbiAiICsgQXJyYXlzLnRvU3RyaW5nKGN1cnJlbnRQYXRoU2F2ZWQudG9BcnJheSgpKSk7CiAgICAgICAgICAvL30KICAgICAgICB9CiAgICAgICAgLy9lbHNlCiAgICAgICAgLy97CiAgICAgICAgLy8gIFN5c3RlbS5vdXQucHJpbnRsbigiTGVuZ3RoICIgKyBuZXh0UGF0aExlbmd0aCArICIgb2YgIiArIEFycmF5cy50b1N0cmluZyhuZXh0UGF0aC50b0FycmF5KCkpICsgIiBpcyBvdmVyICIgKyB0aW1lX2xpbWl0KTsKICAgICAgICAvL30KICAgICAgfQogICAgICAvL2Vsc2UKICAgICAgLy97CiAgICAgIC8vICBTeXN0ZW0ub3V0LnByaW50bG4oIkludmFsaWQgbW92ZSAiICsgaiArICIgb24gIiArIEFycmF5cy50b1N0cmluZyhoaXN0b3J5LnRvQXJyYXkoKSkpOwogICAgICAvL30KICAgICAgLy9pZiAoaiA9PSB0aW1lQXJyYXlbc3RhcnRdLmxlbmd0aCAtIDEpCiAgICAgIC8vewogICAgICAvLyAgU3lzdGVtLm91dC5wcmludGxuKCJGaW5pc2hpbmcgIiArIEFycmF5cy50b1N0cmluZyhoaXN0b3J5LnRvQXJyYXkoKSkgKyAiIGFuZCB3b3JraW5nUGF0aCBpcyAiICsgQXJyYXlzLnRvU3RyaW5nKHdvcmtpbmdQYXRoLnRvQXJyYXkoKSkpOwogICAgICAvL30KICAgIH0KICAgIGlmIChoaXN0b3J5LmVxdWFscyh0YXJnZXRMaXN0KSkKICAgIHsKICAgICAgU3lzdGVtLm91dC5wcmludGxuKCJSZXR1cm5pbmcgYW5zd2VyOiAiICsgQXJyYXlzLnRvU3RyaW5nKHdvcmtpbmdQYXRoLnRvQXJyYXkoKSkpOwogICAgfQoKICAgIHJldHVybiB3b3JraW5nUGF0aDsgICAgICAgIAogIH0KICAKICBwdWJsaWMgc3RhdGljIEJvb2xlYW4gdmFsaWRNb3ZlKGludCBtb3ZlLCBMaXN0PEludGVnZXI+IGhpc3RvcnkpCiAgewogICAgLy9XZSd2ZSBhbHJlYWR5IGZvdW5kIGluZmluaXRlIGxvb3BzIGJ5IHN0YW5kaW5nIHN0aWxsLiBEb24ndCBhbGxvdyBvdGhlcnMuCiAgICBpZiAobW92ZSA9PSBoaXN0b3J5LmdldChoaXN0b3J5LnNpemUoKSAtIDEpKQogICAgewogICAgICByZXR1cm4gZmFsc2U7CiAgICB9CiAgICAvL01vdmluZyB0byB0aGUgYnVsa2hlYWQgb3Igc3RhcnQgYXJlIGFsbW9zdCBhbHdheXMgYWxsb3dlZAogICAgaWYgKG1vdmUgPT0gdGltZUFycmF5Lmxlbmd0aCAtIDEgfHwgbW92ZSA9PSAwKQogICAgewogICAgICAvL0Rvbid0IGFsbG93IHN0YXJ0LWJ1bGtoZWFkIGluZmluaXRlIGxvb3BzCiAgICAgIGlmIChoaXN0b3J5LnNpemUoKSA+PSAyICYmCiAgICAgICAgaGlzdG9yeS5nZXQoaGlzdG9yeS5zaXplKCkgLSAxKSA9PSAwICYmCiAgICAgICAgaGlzdG9yeS5nZXQoaGlzdG9yeS5zaXplKCkgLSAyKSA9PSB0aW1lQXJyYXkubGVuZ3RoIC0gMSkKICAgICAgewogICAgICAgIC8vU3lzdGVtLm91dC5wcmludGxuKCJSZWplY3RpbmcgbG9vcDogIiArIEFycmF5cy50b1N0cmluZyhoaXN0b3J5LnRvQXJyYXkoKSkgKyAiLCBtb3ZlICIgKyBtb3ZlKTsKICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgIH0KICAgICAgcmV0dXJuIHRydWU7CiAgICB9CiAgICAvL090aGVyd2lzZSBpdCdzIG9ubHkgdmFsaWQgaWYgd2UgaGF2ZW4ndCBhbHJlYWR5IGdyYWJiZWQgdGhlIGJ1bm55CiAgICByZXR1cm4gIWhpc3RvcnkuY29udGFpbnMobW92ZSk7CiAgfQogIAogIHB1YmxpYyBzdGF0aWMgaW50IGdldERpc3RhbmNlRnJvbVBhdGgoTGlzdDxJbnRlZ2VyPiBoaXN0b3J5KQogIHsKICAgIC8vU3lzdGVtLm91dC5wcmludCgiRGlzdGFuY2Ugb2YgIiArIEFycmF5cy50b1N0cmluZyhoaXN0b3J5LnRvQXJyYXkoKSkgKyAiIGlzICIpOwogICAgaWYgKGhpc3Rvcnkuc2l6ZSgpIDw9IDEpCiAgICB7CiAgICAgIC8vU3lzdGVtLm91dC5wcmludCgwKTsKICAgICAgLy9TeXN0ZW0ub3V0LnByaW50bG4oKTsKICAgICAgcmV0dXJuIDA7CiAgICB9CiAgICAvL2ludCBoaXN0b3J5SGFzaCA9IGhpc3RvcnkuaGFzaENvZGUoKTsKICAgIC8vaWYgKGhtYXAuY29udGFpbnNLZXkoaGlzdG9yeUhhc2gpKQogICAgLy97CiAgICAvLyAgU3lzdGVtLm91dC5wcmludCgiaGFzaGVkIGFzICIgKyBobWFwLmdldChoaXN0b3J5SGFzaCkpOwogICAgLy8gICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigpOwogICAgLy8gIHJldHVybiBobWFwLmdldChoaXN0b3J5SGFzaCk7CiAgICAvL30KICAgIEl0ZXJhdG9yPEludGVnZXI+IGRpc3RhbmNlSXRlciA9IGhpc3RvcnkuaXRlcmF0b3IoKTsKICAgIGludCBkaXN0YW5jZSA9IDAsIHN0YXJ0ID0gZGlzdGFuY2VJdGVyLm5leHQoKTsKICAgIHdoaWxlIChkaXN0YW5jZUl0ZXIuaGFzTmV4dCgpKQogICAgewogICAgICBpbnQgbmV4dCA9IGRpc3RhbmNlSXRlci5uZXh0KCk7CiAgICAgIGRpc3RhbmNlICs9IHRpbWVBcnJheVtzdGFydF1bbmV4dF07CiAgICAgIHN0YXJ0ID0gbmV4dDsKICAgIH0KICAgIC8vU3lzdGVtLm91dC5wcmludChkaXN0YW5jZSk7CiAgICAvLyAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigpOwogICAgLy9obWFwLnB1dChoaXN0b3J5SGFzaCwgZGlzdGFuY2UpOwogICAgcmV0dXJuIGRpc3RhbmNlOwogIH0KICAKICBwdWJsaWMgc3RhdGljIExpc3Q8SW50ZWdlcj4gZ2V0U2F2ZWRCdW5uaWVzRnJvbVBhdGgoTGlzdDxJbnRlZ2VyPiBoaXN0b3J5KQogIHsKICAgIExpc3Q8SW50ZWdlcj4gc2F2ZWRCdW5uaWVzID0gbmV3IEFycmF5TGlzdDxJbnRlZ2VyPigpOwogICAgSXRlcmF0b3I8SW50ZWdlcj4gc2F2ZUl0ZXIgPSBoaXN0b3J5Lml0ZXJhdG9yKCk7CiAgICB3aGlsZSAoc2F2ZUl0ZXIuaGFzTmV4dCgpKQogICAgewogICAgICBpbnQgbW92ZSA9IHNhdmVJdGVyLm5leHQoKTsKICAgICAgaWYgKG1vdmUgIT0gMCAmJiBtb3ZlICE9IHRpbWVBcnJheS5sZW5ndGggLSAxKQogICAgICB7CiAgICAgICAgc2F2ZWRCdW5uaWVzLmFkZChtb3ZlLTEpOwogICAgICB9CiAgICB9CiAgICAvL1N5c3RlbS5vdXQucHJpbnRsbigiU2F2ZWQgIiArIEFycmF5cy50b1N0cmluZyhzYXZlZEJ1bm5pZXMudG9BcnJheSgpKSArICIgZnJvbSAiICsgQXJyYXlzLnRvU3RyaW5nKGhpc3RvcnkudG9BcnJheSgpKSk7CiAgICByZXR1cm4gc2F2ZWRCdW5uaWVzOwogIH0KCiAgcHVibGljIHN0YXRpYyBCb29sZWFuIGlzQmV0dGVyTGlzdE9mU2F2ZWRCdW5uaWVzKExpc3Q8SW50ZWdlcj4gZmlyc3RCdW5ueUxpc3QsIExpc3Q8SW50ZWdlcj4gc2Vjb25kQnVubnlMaXN0KQogIHsKICAgIGlmIChmaXJzdEJ1bm55TGlzdC5zaXplKCkgPT0gc2Vjb25kQnVubnlMaXN0LnNpemUoKSkKICAgIHsKICAgICAgZm9yIChpbnQgaSA9IDA7IGkgPCBmaXJzdEJ1bm55TGlzdC5zaXplKCk7IGkrKykKICAgICAgewogICAgICAgIGlmIChmaXJzdEJ1bm55TGlzdC5nZXQoaSkgPCBzZWNvbmRCdW5ueUxpc3QuZ2V0KGkpKQogICAgICAgIHsKICAgICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigiQmV0dGVyICIgKyBBcnJheXMudG9TdHJpbmcoZmlyc3RCdW5ueUxpc3QudG9BcnJheSgpKSArICIgdGhhbiAiICsgQXJyYXlzLnRvU3RyaW5nKHNlY29uZEJ1bm55TGlzdC50b0FycmF5KCkpKTsKICAgICAgICAgIHJldHVybiB0cnVlOwogICAgICAgIH0KICAgICAgICBlbHNlIGlmIChmaXJzdEJ1bm55TGlzdC5nZXQoaSkgPiBzZWNvbmRCdW5ueUxpc3QuZ2V0KGkpKQogICAgICAgIHsKICAgICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigiV29yc2UgIiArIEFycmF5cy50b1N0cmluZyhmaXJzdEJ1bm55TGlzdC50b0FycmF5KCkpICsgIiB0aGFuICIgKyBBcnJheXMudG9TdHJpbmcoc2Vjb25kQnVubnlMaXN0LnRvQXJyYXkoKSkpOwogICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgICAgfQogICAgfQogICAgZWxzZQogICAgewogICAgICBpZiAoZmlyc3RCdW5ueUxpc3Quc2l6ZSgpID4gc2Vjb25kQnVubnlMaXN0LnNpemUoKSkKICAgICAgewogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigiQmV0dGVyIHNpemUgIiArIEFycmF5cy50b1N0cmluZyhmaXJzdEJ1bm55TGlzdC50b0FycmF5KCkpICsgIiB0aGFuICIgKyBBcnJheXMudG9TdHJpbmcoc2Vjb25kQnVubnlMaXN0LnRvQXJyYXkoKSkpOwogICAgICB9CiAgICAgIGVsc2UKICAgICAgewogICAgICAgIFN5c3RlbS5vdXQucHJpbnRsbigiV29yc2Ugc2l6ZSAiICsgQXJyYXlzLnRvU3RyaW5nKGZpcnN0QnVubnlMaXN0LnRvQXJyYXkoKSkgKyAiIHRoYW4gIiArIEFycmF5cy50b1N0cmluZyhzZWNvbmRCdW5ueUxpc3QudG9BcnJheSgpKSk7CiAgICAgIH0KICAgICAgcmV0dXJuIGZpcnN0QnVubnlMaXN0LnNpemUoKSA+IHNlY29uZEJ1bm55TGlzdC5zaXplKCk7CiAgICB9CiAgICAvL1N5c3RlbS5vdXQucHJpbnRsbigiRXF1YWwgIiArIEFycmF5cy50b1N0cmluZyhmaXJzdEJ1bm55TGlzdC50b0FycmF5KCkpICsgIiBhbmQgIiArIEFycmF5cy50b1N0cmluZyhzZWNvbmRCdW5ueUxpc3QudG9BcnJheSgpKSk7CiAgICAvLyBlcXVhbCBsaXN0cwogICAgcmV0dXJuIGZhbHNlOwogIH0KfQ==