#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <tuple>
#include <string>
using namespace std;
using Portfolio = vector<int>; // weights in percent, size = 6
double percentile15(vector<double>& data) {
sort(data.begin(), data.end());
size_t index = static_cast<size_t>(floor(0.15 * data.size()));
if (index >= data.size()) index = data.size() - 1;
return data[index];
}
double median(vector<double>& data) {
sort(data.begin(), data.end());
size_t n = data.size();
if (n % 2 == 0)
return (data[n / 2 - 1] + data[n / 2]) / 2.0;
else
return data[n / 2];
}
double lumpSumCAGR(const vector<double>& returns) {
double compound = 1.0;
for (double r : returns) {
compound *= (1.0 + r);
}
return pow(compound, 1.0 / returns.size()) - 1.0;
}
double portfolioReturn(const vector<vector<double>>& assets, const Portfolio& weights, int year) {
double result = 0.0;
for (int i = 0; i < weights.size(); ++i) {
result += (weights[i] / 100.0) * assets[i][year];
}
return result;
}
void generatePortfolios(vector<Portfolio>& portfolios, Portfolio current, int pos, int totalWeight, int maxAssets) {
if (pos == current.size()) {
if (totalWeight == 100) {
int nonZero = count_if(current.begin(), current.end(), [](int w) { return w > 0; });
if (nonZero <= maxAssets) {
portfolios.push_back(current);
}
}
return;
}
for (int w = 0; w <= 100 - totalWeight; w += 5) {
current[pos] = w;
generatePortfolios(portfolios, current, pos + 1, totalWeight + w, maxAssets);
}
}
int main() {
const int numAssets = 6;
const int maxAssets = 3;
const int step = 5;
// Asset names
vector<string> assetNames = {
"scv60Lcg15", "scv95", "vti", "11all", "33gld", "lcbtiltled"
};
// Return data
vector<vector<double>> assets = {
{-0.04,0.14,0.15,-0.14,-0.19,0.26,0.28,0.04,0.08,0.28,0.08,-0.06,0.24,0.22,-0.01,0.26,0.14,-0.02,0.14,0.16,-0.16,0.29,0.15,0.18,-0.03,0.20,0.13,0.20,0.02,0.08,-0.04,0.04,-0.07,0.33,0.12,0.06,0.15,0.01,-0.26,0.31,0.22,-0.03,0.15,0.19,0.08,-0.06,0.16,0.13,-0.11,0.21,0.13,0.12,-0.18,0.15,0.12},
{-0.03,0.13,0.06,-0.29,-0.31,0.43,0.46,0.08,0.07,0.21,0.12,0.06,0.31,0.38,0.02,0.31,0.12,-0.08,0.23,0.14,-0.22,0.37,0.24,0.19,-0.02,0.24,0.17,0.31,-0.06,0.02,0.04,0.11,-0.13,0.41,0.17,0.04,0.15,-0.09,-0.32,0.33,0.23,-0.06,0.18,0.32,0.09,-0.06,0.23,0.09,-0.15,0.20,0.03,0.19,-0.16,0.11,0.08},
{-0.02,0.11,0.12,-0.25,-0.36,0.30,0.21,-0.11,-0.01,0.05,0.16,-0.11,0.19,0.19,0.01,0.28,0.15,-0.02,0.12,0.24,-0.10,0.29,0.06,0.08,-0.01,0.34,0.19,0.30,0.24,0.19,-0.13,-0.09,-0.25,0.30,0.08,0.02,0.12,0.01,-0.37,0.26,0.16,-0.02,0.14,0.31,0.12,0.00,0.11,0.19,-0.07,0.28,0.19,0.18,-0.24,0.22,0.20},
{-0.02,0.12,0.14,-0.13,-0.19,0.22,0.26,0.04,0.06,0.19,0.07,-0.07,0.20,0.20,0.03,0.26,0.19,0.00,0.14,0.15,-0.16,0.24,0.10,0.20,-0.02,0.19,0.12,0.17,0.01,0.05,0.01,0.02,-0.06,0.32,0.14,0.07,0.17,-0.01,-0.25,0.25,0.20,0.00,0.14,0.15,0.11,-0.04,0.13,0.12,-0.10,0.20,0.07,0.12,-0.19,0.10,0.08},
{-0.01,0.11,0.19,0.03,-0.05,0.13,0.23,0.07,0.10,0.44,0.08,-0.13,0.20,0.14,-0.04,0.19,0.16,0.02,0.06,0.08,-0.15,0.17,0.08,0.16,-0.03,0.17,0.11,0.12,0.00,0.02,-0.01,0.02,-0.02,0.30,0.11,0.07,0.19,0.03,-0.21,0.25,0.23,0.00,0.13,0.08,0.09,-0.06,0.13,0.10,-0.08,0.20,0.10,0.11,-0.14,0.11,0.14},
{-0.03,0.13,0.12,-0.22,-0.30,0.25,0.24,-0.02,0.00,0.07,0.08,-0.09,0.14,0.20,0.07,0.26,0.15,-0.02,0.16,0.24,-0.13,0.33,0.10,0.21,-0.03,0.22,0.16,0.21,0.05,0.15,-0.05,-0.02,-0.12,0.32,0.14,0.08,0.17,0.02,-0.32,0.29,0.17,-0.01,0.14,0.17,0.13,-0.03,0.11,0.16,-0.09,0.23,0.11,0.14,-0.24,0.14,0.11}
};
int numYears = assets[0].size();
for (int duration = 1; duration <= 31; duration += 2) {
cout << "\n========== Duration: " << duration << " years ==========" << endl;
vector<Portfolio> portfolios;
generatePortfolios(portfolios, Portfolio(numAssets, 0), 0, 0, maxAssets);
vector<tuple<Portfolio, double, double, double, double>> results;
for (const Portfolio& p : portfolios) {
vector<double> cagrList;
for (int start = 0; start <= numYears - duration; ++start) {
vector<double> subReturns;
for (int i = 0; i < duration; ++i) {
subReturns.push_back(portfolioReturn(assets, p, start + i));
}
double cagr = lumpSumCAGR(subReturns);
cagrList.push_back(cagr);
}
double p15 = percentile15(cagrList);
double pmin = *min_element(cagrList.begin(), cagrList.end());
double pmax = *max_element(cagrList.begin(), cagrList.end());
double pmed = median(cagrList);
results.emplace_back(p, p15, pmin, pmax, pmed);
}
sort(results.begin(), results.end(), [](const auto& a, const auto& b) {
return get<1>(a) > get<1>(b);
});
cout << fixed << setprecision(2);
// Summary table
cout << "\nTop 2 Portfolios Summary (Bottom 15th Percentile CAGR):" << endl;
cout << left << setw(10) << "#" << setw(40) << "Portfolio" << setw(10) << "P15%" << setw(10) << "Min" << setw(10) << "Max" << setw(10) << "Median" << endl;
cout << string(80, '-') << endl;
for (int i = 0; i < 2 && i < results.size(); ++i) {
auto [weights, p15, pmin, pmax, pmed] = results[i];
string portStr;
for (int j = 0; j < numAssets; ++j) {
if (weights[j] > 0) {
portStr += assetNames[j] + ":" + to_string(weights[j]) + "% ";
}
}
cout << left << setw(10) << (i + 1) << setw(40) << portStr << setw(10) << p15 * 100 << setw(10) << pmin * 100 << setw(10) << pmax * 100 << setw(10) << pmed * 100 << endl;
}
// // Detailed breakdown
// for (int i = 0; i < 2 && i < results.size(); ++i) {
// auto [weights, p15, pmin, pmax, pmed] = results[i];
// cout << "\nPortfolio " << (i + 1) << ":\n";
// cout << left << setw(15) << "Asset" << setw(10) << "Weight\n";
// cout << "-------------------------\n";
// for (int j = 0; j < numAssets; ++j) {
// if (weights[j] > 0) {
// cout << left << setw(15) << assetNames[j] << setw(10) << weights[j] << "\n";
// }
// }
// cout << "-------------------------\n";
// cout << "Bottom 15th % CAGR : " << p15 * 100 << "%\n";
// cout << "Min CAGR : " << pmin * 100 << "%\n";
// cout << "Max CAGR : " << pmax * 100 << "%\n";
// cout << "Median CAGR : " << pmed * 100 << "%\n";
// }
}
return 0;
}
I2luY2x1ZGUgPGlvc3RyZWFtPgojaW5jbHVkZSA8dmVjdG9yPgojaW5jbHVkZSA8Y21hdGg+CiNpbmNsdWRlIDxhbGdvcml0aG0+CiNpbmNsdWRlIDxpb21hbmlwPgojaW5jbHVkZSA8dHVwbGU+CiNpbmNsdWRlIDxzdHJpbmc+Cgp1c2luZyBuYW1lc3BhY2Ugc3RkOwoKdXNpbmcgUG9ydGZvbGlvID0gdmVjdG9yPGludD47IC8vIHdlaWdodHMgaW4gcGVyY2VudCwgc2l6ZSA9IDYKCmRvdWJsZSBwZXJjZW50aWxlMTUodmVjdG9yPGRvdWJsZT4mIGRhdGEpIHsKICAgIHNvcnQoZGF0YS5iZWdpbigpLCBkYXRhLmVuZCgpKTsKICAgIHNpemVfdCBpbmRleCA9IHN0YXRpY19jYXN0PHNpemVfdD4oZmxvb3IoMC4xNSAqIGRhdGEuc2l6ZSgpKSk7CiAgICBpZiAoaW5kZXggPj0gZGF0YS5zaXplKCkpIGluZGV4ID0gZGF0YS5zaXplKCkgLSAxOwogICAgcmV0dXJuIGRhdGFbaW5kZXhdOwp9Cgpkb3VibGUgbWVkaWFuKHZlY3Rvcjxkb3VibGU+JiBkYXRhKSB7CiAgICBzb3J0KGRhdGEuYmVnaW4oKSwgZGF0YS5lbmQoKSk7CiAgICBzaXplX3QgbiA9IGRhdGEuc2l6ZSgpOwogICAgaWYgKG4gJSAyID09IDApCiAgICAgICAgcmV0dXJuIChkYXRhW24gLyAyIC0gMV0gKyBkYXRhW24gLyAyXSkgLyAyLjA7CiAgICBlbHNlCiAgICAgICAgcmV0dXJuIGRhdGFbbiAvIDJdOwp9Cgpkb3VibGUgbHVtcFN1bUNBR1IoY29uc3QgdmVjdG9yPGRvdWJsZT4mIHJldHVybnMpIHsKICAgIGRvdWJsZSBjb21wb3VuZCA9IDEuMDsKICAgIGZvciAoZG91YmxlIHIgOiByZXR1cm5zKSB7CiAgICAgICAgY29tcG91bmQgKj0gKDEuMCArIHIpOwogICAgfQogICAgcmV0dXJuIHBvdyhjb21wb3VuZCwgMS4wIC8gcmV0dXJucy5zaXplKCkpIC0gMS4wOwp9Cgpkb3VibGUgcG9ydGZvbGlvUmV0dXJuKGNvbnN0IHZlY3Rvcjx2ZWN0b3I8ZG91YmxlPj4mIGFzc2V0cywgY29uc3QgUG9ydGZvbGlvJiB3ZWlnaHRzLCBpbnQgeWVhcikgewogICAgZG91YmxlIHJlc3VsdCA9IDAuMDsKICAgIGZvciAoaW50IGkgPSAwOyBpIDwgd2VpZ2h0cy5zaXplKCk7ICsraSkgewogICAgICAgIHJlc3VsdCArPSAod2VpZ2h0c1tpXSAvIDEwMC4wKSAqIGFzc2V0c1tpXVt5ZWFyXTsKICAgIH0KICAgIHJldHVybiByZXN1bHQ7Cn0KCnZvaWQgZ2VuZXJhdGVQb3J0Zm9saW9zKHZlY3RvcjxQb3J0Zm9saW8+JiBwb3J0Zm9saW9zLCBQb3J0Zm9saW8gY3VycmVudCwgaW50IHBvcywgaW50IHRvdGFsV2VpZ2h0LCBpbnQgbWF4QXNzZXRzKSB7CiAgICBpZiAocG9zID09IGN1cnJlbnQuc2l6ZSgpKSB7CiAgICAgICAgaWYgKHRvdGFsV2VpZ2h0ID09IDEwMCkgewogICAgICAgICAgICBpbnQgbm9uWmVybyA9IGNvdW50X2lmKGN1cnJlbnQuYmVnaW4oKSwgY3VycmVudC5lbmQoKSwgW10oaW50IHcpIHsgcmV0dXJuIHcgPiAwOyB9KTsKICAgICAgICAgICAgaWYgKG5vblplcm8gPD0gbWF4QXNzZXRzKSB7CiAgICAgICAgICAgICAgICBwb3J0Zm9saW9zLnB1c2hfYmFjayhjdXJyZW50KTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm47CiAgICB9CiAgICBmb3IgKGludCB3ID0gMDsgdyA8PSAxMDAgLSB0b3RhbFdlaWdodDsgdyArPSA1KSB7CiAgICAgICAgY3VycmVudFtwb3NdID0gdzsKICAgICAgICBnZW5lcmF0ZVBvcnRmb2xpb3MocG9ydGZvbGlvcywgY3VycmVudCwgcG9zICsgMSwgdG90YWxXZWlnaHQgKyB3LCBtYXhBc3NldHMpOwogICAgfQp9CgppbnQgbWFpbigpIHsKICAgIGNvbnN0IGludCBudW1Bc3NldHMgPSA2OwogICAgY29uc3QgaW50IG1heEFzc2V0cyA9IDM7CiAgICBjb25zdCBpbnQgc3RlcCA9IDU7CgogICAgLy8gQXNzZXQgbmFtZXMKICAgIHZlY3RvcjxzdHJpbmc+IGFzc2V0TmFtZXMgPSB7CiAgICAgICAgInNjdjYwTGNnMTUiLCAic2N2OTUiLCAidnRpIiwgIjExYWxsIiwgIjMzZ2xkIiwgImxjYnRpbHRsZWQiCiAgICB9OwoKICAgIC8vIFJldHVybiBkYXRhCiAgICB2ZWN0b3I8dmVjdG9yPGRvdWJsZT4+IGFzc2V0cyA9IHsKICAgICAgICB7LTAuMDQsMC4xNCwwLjE1LC0wLjE0LC0wLjE5LDAuMjYsMC4yOCwwLjA0LDAuMDgsMC4yOCwwLjA4LC0wLjA2LDAuMjQsMC4yMiwtMC4wMSwwLjI2LDAuMTQsLTAuMDIsMC4xNCwwLjE2LC0wLjE2LDAuMjksMC4xNSwwLjE4LC0wLjAzLDAuMjAsMC4xMywwLjIwLDAuMDIsMC4wOCwtMC4wNCwwLjA0LC0wLjA3LDAuMzMsMC4xMiwwLjA2LDAuMTUsMC4wMSwtMC4yNiwwLjMxLDAuMjIsLTAuMDMsMC4xNSwwLjE5LDAuMDgsLTAuMDYsMC4xNiwwLjEzLC0wLjExLDAuMjEsMC4xMywwLjEyLC0wLjE4LDAuMTUsMC4xMn0sCiAgICAgICAgey0wLjAzLDAuMTMsMC4wNiwtMC4yOSwtMC4zMSwwLjQzLDAuNDYsMC4wOCwwLjA3LDAuMjEsMC4xMiwwLjA2LDAuMzEsMC4zOCwwLjAyLDAuMzEsMC4xMiwtMC4wOCwwLjIzLDAuMTQsLTAuMjIsMC4zNywwLjI0LDAuMTksLTAuMDIsMC4yNCwwLjE3LDAuMzEsLTAuMDYsMC4wMiwwLjA0LDAuMTEsLTAuMTMsMC40MSwwLjE3LDAuMDQsMC4xNSwtMC4wOSwtMC4zMiwwLjMzLDAuMjMsLTAuMDYsMC4xOCwwLjMyLDAuMDksLTAuMDYsMC4yMywwLjA5LC0wLjE1LDAuMjAsMC4wMywwLjE5LC0wLjE2LDAuMTEsMC4wOH0sCiAgICAgICAgey0wLjAyLDAuMTEsMC4xMiwtMC4yNSwtMC4zNiwwLjMwLDAuMjEsLTAuMTEsLTAuMDEsMC4wNSwwLjE2LC0wLjExLDAuMTksMC4xOSwwLjAxLDAuMjgsMC4xNSwtMC4wMiwwLjEyLDAuMjQsLTAuMTAsMC4yOSwwLjA2LDAuMDgsLTAuMDEsMC4zNCwwLjE5LDAuMzAsMC4yNCwwLjE5LC0wLjEzLC0wLjA5LC0wLjI1LDAuMzAsMC4wOCwwLjAyLDAuMTIsMC4wMSwtMC4zNywwLjI2LDAuMTYsLTAuMDIsMC4xNCwwLjMxLDAuMTIsMC4wMCwwLjExLDAuMTksLTAuMDcsMC4yOCwwLjE5LDAuMTgsLTAuMjQsMC4yMiwwLjIwfSwKICAgICAgICB7LTAuMDIsMC4xMiwwLjE0LC0wLjEzLC0wLjE5LDAuMjIsMC4yNiwwLjA0LDAuMDYsMC4xOSwwLjA3LC0wLjA3LDAuMjAsMC4yMCwwLjAzLDAuMjYsMC4xOSwwLjAwLDAuMTQsMC4xNSwtMC4xNiwwLjI0LDAuMTAsMC4yMCwtMC4wMiwwLjE5LDAuMTIsMC4xNywwLjAxLDAuMDUsMC4wMSwwLjAyLC0wLjA2LDAuMzIsMC4xNCwwLjA3LDAuMTcsLTAuMDEsLTAuMjUsMC4yNSwwLjIwLDAuMDAsMC4xNCwwLjE1LDAuMTEsLTAuMDQsMC4xMywwLjEyLC0wLjEwLDAuMjAsMC4wNywwLjEyLC0wLjE5LDAuMTAsMC4wOH0sCiAgICAgICAgey0wLjAxLDAuMTEsMC4xOSwwLjAzLC0wLjA1LDAuMTMsMC4yMywwLjA3LDAuMTAsMC40NCwwLjA4LC0wLjEzLDAuMjAsMC4xNCwtMC4wNCwwLjE5LDAuMTYsMC4wMiwwLjA2LDAuMDgsLTAuMTUsMC4xNywwLjA4LDAuMTYsLTAuMDMsMC4xNywwLjExLDAuMTIsMC4wMCwwLjAyLC0wLjAxLDAuMDIsLTAuMDIsMC4zMCwwLjExLDAuMDcsMC4xOSwwLjAzLC0wLjIxLDAuMjUsMC4yMywwLjAwLDAuMTMsMC4wOCwwLjA5LC0wLjA2LDAuMTMsMC4xMCwtMC4wOCwwLjIwLDAuMTAsMC4xMSwtMC4xNCwwLjExLDAuMTR9LAogICAgICAgIHstMC4wMywwLjEzLDAuMTIsLTAuMjIsLTAuMzAsMC4yNSwwLjI0LC0wLjAyLDAuMDAsMC4wNywwLjA4LC0wLjA5LDAuMTQsMC4yMCwwLjA3LDAuMjYsMC4xNSwtMC4wMiwwLjE2LDAuMjQsLTAuMTMsMC4zMywwLjEwLDAuMjEsLTAuMDMsMC4yMiwwLjE2LDAuMjEsMC4wNSwwLjE1LC0wLjA1LC0wLjAyLC0wLjEyLDAuMzIsMC4xNCwwLjA4LDAuMTcsMC4wMiwtMC4zMiwwLjI5LDAuMTcsLTAuMDEsMC4xNCwwLjE3LDAuMTMsLTAuMDMsMC4xMSwwLjE2LC0wLjA5LDAuMjMsMC4xMSwwLjE0LC0wLjI0LDAuMTQsMC4xMX0KICAgIH07CgogICAgaW50IG51bVllYXJzID0gYXNzZXRzWzBdLnNpemUoKTsKCiAgICBmb3IgKGludCBkdXJhdGlvbiA9IDE7IGR1cmF0aW9uIDw9IDMxOyBkdXJhdGlvbiArPSAyKSB7CiAgICAgICAgY291dCA8PCAiXG49PT09PT09PT09IER1cmF0aW9uOiAiIDw8IGR1cmF0aW9uIDw8ICIgeWVhcnMgPT09PT09PT09PSIgPDwgZW5kbDsKCiAgICAgICAgdmVjdG9yPFBvcnRmb2xpbz4gcG9ydGZvbGlvczsKICAgICAgICBnZW5lcmF0ZVBvcnRmb2xpb3MocG9ydGZvbGlvcywgUG9ydGZvbGlvKG51bUFzc2V0cywgMCksIDAsIDAsIG1heEFzc2V0cyk7CgogICAgICAgIHZlY3Rvcjx0dXBsZTxQb3J0Zm9saW8sIGRvdWJsZSwgZG91YmxlLCBkb3VibGUsIGRvdWJsZT4+IHJlc3VsdHM7CgogICAgICAgIGZvciAoY29uc3QgUG9ydGZvbGlvJiBwIDogcG9ydGZvbGlvcykgewogICAgICAgICAgICB2ZWN0b3I8ZG91YmxlPiBjYWdyTGlzdDsKICAgICAgICAgICAgZm9yIChpbnQgc3RhcnQgPSAwOyBzdGFydCA8PSBudW1ZZWFycyAtIGR1cmF0aW9uOyArK3N0YXJ0KSB7CiAgICAgICAgICAgICAgICB2ZWN0b3I8ZG91YmxlPiBzdWJSZXR1cm5zOwogICAgICAgICAgICAgICAgZm9yIChpbnQgaSA9IDA7IGkgPCBkdXJhdGlvbjsgKytpKSB7CiAgICAgICAgICAgICAgICAgICAgc3ViUmV0dXJucy5wdXNoX2JhY2socG9ydGZvbGlvUmV0dXJuKGFzc2V0cywgcCwgc3RhcnQgKyBpKSk7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICBkb3VibGUgY2FnciA9IGx1bXBTdW1DQUdSKHN1YlJldHVybnMpOwogICAgICAgICAgICAgICAgY2Fnckxpc3QucHVzaF9iYWNrKGNhZ3IpOwogICAgICAgICAgICB9CgogICAgICAgICAgICBkb3VibGUgcDE1ID0gcGVyY2VudGlsZTE1KGNhZ3JMaXN0KTsKICAgICAgICAgICAgZG91YmxlIHBtaW4gPSAqbWluX2VsZW1lbnQoY2Fnckxpc3QuYmVnaW4oKSwgY2Fnckxpc3QuZW5kKCkpOwogICAgICAgICAgICBkb3VibGUgcG1heCA9ICptYXhfZWxlbWVudChjYWdyTGlzdC5iZWdpbigpLCBjYWdyTGlzdC5lbmQoKSk7CiAgICAgICAgICAgIGRvdWJsZSBwbWVkID0gbWVkaWFuKGNhZ3JMaXN0KTsKCiAgICAgICAgICAgIHJlc3VsdHMuZW1wbGFjZV9iYWNrKHAsIHAxNSwgcG1pbiwgcG1heCwgcG1lZCk7CiAgICAgICAgfQoKICAgICAgICBzb3J0KHJlc3VsdHMuYmVnaW4oKSwgcmVzdWx0cy5lbmQoKSwgW10oY29uc3QgYXV0byYgYSwgY29uc3QgYXV0byYgYikgewogICAgICAgICAgICByZXR1cm4gZ2V0PDE+KGEpID4gZ2V0PDE+KGIpOwogICAgICAgIH0pOwoKICAgICAgICBjb3V0IDw8IGZpeGVkIDw8IHNldHByZWNpc2lvbigyKTsKCiAgICAgICAgLy8gU3VtbWFyeSB0YWJsZQogICAgICAgIGNvdXQgPDwgIlxuVG9wIDIgUG9ydGZvbGlvcyBTdW1tYXJ5IChCb3R0b20gMTV0aCBQZXJjZW50aWxlIENBR1IpOiIgPDwgZW5kbDsKICAgICAgICBjb3V0IDw8IGxlZnQgPDwgc2V0dygxMCkgPDwgIiMiIDw8IHNldHcoNDApIDw8ICJQb3J0Zm9saW8iIDw8IHNldHcoMTApIDw8ICJQMTUlIiA8PCBzZXR3KDEwKSA8PCAiTWluIiA8PCBzZXR3KDEwKSA8PCAiTWF4IiA8PCBzZXR3KDEwKSA8PCAiTWVkaWFuIiA8PCBlbmRsOwogICAgICAgIGNvdXQgPDwgc3RyaW5nKDgwLCAnLScpIDw8IGVuZGw7CgogICAgICAgIGZvciAoaW50IGkgPSAwOyBpIDwgMiAmJiBpIDwgcmVzdWx0cy5zaXplKCk7ICsraSkgewogICAgICAgICAgICBhdXRvIFt3ZWlnaHRzLCBwMTUsIHBtaW4sIHBtYXgsIHBtZWRdID0gcmVzdWx0c1tpXTsKICAgICAgICAgICAgc3RyaW5nIHBvcnRTdHI7CiAgICAgICAgICAgIGZvciAoaW50IGogPSAwOyBqIDwgbnVtQXNzZXRzOyArK2opIHsKICAgICAgICAgICAgICAgIGlmICh3ZWlnaHRzW2pdID4gMCkgewogICAgICAgICAgICAgICAgICAgIHBvcnRTdHIgKz0gYXNzZXROYW1lc1tqXSArICI6IiArIHRvX3N0cmluZyh3ZWlnaHRzW2pdKSArICIlICI7CiAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgIH0KICAgICAgICAgICAgY291dCA8PCBsZWZ0IDw8IHNldHcoMTApIDw8IChpICsgMSkgPDwgc2V0dyg0MCkgPDwgcG9ydFN0ciA8PCBzZXR3KDEwKSA8PCBwMTUgKiAxMDAgPDwgc2V0dygxMCkgPDwgcG1pbiAqIDEwMCA8PCBzZXR3KDEwKSA8PCBwbWF4ICogMTAwIDw8IHNldHcoMTApIDw8IHBtZWQgKiAxMDAgPDwgZW5kbDsKICAgICAgICB9CgogICAgICAgIC8vIC8vIERldGFpbGVkIGJyZWFrZG93bgogICAgICAgIC8vIGZvciAoaW50IGkgPSAwOyBpIDwgMiAmJiBpIDwgcmVzdWx0cy5zaXplKCk7ICsraSkgewogICAgICAgIC8vICAgICBhdXRvIFt3ZWlnaHRzLCBwMTUsIHBtaW4sIHBtYXgsIHBtZWRdID0gcmVzdWx0c1tpXTsKICAgICAgICAvLyAgICAgY291dCA8PCAiXG5Qb3J0Zm9saW8gIiA8PCAoaSArIDEpIDw8ICI6XG4iOwogICAgICAgIC8vICAgICBjb3V0IDw8IGxlZnQgPDwgc2V0dygxNSkgPDwgIkFzc2V0IiA8PCBzZXR3KDEwKSA8PCAiV2VpZ2h0XG4iOwogICAgICAgIC8vICAgICBjb3V0IDw8ICItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG4iOwogICAgICAgIC8vICAgICBmb3IgKGludCBqID0gMDsgaiA8IG51bUFzc2V0czsgKytqKSB7CiAgICAgICAgLy8gICAgICAgICBpZiAod2VpZ2h0c1tqXSA+IDApIHsKICAgICAgICAvLyAgICAgICAgICAgICBjb3V0IDw8IGxlZnQgPDwgc2V0dygxNSkgPDwgYXNzZXROYW1lc1tqXSA8PCBzZXR3KDEwKSA8PCB3ZWlnaHRzW2pdIDw8ICJcbiI7CiAgICAgICAgLy8gICAgICAgICB9CiAgICAgICAgLy8gICAgIH0KICAgICAgICAvLyAgICAgY291dCA8PCAiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIjsKICAgICAgICAvLyAgICAgY291dCA8PCAiQm90dG9tIDE1dGggJSBDQUdSIDogIiA8PCBwMTUgKiAxMDAgPDwgIiVcbiI7CiAgICAgICAgLy8gICAgIGNvdXQgPDwgIk1pbiBDQUdSICAgICAgICAgICA6ICIgPDwgcG1pbiAqIDEwMCA8PCAiJVxuIjsKICAgICAgICAvLyAgICAgY291dCA8PCAiTWF4IENBR1IgICAgICAgICAgIDogIiA8PCBwbWF4ICogMTAwIDw8ICIlXG4iOwogICAgICAgIC8vICAgICBjb3V0IDw8ICJNZWRpYW4gQ0FHUiAgICAgICAgOiAiIDw8IHBtZWQgKiAxMDAgPDwgIiVcbiI7CiAgICAgICAgLy8gfQogICAgfQoKICAgIHJldHVybiAwOwp9Cg==