#include <iostream>
#include <type_traits>
template < std:: size_t ...> struct index_sequence { } ;
template < std:: size_t N, std:: size_t ... Is >
struct make_index_sequence_helper : make_index_sequence_helper< N- 1 , N- 1 , Is...> { } ;
template < std:: size_t ... Is >
struct make_index_sequence_helper< 0 , Is...> {
using type = index_sequence< Is...> ;
} ;
template < std:: size_t N>
using make_index_sequence = typename make_index_sequence_helper< N> :: type ;
// Merging two packs of numbers.
template < typename , typename > struct Merge;
template < template < std:: size_t ...> class P1, template < std:: size_t ...> class P2, std:: size_t ... Is , std:: size_t ... Js >
struct Merge< P1< Is...> , P2< Js...>> {
using type = P1< Is..., Js...> ;
} ;
// Obtaining the last element from a pack.
template < typename , typename > struct LastType;
template < typename T, template < T...> class P, T Last>
struct LastType< T, P< Last>> : std:: integral_constant < T, Last> { } ;
template < typename T, template < T...> class P, T First, T... Rest >
struct LastType< T, P< First, Rest...>> : LastType< T, P< Rest...>> { } ;
// Checking if t of type T exists in a pack of T objects.
template < typename T, T t, T...> struct ExistsInPack;
template < typename T, T t, T First, T... Rest >
struct ExistsInPack< T, t, First, Rest...> : ExistsInPack< T, t, Rest...> { } ;
template < typename T, T t, T... Rest >
struct ExistsInPack< T, t, t, Rest...> : std:: true_type { } ;
template < typename T, T t>
struct ExistsInPack< T, t> : std:: false_type { } ;
// Checking if t of type T is a last child. Using ExistsInPack will not work correctly here.
template < typename T, T t, T...> struct IsALastChild;
template < typename T, T t, T Child, T Parent, T... Rest >
struct IsALastChild< T, t, Child, Parent, Rest...> : IsALastChild< T, t, Rest...> { } ; // Here is the difference with ExistsInPack. We must skip the parent and check the next child (if any) because we don't want the child to be read as a parent.
template < typename T, T t, T Parent, T... Rest >
struct IsALastChild< T, t, t, Parent, Rest...> : std:: true_type { } ;
template < typename T, T t>
struct IsALastChild< T, t> : std:: false_type { } ;
// Obtaining the adjacent vertices of a given vertex.
template < std:: size_t , typename , typename > struct AdjacentVerticesHelper;
template < std:: size_t Vertex, template < std:: size_t ...> class P, std:: size_t From, std:: size_t To, std:: size_t ... RemainingPairs , std:: size_t ... Accumulated >
struct AdjacentVerticesHelper< Vertex, P< From, To, RemainingPairs...> , P< Accumulated...>> : AdjacentVerticesHelper< Vertex, P< RemainingPairs...> , P< Accumulated...>> { } ;
template < std:: size_t Vertex, template < std:: size_t ...> class P, std:: size_t To, std:: size_t ... RemainingPairs , std:: size_t ... Accumulated >
struct AdjacentVerticesHelper< Vertex, P< Vertex, To, RemainingPairs...> , P< Accumulated...>> : AdjacentVerticesHelper< Vertex, P< RemainingPairs...> , P< Accumulated..., To>> { } ;
template < std:: size_t Vertex, template < std:: size_t ...> class P, std:: size_t ... Accumulated >
struct AdjacentVerticesHelper< Vertex, P<> , P< Accumulated...>> {
using type = P< Accumulated...> ;
static const bool nonempty = sizeof ...( Accumulated) > 0 ;
} ;
template < std:: size_t , typename > struct AdjacentVertices;
template < std:: size_t Vertex, template < std:: size_t ...> class P, std:: size_t ... Edges >
struct AdjacentVertices< Vertex, P< Edges...>> : AdjacentVerticesHelper< Vertex, P< Edges...> , P<>> { } ;
// Given Child, find its parent from the pack P<LastChildAndParent...>.
template < std:: size_t Child, std:: size_t First, std:: size_t Second, std:: size_t ... Rest >
struct GetParent : GetParent< Child, Rest...> { } ;
template < std:: size_t Child, std:: size_t Parent, std:: size_t ... Rest >
struct GetParent< Child, Child, Parent, Rest...> : std:: integral_constant < std:: size_t , Parent> { } ;
template < std:: size_t , typename , typename > struct RemoveChildAndParentHelper;
// Given Child, remove Child and its parent from P<LastChildAndParent...>.
template < template < std:: size_t ...> class P, std:: size_t Child, std:: size_t First, std:: size_t Second, std:: size_t ... Rest , std:: size_t ... Accumulated >
struct RemoveChildAndParentHelper< Child, P< First, Second, Rest...> , P< Accumulated...>> : RemoveChildAndParentHelper< Child, P< Rest...> , P< Accumulated..., First, Second>> { } ;
template < template < std:: size_t ...> class P, std:: size_t Child, std:: size_t Parent, std:: size_t ... Rest , std:: size_t ... Accumulated >
struct RemoveChildAndParentHelper< Child, P< Child, Parent, Rest...> , P< Accumulated...>> {
using type = P< Accumulated..., Rest...> ;
} ;
template < template < std:: size_t ...> class P, std:: size_t Child, std:: size_t ... Accumulated >
struct RemoveChildAndParentHelper< Child, P<> , P< Accumulated...>> {
using type = P< Accumulated...> ;
} ;
template < std:: size_t , typename > struct RemoveChildAndParent;
template < template < std:: size_t ...> class P, std:: size_t Child, std:: size_t ... LastChildAndParent >
struct RemoveChildAndParent< Child, P< LastChildAndParent...>> : RemoveChildAndParentHelper< Child, P< LastChildAndParent...> , P<>> { } ;
// Removing from a pack all the elements in P<Visited...>.
template < typename , typename , typename , typename > struct RemoveThoseAlreadyVisitedHelper;
template < typename T, template < T...> class P, T First, T... Rest , T... Visited , T... Accumulated >
struct RemoveThoseAlreadyVisitedHelper< T, P< First, Rest...> , P< Visited...> , P< Accumulated...>> : std:: conditional < ExistsInPack< T, First, Visited...> :: value ,
RemoveThoseAlreadyVisitedHelper< T, P< Rest...> , P< Visited...> , P< Accumulated...>> ,
RemoveThoseAlreadyVisitedHelper< T, P< Rest...> , P< Visited...> , P< Accumulated..., First>>
> :: type { } ;
template < typename T, template < T...> class P, T... Visited , T... Accumulated >
struct RemoveThoseAlreadyVisitedHelper< T, P<> , P< Visited...> , P< Accumulated...>> {
using type = P< Accumulated...> ;
} ;
template < typename , typename , typename > struct RemoveThoseAlreadyVisited;
template < typename T, template < T...> class P, T... Ts , T... Visited >
struct RemoveThoseAlreadyVisited< T, P< Ts...> , P< Visited...>> : RemoveThoseAlreadyVisitedHelper< T, P< Ts...> , P< Visited...> , P<>> { } ;
template < typename T, template < T...> class P, T... Ts >
struct RemoveThoseAlreadyVisited< T, P< Ts...> , P<>> { // These remaining specializations are to avoid using the above specialization since 'type' is immediately obvious and needs no computation.
using type = P< Ts...> ;
} ;
template < typename T, template < T...> class P, T... Visited >
struct RemoveThoseAlreadyVisited< T, P<> , P< Visited...>> {
using type = P<> ;
} ;
template < typename T, template < T...> class P>
struct RemoveThoseAlreadyVisited< T, P<> , P<>> { // This is needed to avoid ambiguity with the above two specializations.
using type = P<> ;
} ;
// Removing duplicates from a pack. This is only used for the alternate Graph class that allows using any values for its vertices (not necessarily consecutive).
template < typename , typename , typename > struct RemoveDuplicatesHelper;
template < typename T, template < T...> class P, T First, T... Rest , T... Accumulated >
struct RemoveDuplicatesHelper< T, P< First, Rest...> , P< Accumulated...>> : std:: conditional < ExistsInPack< T, First, Accumulated..., Rest...> :: value ,
RemoveDuplicatesHelper< T, P< Rest...> , P< Accumulated...>> ,
RemoveDuplicatesHelper< T, P< Rest...> , P< Accumulated..., First>>
> :: type { } ;
template < typename T, template < T...> class P, T... Accumulated >
struct RemoveDuplicatesHelper< T, P<> , P< Accumulated...>> {
using type = P< Accumulated...> ;
} ;
template < typename , typename > struct RemoveDuplicates;
template < typename T, template < T...> class P, T... Ts >
struct RemoveDuplicates< T, P< Ts...>> : RemoveDuplicatesHelper< T, P< Ts...> , P<>> { } ;
// Now the main structs.
template < typename , typename , typename , typename , typename > struct TopologicalSort;
template < typename , typename , typename , typename , typename , typename > struct TopologicalSortHelper;
template < typename , typename , typename , typename , typename , bool > struct CheckIfLastAdjacentVertex;
// End of the topological sort loop. Replacing P<Visited...> with typename Visited and/or P<Edges...> with typename Edges and/or P<Vertices...> with typename Stack will cause ambiguity compiling error since neither this nor the above partial specialization is more specialized than the other.
template < template < std:: size_t ...> class P, std:: size_t ... Visited , std:: size_t ... Edges , std:: size_t ... LastChildAndParent , std:: size_t ... Vertices >
struct TopologicalSort< P<> , P< Edges...> , P< Visited...> , P< LastChildAndParent...> , P< Vertices...>> {
using topological_sort = P< Vertices...> ; // The final stack of vertices describing a topological order.
} ;
// The topological sort loop.
template < template < std:: size_t ...> class P, std:: size_t First, std:: size_t ... Rest , std:: size_t ... Visited , std:: size_t ... Edges , std:: size_t ... LastChildAndParent , std:: size_t ... Vertices > // First, Rest... are the vertices to be visited now (in that order).
struct TopologicalSort< P< First, Rest...> , P< Edges...> , P< Visited...> , P< LastChildAndParent...> , P< Vertices...>> :
TopologicalSortHelper< P< First, Rest...> , P< Edges...> , P< Visited..., First> , P< LastChildAndParent...> , P< Vertices...> , // Append First to P<Visited...>.
typename RemoveThoseAlreadyVisited< std:: size_t , typename AdjacentVertices< First, P< Edges...>> :: type , P< Visited...>> :: type > { } ; // Pass the adjacent vertices of First (if any), except those that have already been visited.
// First has adjacent edges, so visit its adjacent vertices, and prepend First to P<Vertices...> AFTER these visits. To ensure this last part, add First and its last child into P<LastChildAndParent...>, and check later on when this last child of First is visited. Note that since all vertices have UNIQUE std::size_t ID numbers, there will be no mix-up with other vertices when this last child is visited.
template < template < std:: size_t ...> class P, std:: size_t First, std:: size_t ... Rest , std:: size_t ... Visited , std:: size_t ... Edges , std:: size_t ... LastChildAndParent , std:: size_t ... Vertices , typename AdjacentVertices>
struct TopologicalSortHelper< P< First, Rest...> , P< Edges...> , P< Visited...> , P< LastChildAndParent...> , P< Vertices...> , AdjacentVertices> :
CheckIfLastAdjacentVertex< typename Merge< AdjacentVertices, P< Rest...>> :: type , P< Edges...> , P< Visited...> , // Go into CheckIfLastAdjacentVertex, because it First is the last adjacent vertex of some parent vertex, that parent vertex shall be prepended to P<Vertices...> immediate after First is prepended to P<Vertices...>.
P< LastChildAndParent..., LastType< std:: size_t , AdjacentVertices> :: value , First> , P< Vertices...> , IsALastChild< std:: size_t , First, LastChildAndParent...> :: value > { } ;
// First has no adjacent vertices, so prepend it to P<Vertices...>. Here too, we must go into CheckIfLastAdjacentVertex in case First is the last adjacent vertex of some parent vertex.
template < template < std:: size_t ...> class P, std:: size_t First, std:: size_t ... Rest , std:: size_t ... Visited , std:: size_t ... Edges , std:: size_t ... LastChildAndParent , std:: size_t ... Vertices >
struct TopologicalSortHelper< P< First, Rest...> , P< Edges...> , P< Visited...> , P< LastChildAndParent...> , P< Vertices...> , P<>> :
CheckIfLastAdjacentVertex< P< First, Rest...> , P< Edges...> , P< Visited...> , P< LastChildAndParent...> , P< First, Vertices...> , IsALastChild< std:: size_t , First, LastChildAndParent...> :: value > { } ;
// First is the last adjacent vertex of some parent vertex, so prepend its parent vertex to P<Vertices...>. Note that First cannot be mixed up with another vertex because all vertices in the Graph have UNIQUE values.
template < template < std:: size_t ...> class P, std:: size_t First, std:: size_t ... Rest , std:: size_t ... Visited , std:: size_t ... Edges , std:: size_t ... LastChildAndParent , std:: size_t ... Vertices >
struct CheckIfLastAdjacentVertex< P< First, Rest...> , P< Edges...> , P< Visited...> , P< LastChildAndParent...> , P< Vertices...> , true > : // First and its parent vertex must be removed from P<LastChildAndParent...> right now.
TopologicalSort< typename RemoveThoseAlreadyVisited< std:: size_t , P< Rest...> , P< Visited...>> :: type , P< Edges...> , P< Visited...> , // We MUST remove all vertices that have already been visited before going into TopologicalSort, since TopologicalSort always assumes First has not been visited before.
typename RemoveChildAndParent< First, P< LastChildAndParent...>> :: type , P< GetParent< First, LastChildAndParent...> :: value , Vertices...>> { } ; // Do NOT prepend First into P<Vertices...> because this was already done in TopologicalSortHelper<P<First, Rest...>, P<Edges...>, P<Visited...>, P<LastChildAndParent...>, P<Vertices...>, P<>>.
// First is not the last adjacent vertex of some parent vertex, so simply do the regular TopologicalSort (but removing the vertices already visited first, of course).
template < template < std:: size_t ...> class P, std:: size_t ... ToVisit , std:: size_t ... Visited , std:: size_t ... Edges , std:: size_t ... LastChildAndParent , std:: size_t ... Vertices > // No need for First, Rest... here since First needs not be identified in this case.
struct CheckIfLastAdjacentVertex< P< ToVisit...> , P< Edges...> , P< Visited...> , P< LastChildAndParent...> , P< Vertices...> , false > :
TopologicalSort< typename RemoveThoseAlreadyVisited< std:: size_t , P< ToVisit...> , P< Visited...>> :: type , P< Edges...> , P< Visited...> , P< LastChildAndParent...> , P< Vertices...>> { } ;
template < std:: size_t N, std:: size_t ... Edges >
struct Graph : TopologicalSort< make_index_sequence< N> , index_sequence< Edges...> , index_sequence<> , index_sequence<> , index_sequence<>> { } ;
// -------------------------- Testing --------------------------
template < std:: size_t Last>
struct index_sequence< Last> {
static void print( ) { std:: cout << Last << std:: endl ; }
} ;
template < std:: size_t First, std:: size_t ... Rest >
struct index_sequence< First, Rest...> {
static void print( ) { std:: cout << First << ' ' ; index_sequence< Rest...> :: print ( ) ; }
} ;
int main( ) {
using DAG1 = Graph< 6 , 5 ,2 , 5 ,0 , 4 ,0 , 4 ,1 , 2 ,3 , 3 ,1 > ;
DAG1:: topological_sort :: print ( ) ; // 5 4 2 3 1 0
std:: cout << std:: boolalpha << std:: is_same < DAG1:: topological_sort , index_sequence< 5 ,4 ,2 ,3 ,1 ,0 >> :: value << std:: endl ; // true
using DAG2 = Graph< 8 , 0 ,1 , 0 ,2 , 0 ,3 , 1 ,4 , 1 ,5 , 2 ,4 , 2 ,6 , 3 ,5 , 3 ,6 , 4 ,7 , 5 ,7 , 6 ,7 > ;
DAG2:: topological_sort :: print ( ) ; // 0 3 2 6 1 5 4 7
std:: cout << std:: boolalpha << std:: is_same < DAG2:: topological_sort , index_sequence< 0 ,3 ,2 ,6 ,1 ,5 ,4 ,7 >> :: value << std:: endl ; // true
}
I2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8dHlwZV90cmFpdHM+Cgp0ZW1wbGF0ZSA8c3RkOjpzaXplX3QuLi4+IHN0cnVjdCBpbmRleF9zZXF1ZW5jZSB7fTsKCnRlbXBsYXRlIDxzdGQ6OnNpemVfdCBOLCBzdGQ6OnNpemVfdC4uLiBJcz4Kc3RydWN0IG1ha2VfaW5kZXhfc2VxdWVuY2VfaGVscGVyIDogbWFrZV9pbmRleF9zZXF1ZW5jZV9oZWxwZXI8Ti0xLCBOLTEsIElzLi4uPiB7fTsKCnRlbXBsYXRlIDxzdGQ6OnNpemVfdC4uLiBJcz4Kc3RydWN0IG1ha2VfaW5kZXhfc2VxdWVuY2VfaGVscGVyPDAsIElzLi4uPiB7CiAgICB1c2luZyB0eXBlID0gaW5kZXhfc2VxdWVuY2U8SXMuLi4+Owp9OwoKdGVtcGxhdGUgPHN0ZDo6c2l6ZV90IE4+CnVzaW5nIG1ha2VfaW5kZXhfc2VxdWVuY2UgPSB0eXBlbmFtZSBtYWtlX2luZGV4X3NlcXVlbmNlX2hlbHBlcjxOPjo6dHlwZTsKCi8vIE1lcmdpbmcgdHdvIHBhY2tzIG9mIG51bWJlcnMuCnRlbXBsYXRlIDx0eXBlbmFtZSwgdHlwZW5hbWU+IHN0cnVjdCBNZXJnZTsKCnRlbXBsYXRlIDx0ZW1wbGF0ZSA8c3RkOjpzaXplX3QuLi4+IGNsYXNzIFAxLCB0ZW1wbGF0ZSA8c3RkOjpzaXplX3QuLi4+IGNsYXNzIFAyLCBzdGQ6OnNpemVfdC4uLiBJcywgc3RkOjpzaXplX3QuLi4gSnM+CnN0cnVjdCBNZXJnZTxQMTxJcy4uLj4sIFAyPEpzLi4uPj4gewoJdXNpbmcgdHlwZSA9IFAxPElzLi4uLCBKcy4uLj47Cn07CgovLyBPYnRhaW5pbmcgdGhlIGxhc3QgZWxlbWVudCBmcm9tIGEgcGFjay4KdGVtcGxhdGUgPHR5cGVuYW1lLCB0eXBlbmFtZT4gc3RydWN0IExhc3RUeXBlOwoKdGVtcGxhdGUgPHR5cGVuYW1lIFQsIHRlbXBsYXRlIDxULi4uPiBjbGFzcyBQLCBUIExhc3Q+CnN0cnVjdCBMYXN0VHlwZTxULCBQPExhc3Q+PiA6IHN0ZDo6aW50ZWdyYWxfY29uc3RhbnQ8VCwgTGFzdD4ge307Cgp0ZW1wbGF0ZSA8dHlwZW5hbWUgVCwgdGVtcGxhdGUgPFQuLi4+IGNsYXNzIFAsIFQgRmlyc3QsIFQuLi4gUmVzdD4Kc3RydWN0IExhc3RUeXBlPFQsIFA8Rmlyc3QsIFJlc3QuLi4+PiA6IExhc3RUeXBlPFQsIFA8UmVzdC4uLj4+IHt9OwoKLy8gQ2hlY2tpbmcgaWYgdCBvZiB0eXBlIFQgZXhpc3RzIGluIGEgcGFjayBvZiBUIG9iamVjdHMuCnRlbXBsYXRlIDx0eXBlbmFtZSBULCBUIHQsIFQuLi4+IHN0cnVjdCBFeGlzdHNJblBhY2s7Cgp0ZW1wbGF0ZSA8dHlwZW5hbWUgVCwgVCB0LCBUIEZpcnN0LCBULi4uIFJlc3Q+CnN0cnVjdCBFeGlzdHNJblBhY2s8VCwgdCwgRmlyc3QsIFJlc3QuLi4+IDogRXhpc3RzSW5QYWNrPFQsIHQsIFJlc3QuLi4+IHt9OwoKdGVtcGxhdGUgPHR5cGVuYW1lIFQsIFQgdCwgVC4uLiBSZXN0PgpzdHJ1Y3QgRXhpc3RzSW5QYWNrPFQsIHQsIHQsIFJlc3QuLi4+IDogc3RkOjp0cnVlX3R5cGUge307Cgp0ZW1wbGF0ZSA8dHlwZW5hbWUgVCwgVCB0PgpzdHJ1Y3QgRXhpc3RzSW5QYWNrPFQsIHQ+IDogc3RkOjpmYWxzZV90eXBlIHt9OwoKLy8gQ2hlY2tpbmcgaWYgdCBvZiB0eXBlIFQgaXMgYSBsYXN0IGNoaWxkLiAgVXNpbmcgRXhpc3RzSW5QYWNrIHdpbGwgbm90IHdvcmsgY29ycmVjdGx5IGhlcmUuCnRlbXBsYXRlIDx0eXBlbmFtZSBULCBUIHQsIFQuLi4+IHN0cnVjdCBJc0FMYXN0Q2hpbGQ7Cgp0ZW1wbGF0ZSA8dHlwZW5hbWUgVCwgVCB0LCBUIENoaWxkLCBUIFBhcmVudCwgVC4uLiBSZXN0PgpzdHJ1Y3QgSXNBTGFzdENoaWxkPFQsIHQsIENoaWxkLCBQYXJlbnQsIFJlc3QuLi4+IDogSXNBTGFzdENoaWxkPFQsIHQsIFJlc3QuLi4+IHt9OyAgLy8gSGVyZSBpcyB0aGUgZGlmZmVyZW5jZSB3aXRoIEV4aXN0c0luUGFjay4gIFdlIG11c3Qgc2tpcCB0aGUgcGFyZW50IGFuZCBjaGVjayB0aGUgbmV4dCBjaGlsZCAoaWYgYW55KSBiZWNhdXNlIHdlIGRvbid0IHdhbnQgdGhlIGNoaWxkIHRvIGJlIHJlYWQgYXMgYSBwYXJlbnQuCgp0ZW1wbGF0ZSA8dHlwZW5hbWUgVCwgVCB0LCBUIFBhcmVudCwgVC4uLiBSZXN0PgpzdHJ1Y3QgSXNBTGFzdENoaWxkPFQsIHQsIHQsIFBhcmVudCwgUmVzdC4uLj4gOiBzdGQ6OnRydWVfdHlwZSB7fTsKCnRlbXBsYXRlIDx0eXBlbmFtZSBULCBUIHQ+CnN0cnVjdCBJc0FMYXN0Q2hpbGQ8VCwgdD4gOiBzdGQ6OmZhbHNlX3R5cGUge307CgovLyBPYnRhaW5pbmcgdGhlIGFkamFjZW50IHZlcnRpY2VzIG9mIGEgZ2l2ZW4gdmVydGV4Lgp0ZW1wbGF0ZSA8c3RkOjpzaXplX3QsIHR5cGVuYW1lLCB0eXBlbmFtZT4gc3RydWN0IEFkamFjZW50VmVydGljZXNIZWxwZXI7Cgp0ZW1wbGF0ZSA8c3RkOjpzaXplX3QgVmVydGV4LCB0ZW1wbGF0ZSA8c3RkOjpzaXplX3QuLi4+IGNsYXNzIFAsIHN0ZDo6c2l6ZV90IEZyb20sIHN0ZDo6c2l6ZV90IFRvLCBzdGQ6OnNpemVfdC4uLiBSZW1haW5pbmdQYWlycywgc3RkOjpzaXplX3QuLi4gQWNjdW11bGF0ZWQ+CnN0cnVjdCBBZGphY2VudFZlcnRpY2VzSGVscGVyPFZlcnRleCwgUDxGcm9tLCBUbywgUmVtYWluaW5nUGFpcnMuLi4+LCBQPEFjY3VtdWxhdGVkLi4uPj4gOiBBZGphY2VudFZlcnRpY2VzSGVscGVyPFZlcnRleCwgUDxSZW1haW5pbmdQYWlycy4uLj4sIFA8QWNjdW11bGF0ZWQuLi4+PiB7fTsKCnRlbXBsYXRlIDxzdGQ6OnNpemVfdCBWZXJ0ZXgsIHRlbXBsYXRlIDxzdGQ6OnNpemVfdC4uLj4gY2xhc3MgUCwgc3RkOjpzaXplX3QgVG8sIHN0ZDo6c2l6ZV90Li4uIFJlbWFpbmluZ1BhaXJzLCBzdGQ6OnNpemVfdC4uLiBBY2N1bXVsYXRlZD4Kc3RydWN0IEFkamFjZW50VmVydGljZXNIZWxwZXI8VmVydGV4LCBQPFZlcnRleCwgVG8sIFJlbWFpbmluZ1BhaXJzLi4uPiwgUDxBY2N1bXVsYXRlZC4uLj4+IDogQWRqYWNlbnRWZXJ0aWNlc0hlbHBlcjxWZXJ0ZXgsIFA8UmVtYWluaW5nUGFpcnMuLi4+LCBQPEFjY3VtdWxhdGVkLi4uLCBUbz4+IHt9OwoKdGVtcGxhdGUgPHN0ZDo6c2l6ZV90IFZlcnRleCwgdGVtcGxhdGUgPHN0ZDo6c2l6ZV90Li4uPiBjbGFzcyBQLCBzdGQ6OnNpemVfdC4uLiBBY2N1bXVsYXRlZD4Kc3RydWN0IEFkamFjZW50VmVydGljZXNIZWxwZXI8VmVydGV4LCBQPD4sIFA8QWNjdW11bGF0ZWQuLi4+PiB7Cgl1c2luZyB0eXBlID0gUDxBY2N1bXVsYXRlZC4uLj47CglzdGF0aWMgY29uc3QgYm9vbCBub25lbXB0eSA9IHNpemVvZi4uLihBY2N1bXVsYXRlZCkgPiAwOwp9OwoKdGVtcGxhdGUgPHN0ZDo6c2l6ZV90LCB0eXBlbmFtZT4gc3RydWN0IEFkamFjZW50VmVydGljZXM7Cgp0ZW1wbGF0ZSA8c3RkOjpzaXplX3QgVmVydGV4LCB0ZW1wbGF0ZSA8c3RkOjpzaXplX3QuLi4+IGNsYXNzIFAsIHN0ZDo6c2l6ZV90Li4uIEVkZ2VzPgpzdHJ1Y3QgQWRqYWNlbnRWZXJ0aWNlczxWZXJ0ZXgsIFA8RWRnZXMuLi4+PiA6IEFkamFjZW50VmVydGljZXNIZWxwZXI8VmVydGV4LCBQPEVkZ2VzLi4uPiwgUDw+PiB7fTsKCi8vIEdpdmVuIENoaWxkLCBmaW5kIGl0cyBwYXJlbnQgZnJvbSB0aGUgcGFjayBQPExhc3RDaGlsZEFuZFBhcmVudC4uLj4uCnRlbXBsYXRlIDxzdGQ6OnNpemVfdCBDaGlsZCwgc3RkOjpzaXplX3QgRmlyc3QsIHN0ZDo6c2l6ZV90IFNlY29uZCwgc3RkOjpzaXplX3QuLi4gUmVzdD4Kc3RydWN0IEdldFBhcmVudCA6IEdldFBhcmVudDxDaGlsZCwgUmVzdC4uLj4ge307Cgp0ZW1wbGF0ZSA8c3RkOjpzaXplX3QgQ2hpbGQsIHN0ZDo6c2l6ZV90IFBhcmVudCwgc3RkOjpzaXplX3QuLi4gUmVzdD4Kc3RydWN0IEdldFBhcmVudDxDaGlsZCwgQ2hpbGQsIFBhcmVudCwgUmVzdC4uLj4gOiBzdGQ6OmludGVncmFsX2NvbnN0YW50PHN0ZDo6c2l6ZV90LCBQYXJlbnQ+IHt9OwoKdGVtcGxhdGUgPHN0ZDo6c2l6ZV90LCB0eXBlbmFtZSwgdHlwZW5hbWU+IHN0cnVjdCBSZW1vdmVDaGlsZEFuZFBhcmVudEhlbHBlcjsKCi8vIEdpdmVuIENoaWxkLCByZW1vdmUgQ2hpbGQgYW5kIGl0cyBwYXJlbnQgZnJvbSBQPExhc3RDaGlsZEFuZFBhcmVudC4uLj4uCnRlbXBsYXRlIDx0ZW1wbGF0ZSA8c3RkOjpzaXplX3QuLi4+IGNsYXNzIFAsIHN0ZDo6c2l6ZV90IENoaWxkLCBzdGQ6OnNpemVfdCBGaXJzdCwgc3RkOjpzaXplX3QgU2Vjb25kLCBzdGQ6OnNpemVfdC4uLiBSZXN0LCBzdGQ6OnNpemVfdC4uLiBBY2N1bXVsYXRlZD4Kc3RydWN0IFJlbW92ZUNoaWxkQW5kUGFyZW50SGVscGVyPENoaWxkLCBQPEZpcnN0LCBTZWNvbmQsIFJlc3QuLi4+LCBQPEFjY3VtdWxhdGVkLi4uPj4gOiBSZW1vdmVDaGlsZEFuZFBhcmVudEhlbHBlcjxDaGlsZCwgUDxSZXN0Li4uPiwgUDxBY2N1bXVsYXRlZC4uLiwgRmlyc3QsIFNlY29uZD4+IHt9OwoKdGVtcGxhdGUgPHRlbXBsYXRlIDxzdGQ6OnNpemVfdC4uLj4gY2xhc3MgUCwgc3RkOjpzaXplX3QgQ2hpbGQsIHN0ZDo6c2l6ZV90IFBhcmVudCwgc3RkOjpzaXplX3QuLi4gUmVzdCwgc3RkOjpzaXplX3QuLi4gQWNjdW11bGF0ZWQ+CnN0cnVjdCBSZW1vdmVDaGlsZEFuZFBhcmVudEhlbHBlcjxDaGlsZCwgUDxDaGlsZCwgUGFyZW50LCBSZXN0Li4uPiwgUDxBY2N1bXVsYXRlZC4uLj4+IHsKCXVzaW5nIHR5cGUgPSBQPEFjY3VtdWxhdGVkLi4uLCBSZXN0Li4uPjsKfTsKCnRlbXBsYXRlIDx0ZW1wbGF0ZSA8c3RkOjpzaXplX3QuLi4+IGNsYXNzIFAsIHN0ZDo6c2l6ZV90IENoaWxkLCBzdGQ6OnNpemVfdC4uLiBBY2N1bXVsYXRlZD4Kc3RydWN0IFJlbW92ZUNoaWxkQW5kUGFyZW50SGVscGVyPENoaWxkLCBQPD4sIFA8QWNjdW11bGF0ZWQuLi4+PiB7Cgl1c2luZyB0eXBlID0gUDxBY2N1bXVsYXRlZC4uLj47Cn07Cgp0ZW1wbGF0ZSA8c3RkOjpzaXplX3QsIHR5cGVuYW1lPiBzdHJ1Y3QgUmVtb3ZlQ2hpbGRBbmRQYXJlbnQ7Cgp0ZW1wbGF0ZSA8dGVtcGxhdGUgPHN0ZDo6c2l6ZV90Li4uPiBjbGFzcyBQLCBzdGQ6OnNpemVfdCBDaGlsZCwgc3RkOjpzaXplX3QuLi4gTGFzdENoaWxkQW5kUGFyZW50PgpzdHJ1Y3QgUmVtb3ZlQ2hpbGRBbmRQYXJlbnQ8Q2hpbGQsIFA8TGFzdENoaWxkQW5kUGFyZW50Li4uPj4gOiBSZW1vdmVDaGlsZEFuZFBhcmVudEhlbHBlcjxDaGlsZCwgUDxMYXN0Q2hpbGRBbmRQYXJlbnQuLi4+LCBQPD4+IHt9OwoKLy8gUmVtb3ZpbmcgZnJvbSBhIHBhY2sgYWxsIHRoZSBlbGVtZW50cyBpbiBQPFZpc2l0ZWQuLi4+Lgp0ZW1wbGF0ZSA8dHlwZW5hbWUsIHR5cGVuYW1lLCB0eXBlbmFtZSwgdHlwZW5hbWU+IHN0cnVjdCBSZW1vdmVUaG9zZUFscmVhZHlWaXNpdGVkSGVscGVyOwoKdGVtcGxhdGUgPHR5cGVuYW1lIFQsIHRlbXBsYXRlIDxULi4uPiBjbGFzcyBQLCBUIEZpcnN0LCBULi4uIFJlc3QsIFQuLi4gVmlzaXRlZCwgVC4uLiBBY2N1bXVsYXRlZD4Kc3RydWN0IFJlbW92ZVRob3NlQWxyZWFkeVZpc2l0ZWRIZWxwZXI8VCwgUDxGaXJzdCwgUmVzdC4uLj4sIFA8VmlzaXRlZC4uLj4sIFA8QWNjdW11bGF0ZWQuLi4+PiA6IHN0ZDo6Y29uZGl0aW9uYWw8RXhpc3RzSW5QYWNrPFQsIEZpcnN0LCBWaXNpdGVkLi4uPjo6dmFsdWUsCgkJUmVtb3ZlVGhvc2VBbHJlYWR5VmlzaXRlZEhlbHBlcjxULCBQPFJlc3QuLi4+LCBQPFZpc2l0ZWQuLi4+LCBQPEFjY3VtdWxhdGVkLi4uPj4sCgkJUmVtb3ZlVGhvc2VBbHJlYWR5VmlzaXRlZEhlbHBlcjxULCBQPFJlc3QuLi4+LCBQPFZpc2l0ZWQuLi4+LCBQPEFjY3VtdWxhdGVkLi4uLCBGaXJzdD4+Cgk+Ojp0eXBlIHt9OwoKdGVtcGxhdGUgPHR5cGVuYW1lIFQsIHRlbXBsYXRlIDxULi4uPiBjbGFzcyBQLCBULi4uIFZpc2l0ZWQsIFQuLi4gQWNjdW11bGF0ZWQ+CnN0cnVjdCBSZW1vdmVUaG9zZUFscmVhZHlWaXNpdGVkSGVscGVyPFQsIFA8PiwgUDxWaXNpdGVkLi4uPiwgUDxBY2N1bXVsYXRlZC4uLj4+IHsKCXVzaW5nIHR5cGUgPSBQPEFjY3VtdWxhdGVkLi4uPjsKfTsKCnRlbXBsYXRlIDx0eXBlbmFtZSwgdHlwZW5hbWUsIHR5cGVuYW1lPiBzdHJ1Y3QgUmVtb3ZlVGhvc2VBbHJlYWR5VmlzaXRlZDsKCnRlbXBsYXRlIDx0eXBlbmFtZSBULCB0ZW1wbGF0ZSA8VC4uLj4gY2xhc3MgUCwgVC4uLiBUcywgVC4uLiBWaXNpdGVkPgpzdHJ1Y3QgUmVtb3ZlVGhvc2VBbHJlYWR5VmlzaXRlZDxULCBQPFRzLi4uPiwgUDxWaXNpdGVkLi4uPj4gOiBSZW1vdmVUaG9zZUFscmVhZHlWaXNpdGVkSGVscGVyPFQsIFA8VHMuLi4+LCBQPFZpc2l0ZWQuLi4+LCBQPD4+IHt9OwoKdGVtcGxhdGUgPHR5cGVuYW1lIFQsIHRlbXBsYXRlIDxULi4uPiBjbGFzcyBQLCBULi4uIFRzPgpzdHJ1Y3QgUmVtb3ZlVGhvc2VBbHJlYWR5VmlzaXRlZDxULCBQPFRzLi4uPiwgUDw+PiB7ICAvLyBUaGVzZSByZW1haW5pbmcgc3BlY2lhbGl6YXRpb25zIGFyZSB0byBhdm9pZCB1c2luZyB0aGUgYWJvdmUgc3BlY2lhbGl6YXRpb24gc2luY2UgJ3R5cGUnIGlzIGltbWVkaWF0ZWx5IG9idmlvdXMgYW5kIG5lZWRzIG5vIGNvbXB1dGF0aW9uLgoJdXNpbmcgdHlwZSA9IFA8VHMuLi4+Owp9OwoKdGVtcGxhdGUgPHR5cGVuYW1lIFQsIHRlbXBsYXRlIDxULi4uPiBjbGFzcyBQLCBULi4uIFZpc2l0ZWQ+CnN0cnVjdCBSZW1vdmVUaG9zZUFscmVhZHlWaXNpdGVkPFQsIFA8PiwgUDxWaXNpdGVkLi4uPj4gewoJdXNpbmcgdHlwZSA9IFA8PjsKfTsKCnRlbXBsYXRlIDx0eXBlbmFtZSBULCB0ZW1wbGF0ZSA8VC4uLj4gY2xhc3MgUD4Kc3RydWN0IFJlbW92ZVRob3NlQWxyZWFkeVZpc2l0ZWQ8VCwgUDw+LCBQPD4+IHsgIC8vIFRoaXMgaXMgbmVlZGVkIHRvIGF2b2lkIGFtYmlndWl0eSB3aXRoIHRoZSBhYm92ZSB0d28gc3BlY2lhbGl6YXRpb25zLgoJdXNpbmcgdHlwZSA9IFA8PjsKfTsKCi8vIFJlbW92aW5nIGR1cGxpY2F0ZXMgZnJvbSBhIHBhY2suICBUaGlzIGlzIG9ubHkgdXNlZCBmb3IgdGhlIGFsdGVybmF0ZSBHcmFwaCBjbGFzcyB0aGF0IGFsbG93cyB1c2luZyBhbnkgdmFsdWVzIGZvciBpdHMgdmVydGljZXMgKG5vdCBuZWNlc3NhcmlseSBjb25zZWN1dGl2ZSkuCnRlbXBsYXRlIDx0eXBlbmFtZSwgdHlwZW5hbWUsIHR5cGVuYW1lPiBzdHJ1Y3QgUmVtb3ZlRHVwbGljYXRlc0hlbHBlcjsKCnRlbXBsYXRlIDx0eXBlbmFtZSBULCB0ZW1wbGF0ZSA8VC4uLj4gY2xhc3MgUCwgVCBGaXJzdCwgVC4uLiBSZXN0LCBULi4uIEFjY3VtdWxhdGVkPgpzdHJ1Y3QgUmVtb3ZlRHVwbGljYXRlc0hlbHBlcjxULCBQPEZpcnN0LCBSZXN0Li4uPiwgUDxBY2N1bXVsYXRlZC4uLj4+IDogc3RkOjpjb25kaXRpb25hbDxFeGlzdHNJblBhY2s8VCwgRmlyc3QsIEFjY3VtdWxhdGVkLi4uLCBSZXN0Li4uPjo6dmFsdWUsCgkJUmVtb3ZlRHVwbGljYXRlc0hlbHBlcjxULCBQPFJlc3QuLi4+LCBQPEFjY3VtdWxhdGVkLi4uPj4sCgkJUmVtb3ZlRHVwbGljYXRlc0hlbHBlcjxULCBQPFJlc3QuLi4+LCBQPEFjY3VtdWxhdGVkLi4uLCBGaXJzdD4+Cgk+Ojp0eXBlIHt9OwoKdGVtcGxhdGUgPHR5cGVuYW1lIFQsIHRlbXBsYXRlIDxULi4uPiBjbGFzcyBQLCBULi4uIEFjY3VtdWxhdGVkPgpzdHJ1Y3QgUmVtb3ZlRHVwbGljYXRlc0hlbHBlcjxULCBQPD4sIFA8QWNjdW11bGF0ZWQuLi4+PiB7Cgl1c2luZyB0eXBlID0gUDxBY2N1bXVsYXRlZC4uLj47Cn07Cgp0ZW1wbGF0ZSA8dHlwZW5hbWUsIHR5cGVuYW1lPiBzdHJ1Y3QgUmVtb3ZlRHVwbGljYXRlczsKCnRlbXBsYXRlIDx0eXBlbmFtZSBULCB0ZW1wbGF0ZSA8VC4uLj4gY2xhc3MgUCwgVC4uLiBUcz4Kc3RydWN0IFJlbW92ZUR1cGxpY2F0ZXM8VCwgUDxUcy4uLj4+IDogUmVtb3ZlRHVwbGljYXRlc0hlbHBlcjxULCBQPFRzLi4uPiwgUDw+PiB7fTsKCi8vIE5vdyB0aGUgbWFpbiBzdHJ1Y3RzLgp0ZW1wbGF0ZSA8dHlwZW5hbWUsIHR5cGVuYW1lLCB0eXBlbmFtZSwgdHlwZW5hbWUsIHR5cGVuYW1lPiBzdHJ1Y3QgVG9wb2xvZ2ljYWxTb3J0Owp0ZW1wbGF0ZSA8dHlwZW5hbWUsIHR5cGVuYW1lLCB0eXBlbmFtZSwgdHlwZW5hbWUsIHR5cGVuYW1lLCB0eXBlbmFtZT4gc3RydWN0IFRvcG9sb2dpY2FsU29ydEhlbHBlcjsKdGVtcGxhdGUgPHR5cGVuYW1lLCB0eXBlbmFtZSwgdHlwZW5hbWUsIHR5cGVuYW1lLCB0eXBlbmFtZSwgYm9vbD4gc3RydWN0IENoZWNrSWZMYXN0QWRqYWNlbnRWZXJ0ZXg7CgovLyBFbmQgb2YgdGhlIHRvcG9sb2dpY2FsIHNvcnQgbG9vcC4gIFJlcGxhY2luZyBQPFZpc2l0ZWQuLi4+IHdpdGggdHlwZW5hbWUgVmlzaXRlZCBhbmQvb3IgUDxFZGdlcy4uLj4gd2l0aCB0eXBlbmFtZSBFZGdlcyBhbmQvb3IgUDxWZXJ0aWNlcy4uLj4gd2l0aCB0eXBlbmFtZSBTdGFjayB3aWxsIGNhdXNlIGFtYmlndWl0eSBjb21waWxpbmcgZXJyb3Igc2luY2UgbmVpdGhlciB0aGlzIG5vciB0aGUgYWJvdmUgcGFydGlhbCBzcGVjaWFsaXphdGlvbiBpcyBtb3JlIHNwZWNpYWxpemVkIHRoYW4gdGhlIG90aGVyLiAKdGVtcGxhdGUgPHRlbXBsYXRlIDxzdGQ6OnNpemVfdC4uLj4gY2xhc3MgUCwgc3RkOjpzaXplX3QuLi4gVmlzaXRlZCwgc3RkOjpzaXplX3QuLi4gRWRnZXMsIHN0ZDo6c2l6ZV90Li4uIExhc3RDaGlsZEFuZFBhcmVudCwgc3RkOjpzaXplX3QuLi4gVmVydGljZXM+CnN0cnVjdCBUb3BvbG9naWNhbFNvcnQ8UDw+LCBQPEVkZ2VzLi4uPiwgUDxWaXNpdGVkLi4uPiwgUDxMYXN0Q2hpbGRBbmRQYXJlbnQuLi4+LCBQPFZlcnRpY2VzLi4uPj4gewoJdXNpbmcgdG9wb2xvZ2ljYWxfc29ydCA9IFA8VmVydGljZXMuLi4+OyAgLy8gVGhlIGZpbmFsIHN0YWNrIG9mIHZlcnRpY2VzIGRlc2NyaWJpbmcgYSB0b3BvbG9naWNhbCBvcmRlci4KfTsKCi8vIFRoZSB0b3BvbG9naWNhbCBzb3J0IGxvb3AuCnRlbXBsYXRlIDx0ZW1wbGF0ZSA8c3RkOjpzaXplX3QuLi4+IGNsYXNzIFAsIHN0ZDo6c2l6ZV90IEZpcnN0LCBzdGQ6OnNpemVfdC4uLiBSZXN0LCBzdGQ6OnNpemVfdC4uLiBWaXNpdGVkLCBzdGQ6OnNpemVfdC4uLiBFZGdlcywgc3RkOjpzaXplX3QuLi4gTGFzdENoaWxkQW5kUGFyZW50LCBzdGQ6OnNpemVfdC4uLiBWZXJ0aWNlcz4gIC8vIEZpcnN0LCBSZXN0Li4uIGFyZSB0aGUgdmVydGljZXMgdG8gYmUgdmlzaXRlZCBub3cgKGluIHRoYXQgb3JkZXIpLgpzdHJ1Y3QgVG9wb2xvZ2ljYWxTb3J0PFA8Rmlyc3QsIFJlc3QuLi4+LCBQPEVkZ2VzLi4uPiwgUDxWaXNpdGVkLi4uPiwgUDxMYXN0Q2hpbGRBbmRQYXJlbnQuLi4+LCBQPFZlcnRpY2VzLi4uPj4gOgoJVG9wb2xvZ2ljYWxTb3J0SGVscGVyPFA8Rmlyc3QsIFJlc3QuLi4+LCBQPEVkZ2VzLi4uPiwgUDxWaXNpdGVkLi4uLCBGaXJzdD4sIFA8TGFzdENoaWxkQW5kUGFyZW50Li4uPiwgUDxWZXJ0aWNlcy4uLj4sICAvLyBBcHBlbmQgRmlyc3QgdG8gUDxWaXNpdGVkLi4uPi4KCQkJdHlwZW5hbWUgUmVtb3ZlVGhvc2VBbHJlYWR5VmlzaXRlZDxzdGQ6OnNpemVfdCwgdHlwZW5hbWUgQWRqYWNlbnRWZXJ0aWNlczxGaXJzdCwgUDxFZGdlcy4uLj4+Ojp0eXBlLCBQPFZpc2l0ZWQuLi4+Pjo6dHlwZT4ge307IC8vIFBhc3MgdGhlIGFkamFjZW50IHZlcnRpY2VzIG9mIEZpcnN0IChpZiBhbnkpLCBleGNlcHQgdGhvc2UgdGhhdCBoYXZlIGFscmVhZHkgYmVlbiB2aXNpdGVkLgoKLy8gRmlyc3QgaGFzIGFkamFjZW50IGVkZ2VzLCBzbyB2aXNpdCBpdHMgYWRqYWNlbnQgdmVydGljZXMsIGFuZCBwcmVwZW5kIEZpcnN0IHRvIFA8VmVydGljZXMuLi4+IEFGVEVSIHRoZXNlIHZpc2l0cy4gIFRvIGVuc3VyZSB0aGlzIGxhc3QgcGFydCwgYWRkIEZpcnN0IGFuZCBpdHMgbGFzdCBjaGlsZCBpbnRvIFA8TGFzdENoaWxkQW5kUGFyZW50Li4uPiwgYW5kIGNoZWNrIGxhdGVyIG9uIHdoZW4gdGhpcyBsYXN0IGNoaWxkIG9mIEZpcnN0IGlzIHZpc2l0ZWQuICBOb3RlIHRoYXQgc2luY2UgYWxsIHZlcnRpY2VzIGhhdmUgVU5JUVVFIHN0ZDo6c2l6ZV90IElEIG51bWJlcnMsIHRoZXJlIHdpbGwgYmUgbm8gbWl4LXVwIHdpdGggb3RoZXIgdmVydGljZXMgd2hlbiB0aGlzIGxhc3QgY2hpbGQgaXMgdmlzaXRlZC4KdGVtcGxhdGUgPHRlbXBsYXRlIDxzdGQ6OnNpemVfdC4uLj4gY2xhc3MgUCwgc3RkOjpzaXplX3QgRmlyc3QsIHN0ZDo6c2l6ZV90Li4uIFJlc3QsIHN0ZDo6c2l6ZV90Li4uIFZpc2l0ZWQsIHN0ZDo6c2l6ZV90Li4uIEVkZ2VzLCBzdGQ6OnNpemVfdC4uLiBMYXN0Q2hpbGRBbmRQYXJlbnQsIHN0ZDo6c2l6ZV90Li4uIFZlcnRpY2VzLCB0eXBlbmFtZSBBZGphY2VudFZlcnRpY2VzPgpzdHJ1Y3QgVG9wb2xvZ2ljYWxTb3J0SGVscGVyPFA8Rmlyc3QsIFJlc3QuLi4+LCBQPEVkZ2VzLi4uPiwgUDxWaXNpdGVkLi4uPiwgUDxMYXN0Q2hpbGRBbmRQYXJlbnQuLi4+LCBQPFZlcnRpY2VzLi4uPiwgQWRqYWNlbnRWZXJ0aWNlcz4gOgoJQ2hlY2tJZkxhc3RBZGphY2VudFZlcnRleDx0eXBlbmFtZSBNZXJnZTxBZGphY2VudFZlcnRpY2VzLCBQPFJlc3QuLi4+Pjo6dHlwZSwgUDxFZGdlcy4uLj4sIFA8VmlzaXRlZC4uLj4sICAvLyBHbyBpbnRvIENoZWNrSWZMYXN0QWRqYWNlbnRWZXJ0ZXgsIGJlY2F1c2UgaXQgRmlyc3QgaXMgdGhlIGxhc3QgYWRqYWNlbnQgdmVydGV4IG9mIHNvbWUgcGFyZW50IHZlcnRleCwgdGhhdCBwYXJlbnQgdmVydGV4IHNoYWxsIGJlIHByZXBlbmRlZCB0byBQPFZlcnRpY2VzLi4uPiBpbW1lZGlhdGUgYWZ0ZXIgRmlyc3QgaXMgcHJlcGVuZGVkIHRvIFA8VmVydGljZXMuLi4+LgoJCVA8TGFzdENoaWxkQW5kUGFyZW50Li4uLCBMYXN0VHlwZTxzdGQ6OnNpemVfdCwgQWRqYWNlbnRWZXJ0aWNlcz46OnZhbHVlLCBGaXJzdD4sIFA8VmVydGljZXMuLi4+LCBJc0FMYXN0Q2hpbGQ8c3RkOjpzaXplX3QsIEZpcnN0LCBMYXN0Q2hpbGRBbmRQYXJlbnQuLi4+Ojp2YWx1ZT4ge307CgovLyBGaXJzdCBoYXMgbm8gYWRqYWNlbnQgdmVydGljZXMsIHNvIHByZXBlbmQgaXQgdG8gUDxWZXJ0aWNlcy4uLj4uICBIZXJlIHRvbywgd2UgbXVzdCBnbyBpbnRvIENoZWNrSWZMYXN0QWRqYWNlbnRWZXJ0ZXggaW4gY2FzZSBGaXJzdCBpcyB0aGUgbGFzdCBhZGphY2VudCB2ZXJ0ZXggb2Ygc29tZSBwYXJlbnQgdmVydGV4Lgp0ZW1wbGF0ZSA8dGVtcGxhdGUgPHN0ZDo6c2l6ZV90Li4uPiBjbGFzcyBQLCBzdGQ6OnNpemVfdCBGaXJzdCwgc3RkOjpzaXplX3QuLi4gUmVzdCwgc3RkOjpzaXplX3QuLi4gVmlzaXRlZCwgc3RkOjpzaXplX3QuLi4gRWRnZXMsIHN0ZDo6c2l6ZV90Li4uIExhc3RDaGlsZEFuZFBhcmVudCwgc3RkOjpzaXplX3QuLi4gVmVydGljZXM+CnN0cnVjdCBUb3BvbG9naWNhbFNvcnRIZWxwZXI8UDxGaXJzdCwgUmVzdC4uLj4sIFA8RWRnZXMuLi4+LCBQPFZpc2l0ZWQuLi4+LCBQPExhc3RDaGlsZEFuZFBhcmVudC4uLj4sIFA8VmVydGljZXMuLi4+LCBQPD4+IDoKCUNoZWNrSWZMYXN0QWRqYWNlbnRWZXJ0ZXg8UDxGaXJzdCwgUmVzdC4uLj4sIFA8RWRnZXMuLi4+LCBQPFZpc2l0ZWQuLi4+LCBQPExhc3RDaGlsZEFuZFBhcmVudC4uLj4sIFA8Rmlyc3QsIFZlcnRpY2VzLi4uPiwgSXNBTGFzdENoaWxkPHN0ZDo6c2l6ZV90LCBGaXJzdCwgTGFzdENoaWxkQW5kUGFyZW50Li4uPjo6dmFsdWU+IHt9OwoKLy8gRmlyc3QgaXMgdGhlIGxhc3QgYWRqYWNlbnQgdmVydGV4IG9mIHNvbWUgcGFyZW50IHZlcnRleCwgc28gcHJlcGVuZCBpdHMgcGFyZW50IHZlcnRleCB0byBQPFZlcnRpY2VzLi4uPi4gIE5vdGUgdGhhdCBGaXJzdCBjYW5ub3QgYmUgbWl4ZWQgdXAgd2l0aCBhbm90aGVyIHZlcnRleCBiZWNhdXNlIGFsbCB2ZXJ0aWNlcyBpbiB0aGUgR3JhcGggaGF2ZSBVTklRVUUgdmFsdWVzLgp0ZW1wbGF0ZSA8dGVtcGxhdGUgPHN0ZDo6c2l6ZV90Li4uPiBjbGFzcyBQLCBzdGQ6OnNpemVfdCBGaXJzdCwgc3RkOjpzaXplX3QuLi4gUmVzdCwgc3RkOjpzaXplX3QuLi4gVmlzaXRlZCwgc3RkOjpzaXplX3QuLi4gRWRnZXMsIHN0ZDo6c2l6ZV90Li4uIExhc3RDaGlsZEFuZFBhcmVudCwgc3RkOjpzaXplX3QuLi4gVmVydGljZXM+CnN0cnVjdCBDaGVja0lmTGFzdEFkamFjZW50VmVydGV4PFA8Rmlyc3QsIFJlc3QuLi4+LCBQPEVkZ2VzLi4uPiwgUDxWaXNpdGVkLi4uPiwgUDxMYXN0Q2hpbGRBbmRQYXJlbnQuLi4+LCBQPFZlcnRpY2VzLi4uPiwgdHJ1ZT4gOiAgLy8gRmlyc3QgYW5kIGl0cyBwYXJlbnQgdmVydGV4IG11c3QgYmUgcmVtb3ZlZCBmcm9tIFA8TGFzdENoaWxkQW5kUGFyZW50Li4uPiByaWdodCBub3cuCglUb3BvbG9naWNhbFNvcnQ8dHlwZW5hbWUgUmVtb3ZlVGhvc2VBbHJlYWR5VmlzaXRlZDxzdGQ6OnNpemVfdCwgUDxSZXN0Li4uPiwgUDxWaXNpdGVkLi4uPj46OnR5cGUsIFA8RWRnZXMuLi4+LCBQPFZpc2l0ZWQuLi4+LCAgLy8gV2UgTVVTVCByZW1vdmUgYWxsIHZlcnRpY2VzIHRoYXQgaGF2ZSBhbHJlYWR5IGJlZW4gdmlzaXRlZCBiZWZvcmUgZ29pbmcgaW50byBUb3BvbG9naWNhbFNvcnQsIHNpbmNlIFRvcG9sb2dpY2FsU29ydCBhbHdheXMgYXNzdW1lcyBGaXJzdCBoYXMgbm90IGJlZW4gdmlzaXRlZCBiZWZvcmUuCgkJdHlwZW5hbWUgUmVtb3ZlQ2hpbGRBbmRQYXJlbnQ8Rmlyc3QsIFA8TGFzdENoaWxkQW5kUGFyZW50Li4uPj46OnR5cGUsIFA8R2V0UGFyZW50PEZpcnN0LCBMYXN0Q2hpbGRBbmRQYXJlbnQuLi4+Ojp2YWx1ZSwgVmVydGljZXMuLi4+PiB7fTsgIC8vIERvIE5PVCBwcmVwZW5kIEZpcnN0IGludG8gUDxWZXJ0aWNlcy4uLj4gYmVjYXVzZSB0aGlzIHdhcyBhbHJlYWR5IGRvbmUgaW4gVG9wb2xvZ2ljYWxTb3J0SGVscGVyPFA8Rmlyc3QsIFJlc3QuLi4+LCBQPEVkZ2VzLi4uPiwgUDxWaXNpdGVkLi4uPiwgUDxMYXN0Q2hpbGRBbmRQYXJlbnQuLi4+LCBQPFZlcnRpY2VzLi4uPiwgUDw+Pi4KCQovLyBGaXJzdCBpcyBub3QgdGhlIGxhc3QgYWRqYWNlbnQgdmVydGV4IG9mIHNvbWUgcGFyZW50IHZlcnRleCwgc28gc2ltcGx5IGRvIHRoZSByZWd1bGFyIFRvcG9sb2dpY2FsU29ydCAoYnV0IHJlbW92aW5nIHRoZSB2ZXJ0aWNlcyBhbHJlYWR5IHZpc2l0ZWQgZmlyc3QsIG9mIGNvdXJzZSkuCnRlbXBsYXRlIDx0ZW1wbGF0ZSA8c3RkOjpzaXplX3QuLi4+IGNsYXNzIFAsIHN0ZDo6c2l6ZV90Li4uIFRvVmlzaXQsIHN0ZDo6c2l6ZV90Li4uIFZpc2l0ZWQsIHN0ZDo6c2l6ZV90Li4uIEVkZ2VzLCBzdGQ6OnNpemVfdC4uLiBMYXN0Q2hpbGRBbmRQYXJlbnQsIHN0ZDo6c2l6ZV90Li4uIFZlcnRpY2VzPiAgLy8gTm8gbmVlZCBmb3IgRmlyc3QsIFJlc3QuLi4gaGVyZSBzaW5jZSBGaXJzdCBuZWVkcyBub3QgYmUgaWRlbnRpZmllZCBpbiB0aGlzIGNhc2UuCnN0cnVjdCBDaGVja0lmTGFzdEFkamFjZW50VmVydGV4PFA8VG9WaXNpdC4uLj4sIFA8RWRnZXMuLi4+LCBQPFZpc2l0ZWQuLi4+LCBQPExhc3RDaGlsZEFuZFBhcmVudC4uLj4sIFA8VmVydGljZXMuLi4+LCBmYWxzZT4gOgoJVG9wb2xvZ2ljYWxTb3J0PHR5cGVuYW1lIFJlbW92ZVRob3NlQWxyZWFkeVZpc2l0ZWQ8c3RkOjpzaXplX3QsIFA8VG9WaXNpdC4uLj4sIFA8VmlzaXRlZC4uLj4+Ojp0eXBlLCBQPEVkZ2VzLi4uPiwgUDxWaXNpdGVkLi4uPiwgUDxMYXN0Q2hpbGRBbmRQYXJlbnQuLi4+LCBQPFZlcnRpY2VzLi4uPj4ge307Cgp0ZW1wbGF0ZSA8c3RkOjpzaXplX3QgTiwgc3RkOjpzaXplX3QuLi4gRWRnZXM+CnN0cnVjdCBHcmFwaCA6IFRvcG9sb2dpY2FsU29ydDxtYWtlX2luZGV4X3NlcXVlbmNlPE4+LCBpbmRleF9zZXF1ZW5jZTxFZGdlcy4uLj4sIGluZGV4X3NlcXVlbmNlPD4sIGluZGV4X3NlcXVlbmNlPD4sIGluZGV4X3NlcXVlbmNlPD4+IHt9OwoKLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0gVGVzdGluZyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQp0ZW1wbGF0ZSA8c3RkOjpzaXplX3QgTGFzdD4Kc3RydWN0IGluZGV4X3NlcXVlbmNlPExhc3Q+IHsKICAgIHN0YXRpYyB2b2lkIHByaW50KCkge3N0ZDo6Y291dCA8PCBMYXN0IDw8IHN0ZDo6ZW5kbDt9Cn07Cgp0ZW1wbGF0ZSA8c3RkOjpzaXplX3QgRmlyc3QsIHN0ZDo6c2l6ZV90Li4uIFJlc3Q+CnN0cnVjdCBpbmRleF9zZXF1ZW5jZTxGaXJzdCwgUmVzdC4uLj4gewogICAgc3RhdGljIHZvaWQgcHJpbnQoKSB7c3RkOjpjb3V0IDw8IEZpcnN0IDw8ICcgJzsgIGluZGV4X3NlcXVlbmNlPFJlc3QuLi4+OjpwcmludCgpO30KfTsKCmludCBtYWluKCkgewoJdXNpbmcgREFHMSA9IEdyYXBoPDYsIDUsMiwgNSwwLCA0LDAsIDQsMSwgMiwzLCAzLDE+OwoJREFHMTo6dG9wb2xvZ2ljYWxfc29ydDo6cHJpbnQoKTsgIC8vIDUgNCAyIDMgMSAwCglzdGQ6OmNvdXQgPDwgc3RkOjpib29sYWxwaGEgPDwgc3RkOjppc19zYW1lPERBRzE6OnRvcG9sb2dpY2FsX3NvcnQsIGluZGV4X3NlcXVlbmNlPDUsNCwyLDMsMSwwPj46OnZhbHVlIDw8IHN0ZDo6ZW5kbDsgIC8vIHRydWUKCQoJdXNpbmcgREFHMiA9IEdyYXBoPDgsIDAsMSwgMCwyLCAwLDMsIDEsNCwgMSw1LCAyLDQsIDIsNiwgMyw1LCAzLDYsIDQsNywgNSw3LCA2LDc+OwoJREFHMjo6dG9wb2xvZ2ljYWxfc29ydDo6cHJpbnQoKTsgIC8vIDAgMyAyIDYgMSA1IDQgNwoJc3RkOjpjb3V0IDw8IHN0ZDo6Ym9vbGFscGhhIDw8IHN0ZDo6aXNfc2FtZTxEQUcyOjp0b3BvbG9naWNhbF9zb3J0LCBpbmRleF9zZXF1ZW5jZTwwLDMsMiw2LDEsNSw0LDc+Pjo6dmFsdWUgPDwgc3RkOjplbmRsOyAgLy8gdHJ1ZQp9