/**
* Merge Sort using O(1) temporary storage.
* @see http://e...content-available-to-author-only...a.org/wiki/Merge_sort#Analysis
*
* Author: Erel Segal
* Created: 2010-10-17
*/
#include <vector>
#include <iostream>
#include <stdlib.h>
using namespace std;
/*
FUNCTIONS FOR DEBUGGING
*/
template <class Iterator> ostream& print (ostream& out, Iterator iFrom, Iterator iTo) {
for (; iFrom!=iTo; ++iFrom) out << *iFrom << " ";
return (out << endl);
}
template <class Iterator> bool assertSorted(Iterator iFrom, Iterator iTo) {
for (; iFrom<iTo-1; ++iFrom) {
if (*iFrom > *(iFrom+1)) {
cerr << "array is out of order: " << *iFrom << " > " << *(iFrom+1) << endl;
return false;
}
}
return true;
}
int* randomArray(size_t size) {
int* a = new int[size];
for (size_t i=0; i<size; ++i)
a[i] = rand()%100;
return a;
}
template <class T> void swap ( T& a, T& b ) { T c(a); a=b; b=c; }
size_t gcd(size_t n, size_t m) {return m==0?n:gcd(m,n%m);}
/**
* Rotate the combined range [iFrom1, iTo1)[iFrom2, iTo2) such that the elements [iFrom1,iTo1) will be at the end.
*/
template <class Iterator> void rotateRanges (Iterator iFrom1, Iterator iTo1, Iterator iFrom2, Iterator iTo2) {
size_t size1 = iTo1-iFrom1, size2 = iTo2-iFrom2, totalSize = size1+size2;
size_t gcd12 = gcd(size1,size2);
size_t offset1start, offset1, offset2;
Iterator iter1, iter2;
typename std::iterator_traits<Iterator>::value_type temp; // the O(1) temporary storage
for (offset1start=0; offset1start<gcd12; ++offset1start) {
temp = *(iFrom1+offset1start);
for (offset1=offset1start;;) {
iter1 = (offset1<size1? iFrom1+offset1: iFrom2+(offset1-size1));
offset2 = (offset1+size1)%totalSize;
iter2 = (offset2<size1? iFrom1+offset2: iFrom2+(offset2-size1));
if (offset2==offset1start) break; // completed a cycle
*iter1 = *iter2;
offset1 = offset2;
}
*iter1 = temp;
}
}
/**
* Merge the two sorted ranges using the given temporary storage (O(1) storage).
* PRECONDITION: [iFrom1, iTo1) are in ascending order, [iFrom2, iTo2) are in ascending order
* POSTCONDITION: [iFrom1, iTo1) are in ascending order AND <= [iFrom2, iTo2), who are also in ascending order
*/
template <class Iterator> void mergeRanges (Iterator iFrom1, Iterator iTo1, Iterator iFrom2, Iterator iTo2) {
// HERE: [iFrom1, iTo1) are in ascending order
// HERE: [iFrom2, iTo2) are in ascending order
Iterator i1 = iFrom1;
// HERE: [iFrom1, i1) are in ascending order and < [iFrom2, iTo2) [empty range]
for (;;) {
while (*i1 < *iFrom2 && i1 < iTo1) {
++i1;
}
// HERE: [iFrom1,i1) are in ascending order and < [iFrom2,iTo2)
if (i1 == iTo1) {
// HERE: [iFrom1, iTo1) are in ascending order and < [iFrom2, iTo2)
return; // POSTCONDITION satisfied.
}
// HERE: [i1] >= [iFrom2]
Iterator j2 = iFrom2;
// HERE: [iFrom2,j2) are in ascending order and <= [i1,iTo1) [empty range]
while (*j2 <= *i1 && j2 < iTo2) {
++j2;
}
// HERE: [iFrom2,j2) are in ascending order and <= [i1,iTo1)
if (j2 == iTo2) {
// HERE: [iFrom2,iTo2) <= [i1,iTo1)
rotateRanges(i1, iTo1, iFrom2, iTo2);
// HERE: [i1,iTo1) <= [iFrom2,iTo2) and in ascending order.
// Since [iFrom1,i1) are also in ascending order, it follows that:
// [iFrom1,iTo1) <= [iFrom2,iTo2) and in ascending order.
return; // POSTCONDITION satisfied.
}
// HERE: [j2] > [i1]
Iterator j1 = i1;
// HERE: [iFrom2,j2) <= [i1,j1) < [j2] [empty range]
while (*j1 < *j2 && j1 < iTo1) {
++j1;
}
// HERE: [iFrom2,j2) <= [i1,j1) < [j2]
rotateRanges(i1, j1, iFrom2, j2);
// HERE: [i1,j1) <= [iFrom2,j2) < [j2]
// HERE: [iFrom1,j1) are in ascending order and <= [iFrom2,iTo2)
i1 = j1;
// HERE: [iFrom1,i1) are in ascending order and <= [iFrom2,iTo2)
}
}
/* Sort the given array in the range [iFrom,iTo), using O(1) temporary storage */
template <class Iterator> void merge_sort(Iterator iFrom, Iterator iTo, string prefix="") {
if (iTo <= iFrom+1)
return;
// recursively sort each half seperately:
size_t diff = (iTo-iFrom);
cout << prefix << "merge_sort " << diff << " elements: "; print(cout, iFrom, iTo);
Iterator iMiddle = iFrom+diff/2;
merge_sort(iFrom, iMiddle, prefix+" ");
merge_sort(iMiddle, iTo, prefix+" ");
mergeRanges(iFrom, iMiddle, iMiddle, iTo);
cout << prefix << "sorted " << diff << " elements: "; print(cout, iFrom, iTo);
if (!assertSorted(iFrom, iTo))
exit(1);
}
#include <stdlib.h>
#include <time.h>
int main() {
{
cout << "Demonstrating rotateRanges: " << endl;
int a[] = {1,2, 101,102,103,104};
int b[] = {3,4,5,5,6,7,8,9};
cout << "before rotate: " << endl;
print (cout, a, a+6);
print (cout, b, b+8);
rotateRanges(a+2, a+6, b+0, b+8);
cout << "after rotate of a[2,6) with b[0,8) : " << endl;
print (cout, a, a+6);
print (cout, b, b+8);
cout << endl;
int c[] = {1,2, 101,102,103,104};
int d[] = {3,4};
cout << "before rotate: " << endl;
print (cout, c, c+6);
print (cout, d, d+2);
rotateRanges(c+2, c+6, d+0, d+2);
cout << "after rotate of c[2,6) with d[0,2): " << endl;
print (cout, c, c+6);
print (cout, d, d+2);
cout << endl << endl;
}
{
cout << "Demonstrating mergeRanges: " << endl;
int a[] = {3,8};
int b[] = {2,4,9};
cout << "before merge: " << endl;
print (cout, a, a+2);
print (cout, b, b+3);
mergeRanges(a, a+2, b, b+3);
cout << "after merge: " << endl;
print (cout, a, a+2);
print (cout, b, b+3);
cout << endl;
}
{
int c[] = {15,77,83,86,93};
int d[] = {21,35,86,86,92};
cout << "before merge: " << endl;
print (cout, c, c+5);
print (cout, d, d+5);
mergeRanges(c, c+5, d, d+5);
cout << "after merge: " << endl;
print (cout, c, c+5);
print (cout, d, d+5);
cout << endl << endl;
}
{
int c[] = {6,7,8,9,10};
int d[] = {1,2,3,4,5};
cout << "before merge: " << endl;
print (cout, c, c+5);
print (cout, d, d+5);
mergeRanges(c, c+5, d, d+5);
cout << "after merge: " << endl;
print (cout, c, c+5);
print (cout, d, d+5);
cout << endl << endl;
}
cout << "Demonstrating merge_sort: " << endl;
size_t size=30;
int* numbers = randomArray(size);
merge_sort(numbers, numbers+size);
assertSorted(numbers, numbers+size);
}
LyoqCiogTWVyZ2UgU29ydCB1c2luZyBPKDEpIHRlbXBvcmFyeSBzdG9yYWdlLgoqIEBzZWUgaHR0cDovL2UuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLmEub3JnL3dpa2kvTWVyZ2Vfc29ydCNBbmFseXNpcwoqCiogQXV0aG9yOiBFcmVsIFNlZ2FsCiogQ3JlYXRlZDogMjAxMC0xMC0xNwoqLwoKI2luY2x1ZGUgPHZlY3Rvcj4KI2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8c3RkbGliLmg+CnVzaW5nIG5hbWVzcGFjZSBzdGQ7CgoKLyoKICAgICAgICAgIEZVTkNUSU9OUyBGT1IgREVCVUdHSU5HIAoqLwoKdGVtcGxhdGUgPGNsYXNzIEl0ZXJhdG9yPiBvc3RyZWFtJiBwcmludCAob3N0cmVhbSYgb3V0LCBJdGVyYXRvciBpRnJvbSwgSXRlcmF0b3IgaVRvKSB7Cglmb3IgKDsgaUZyb20hPWlUbzsgKytpRnJvbSkgb3V0IDw8ICppRnJvbSA8PCAiICI7CglyZXR1cm4gKG91dCA8PCBlbmRsKTsKfQoKdGVtcGxhdGUgPGNsYXNzIEl0ZXJhdG9yPiBib29sIGFzc2VydFNvcnRlZChJdGVyYXRvciBpRnJvbSwgSXRlcmF0b3IgaVRvKSB7Cglmb3IgKDsgaUZyb208aVRvLTE7ICsraUZyb20pIHsKCQlpZiAoKmlGcm9tID4gKihpRnJvbSsxKSkgewoJCQljZXJyIDw8ICJhcnJheSBpcyBvdXQgb2Ygb3JkZXI6ICIgPDwgKmlGcm9tIDw8ICIgPiAiIDw8ICooaUZyb20rMSkgPDwgZW5kbDsKCQkJcmV0dXJuIGZhbHNlOwoJCX0KCX0KCXJldHVybiB0cnVlOwp9CiAKaW50KiByYW5kb21BcnJheShzaXplX3Qgc2l6ZSkgewoJaW50KiBhID0gbmV3IGludFtzaXplXTsKCWZvciAoc2l6ZV90IGk9MDsgaTxzaXplOyArK2kpCgkJYVtpXSA9IHJhbmQoKSUxMDA7CglyZXR1cm4gYTsKfQoKCnRlbXBsYXRlIDxjbGFzcyBUPiB2b2lkIHN3YXAgKCBUJiBhLCBUJiBiICkgeyBUIGMoYSk7IGE9YjsgYj1jOyB9CgpzaXplX3QgZ2NkKHNpemVfdCBuLCBzaXplX3QgbSkge3JldHVybiBtPT0wP246Z2NkKG0sbiVtKTt9CgoKCi8qKgogKiBSb3RhdGUgdGhlIGNvbWJpbmVkIHJhbmdlIFtpRnJvbTEsIGlUbzEpW2lGcm9tMiwgaVRvMikgc3VjaCB0aGF0IHRoZSBlbGVtZW50cyBbaUZyb20xLGlUbzEpIHdpbGwgYmUgYXQgdGhlIGVuZC4KICovIAp0ZW1wbGF0ZSA8Y2xhc3MgSXRlcmF0b3I+IHZvaWQgcm90YXRlUmFuZ2VzIChJdGVyYXRvciBpRnJvbTEsIEl0ZXJhdG9yIGlUbzEsIEl0ZXJhdG9yIGlGcm9tMiwgSXRlcmF0b3IgaVRvMikgewoJc2l6ZV90IHNpemUxID0gaVRvMS1pRnJvbTEsIHNpemUyID0gaVRvMi1pRnJvbTIsIHRvdGFsU2l6ZSA9IHNpemUxK3NpemUyOwoJc2l6ZV90IGdjZDEyID0gZ2NkKHNpemUxLHNpemUyKTsKCXNpemVfdCBvZmZzZXQxc3RhcnQsIG9mZnNldDEsIG9mZnNldDI7CglJdGVyYXRvciBpdGVyMSwgaXRlcjI7Cgl0eXBlbmFtZSBzdGQ6Oml0ZXJhdG9yX3RyYWl0czxJdGVyYXRvcj46OnZhbHVlX3R5cGUgdGVtcDsgLy8gdGhlIE8oMSkgdGVtcG9yYXJ5IHN0b3JhZ2UKCWZvciAob2Zmc2V0MXN0YXJ0PTA7IG9mZnNldDFzdGFydDxnY2QxMjsgKytvZmZzZXQxc3RhcnQpIHsKCQl0ZW1wID0gKihpRnJvbTErb2Zmc2V0MXN0YXJ0KTsKCQlmb3IgKG9mZnNldDE9b2Zmc2V0MXN0YXJ0OzspIHsKCQkJaXRlcjEgPSAob2Zmc2V0MTxzaXplMT8gaUZyb20xK29mZnNldDE6IGlGcm9tMisob2Zmc2V0MS1zaXplMSkpOwoJCQlvZmZzZXQyID0gKG9mZnNldDErc2l6ZTEpJXRvdGFsU2l6ZTsKCQkJaXRlcjIgPSAob2Zmc2V0MjxzaXplMT8gaUZyb20xK29mZnNldDI6IGlGcm9tMisob2Zmc2V0Mi1zaXplMSkpOwoJCQlpZiAob2Zmc2V0Mj09b2Zmc2V0MXN0YXJ0KSBicmVhazsgIC8vIGNvbXBsZXRlZCBhIGN5Y2xlCgkJCSppdGVyMSA9ICppdGVyMjsKCQkJb2Zmc2V0MSA9IG9mZnNldDI7CgkJfQoJCSppdGVyMSA9IHRlbXA7Cgl9Cn0KCi8qKgogKiBNZXJnZSB0aGUgdHdvIHNvcnRlZCByYW5nZXMgdXNpbmcgdGhlIGdpdmVuIHRlbXBvcmFyeSBzdG9yYWdlIChPKDEpIHN0b3JhZ2UpLgogKiBQUkVDT05ESVRJT046ICBbaUZyb20xLCBpVG8xKSBhcmUgaW4gYXNjZW5kaW5nIG9yZGVyLCBbaUZyb20yLCBpVG8yKSBhcmUgaW4gYXNjZW5kaW5nIG9yZGVyCiAqIFBPU1RDT05ESVRJT046IFtpRnJvbTEsIGlUbzEpIGFyZSBpbiBhc2NlbmRpbmcgb3JkZXIgQU5EIDw9IFtpRnJvbTIsIGlUbzIpLCB3aG8gYXJlIGFsc28gaW4gYXNjZW5kaW5nIG9yZGVyCiAqLyAKdGVtcGxhdGUgPGNsYXNzIEl0ZXJhdG9yPiB2b2lkIG1lcmdlUmFuZ2VzIChJdGVyYXRvciBpRnJvbTEsIEl0ZXJhdG9yIGlUbzEsIEl0ZXJhdG9yIGlGcm9tMiwgSXRlcmF0b3IgaVRvMikgewovLyBIRVJFOiBbaUZyb20xLCBpVG8xKSBhcmUgaW4gYXNjZW5kaW5nIG9yZGVyCi8vIEhFUkU6IFtpRnJvbTIsIGlUbzIpIGFyZSBpbiBhc2NlbmRpbmcgb3JkZXIKCglJdGVyYXRvciBpMSA9IGlGcm9tMTsKCS8vIEhFUkU6IFtpRnJvbTEsIGkxKSBhcmUgaW4gYXNjZW5kaW5nIG9yZGVyIGFuZCA8IFtpRnJvbTIsIGlUbzIpIFtlbXB0eSByYW5nZV0KCglmb3IgKDs7KSB7CgkJd2hpbGUgKCppMSA8ICppRnJvbTIgJiYgaTEgPCBpVG8xKSB7CgkJCSsraTE7CgkJfQoJCS8vIEhFUkU6IFtpRnJvbTEsaTEpIGFyZSBpbiBhc2NlbmRpbmcgb3JkZXIgYW5kIDwgW2lGcm9tMixpVG8yKQoKCQlpZiAoaTEgPT0gaVRvMSkgewoJCQkvLyBIRVJFOiBbaUZyb20xLCBpVG8xKSBhcmUgaW4gYXNjZW5kaW5nIG9yZGVyIGFuZCA8IFtpRnJvbTIsIGlUbzIpCgkJCXJldHVybjsgIC8vIFBPU1RDT05ESVRJT04gc2F0aXNmaWVkLgoJCX0KCgkJLy8gSEVSRTogW2kxXSA+PSBbaUZyb20yXQoKCQlJdGVyYXRvciBqMiA9IGlGcm9tMjsKCQkvLyBIRVJFOiBbaUZyb20yLGoyKSBhcmUgaW4gYXNjZW5kaW5nIG9yZGVyIGFuZCA8PSBbaTEsaVRvMSkgW2VtcHR5IHJhbmdlXQoJCXdoaWxlICgqajIgPD0gKmkxICYmIGoyIDwgaVRvMikgewoJCQkrK2oyOwoJCX0KCQkvLyBIRVJFOiBbaUZyb20yLGoyKSBhcmUgaW4gYXNjZW5kaW5nIG9yZGVyIGFuZCA8PSBbaTEsaVRvMSkKCgkJaWYgKGoyID09IGlUbzIpIHsKCQkvLyBIRVJFOiBbaUZyb20yLGlUbzIpIDw9IFtpMSxpVG8xKQoJCQlyb3RhdGVSYW5nZXMoaTEsIGlUbzEsIGlGcm9tMiwgaVRvMik7CgkJCS8vIEhFUkU6IFtpMSxpVG8xKSA8PSBbaUZyb20yLGlUbzIpIGFuZCBpbiBhc2NlbmRpbmcgb3JkZXIuCgkJCS8vIFNpbmNlIFtpRnJvbTEsaTEpIGFyZSBhbHNvIGluIGFzY2VuZGluZyBvcmRlciwgaXQgZm9sbG93cyB0aGF0OgoJCQkvLyAgICBbaUZyb20xLGlUbzEpIDw9IFtpRnJvbTIsaVRvMikgYW5kIGluIGFzY2VuZGluZyBvcmRlci4KCQkJcmV0dXJuOyAgLy8gUE9TVENPTkRJVElPTiBzYXRpc2ZpZWQuCgkJfQoKCQkvLyBIRVJFOiBbajJdID4gW2kxXQoKCQlJdGVyYXRvciBqMSA9IGkxOwoJCS8vIEhFUkU6IFtpRnJvbTIsajIpIDw9IFtpMSxqMSkgPCBbajJdICAgW2VtcHR5IHJhbmdlXQoKCQl3aGlsZSAoKmoxIDwgKmoyICYmIGoxIDwgaVRvMSkgewoJCQkrK2oxOwoJCX0KCQkvLyBIRVJFOiBbaUZyb20yLGoyKSA8PSBbaTEsajEpIDwgW2oyXQoKCQlyb3RhdGVSYW5nZXMoaTEsIGoxLCBpRnJvbTIsIGoyKTsKCQkvLyBIRVJFOiBbaTEsajEpIDw9IFtpRnJvbTIsajIpIDwgW2oyXQoJCS8vIEhFUkU6IFtpRnJvbTEsajEpIGFyZSBpbiBhc2NlbmRpbmcgb3JkZXIgYW5kIDw9IFtpRnJvbTIsaVRvMikKCgkJaTEgPSBqMTsKCQkvLyBIRVJFOiBbaUZyb20xLGkxKSBhcmUgaW4gYXNjZW5kaW5nIG9yZGVyIGFuZCA8PSBbaUZyb20yLGlUbzIpCgl9Cn0KCgovKiBTb3J0IHRoZSBnaXZlbiBhcnJheSBpbiB0aGUgcmFuZ2UgW2lGcm9tLGlUbyksIHVzaW5nIE8oMSkgdGVtcG9yYXJ5IHN0b3JhZ2UgICovCnRlbXBsYXRlIDxjbGFzcyBJdGVyYXRvcj4gdm9pZCBtZXJnZV9zb3J0KEl0ZXJhdG9yIGlGcm9tLCBJdGVyYXRvciBpVG8sIHN0cmluZyBwcmVmaXg9IiIpIHsKCWlmIChpVG8gPD0gaUZyb20rMSkKCQlyZXR1cm47CgoJLy8gcmVjdXJzaXZlbHkgc29ydCBlYWNoIGhhbGYgc2VwZXJhdGVseToKCXNpemVfdCBkaWZmID0gKGlUby1pRnJvbSk7Cgljb3V0IDw8IHByZWZpeCA8PCAibWVyZ2Vfc29ydCAiIDw8IGRpZmYgPDwgIiBlbGVtZW50czogIjsgcHJpbnQoY291dCwgaUZyb20sIGlUbyk7CglJdGVyYXRvciBpTWlkZGxlID0gaUZyb20rZGlmZi8yOwoKCW1lcmdlX3NvcnQoaUZyb20sIGlNaWRkbGUsIHByZWZpeCsiICIpOwoJbWVyZ2Vfc29ydChpTWlkZGxlLCBpVG8sIHByZWZpeCsiICIpOwoKCW1lcmdlUmFuZ2VzKGlGcm9tLCBpTWlkZGxlLCBpTWlkZGxlLCBpVG8pOwoKCWNvdXQgPDwgcHJlZml4IDw8ICJzb3J0ZWQgIiA8PCBkaWZmIDw8ICIgZWxlbWVudHM6ICI7IHByaW50KGNvdXQsIGlGcm9tLCBpVG8pOwoJaWYgKCFhc3NlcnRTb3J0ZWQoaUZyb20sIGlUbykpCgkJZXhpdCgxKTsKfQoKCiNpbmNsdWRlIDxzdGRsaWIuaD4KI2luY2x1ZGUgPHRpbWUuaD4KaW50IG1haW4oKSB7Cgl7Cgljb3V0IDw8ICJEZW1vbnN0cmF0aW5nIHJvdGF0ZVJhbmdlczogIiA8PCBlbmRsOwoJaW50IGFbXSA9IHsxLDIsIDEwMSwxMDIsMTAzLDEwNH07CglpbnQgYltdID0gezMsNCw1LDUsNiw3LDgsOX07Cgljb3V0IDw8ICJiZWZvcmUgcm90YXRlOiAiIDw8IGVuZGw7CglwcmludCAoY291dCwgYSwgYSs2KTsgCglwcmludCAoY291dCwgYiwgYis4KTsgCglyb3RhdGVSYW5nZXMoYSsyLCBhKzYsIGIrMCwgYis4KTsKCWNvdXQgPDwgImFmdGVyIHJvdGF0ZSBvZiBhWzIsNikgd2l0aCBiWzAsOCkgOiAiIDw8IGVuZGw7CglwcmludCAoY291dCwgYSwgYSs2KTsgCglwcmludCAoY291dCwgYiwgYis4KTsgCgljb3V0IDw8IGVuZGw7CgoJaW50IGNbXSA9IHsxLDIsIDEwMSwxMDIsMTAzLDEwNH07CglpbnQgZFtdID0gezMsNH07Cgljb3V0IDw8ICJiZWZvcmUgcm90YXRlOiAiIDw8IGVuZGw7CglwcmludCAoY291dCwgYywgYys2KTsgCglwcmludCAoY291dCwgZCwgZCsyKTsgCglyb3RhdGVSYW5nZXMoYysyLCBjKzYsIGQrMCwgZCsyKTsKCWNvdXQgPDwgImFmdGVyIHJvdGF0ZSBvZiBjWzIsNikgd2l0aCBkWzAsMik6ICIgPDwgZW5kbDsKCXByaW50IChjb3V0LCBjLCBjKzYpOyAKCXByaW50IChjb3V0LCBkLCBkKzIpOyAgIAoJY291dCA8PCBlbmRsIDw8IGVuZGw7Cgl9CgkKCXsKCWNvdXQgPDwgIkRlbW9uc3RyYXRpbmcgbWVyZ2VSYW5nZXM6ICIgPDwgZW5kbDsKCWludCBhW10gPSB7Myw4fTsKCWludCBiW10gPSB7Miw0LDl9OwoJY291dCA8PCAiYmVmb3JlIG1lcmdlOiAiIDw8IGVuZGw7CglwcmludCAoY291dCwgYSwgYSsyKTsgCglwcmludCAoY291dCwgYiwgYiszKTsgCgltZXJnZVJhbmdlcyhhLCBhKzIsIGIsIGIrMyk7Cgljb3V0IDw8ICJhZnRlciBtZXJnZTogIiA8PCBlbmRsOwoJcHJpbnQgKGNvdXQsIGEsIGErMik7IAoJcHJpbnQgKGNvdXQsIGIsIGIrMyk7IAoJY291dCA8PCBlbmRsOwoJfQoJewoJaW50IGNbXSA9IHsxNSw3Nyw4Myw4Niw5M307CglpbnQgZFtdID0gezIxLDM1LDg2LDg2LDkyfTsKCWNvdXQgPDwgImJlZm9yZSBtZXJnZTogIiA8PCBlbmRsOwoJcHJpbnQgKGNvdXQsIGMsIGMrNSk7IAoJcHJpbnQgKGNvdXQsIGQsIGQrNSk7IAoJbWVyZ2VSYW5nZXMoYywgYys1LCBkLCBkKzUpOwoJY291dCA8PCAiYWZ0ZXIgbWVyZ2U6ICIgPDwgZW5kbDsKCXByaW50IChjb3V0LCBjLCBjKzUpOyAKCXByaW50IChjb3V0LCBkLCBkKzUpOyAKCWNvdXQgPDwgZW5kbCA8PCBlbmRsOwoJfQoJewoJaW50IGNbXSA9IHs2LDcsOCw5LDEwfTsKCWludCBkW10gPSB7MSwyLDMsNCw1fTsKCWNvdXQgPDwgImJlZm9yZSBtZXJnZTogIiA8PCBlbmRsOwoJcHJpbnQgKGNvdXQsIGMsIGMrNSk7IAoJcHJpbnQgKGNvdXQsIGQsIGQrNSk7IAoJbWVyZ2VSYW5nZXMoYywgYys1LCBkLCBkKzUpOwoJY291dCA8PCAiYWZ0ZXIgbWVyZ2U6ICIgPDwgZW5kbDsKCXByaW50IChjb3V0LCBjLCBjKzUpOyAKCXByaW50IChjb3V0LCBkLCBkKzUpOyAKCWNvdXQgPDwgZW5kbCA8PCBlbmRsOwoJfQoKCWNvdXQgPDwgIkRlbW9uc3RyYXRpbmcgbWVyZ2Vfc29ydDogIiA8PCBlbmRsOwoJc2l6ZV90IHNpemU9MzA7CglpbnQqIG51bWJlcnMgPSByYW5kb21BcnJheShzaXplKTsKCW1lcmdlX3NvcnQobnVtYmVycywgbnVtYmVycytzaXplKTsKCWFzc2VydFNvcnRlZChudW1iZXJzLCBudW1iZXJzK3NpemUpOwp9Cg==