#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 += 2) {//const int step = 10;
current[pos] = w;
generatePortfolios(portfolios, current, pos + 1, totalWeight + w, maxAssets);
}
}
int main() {
const int numAssets = 6;
const int maxAssets = 4;
// Asset names
vector<string> assetNames = {
"scv", "lcg", "em", "reit", "gld" , "eu ltb"
//"scv60Lcg15", "scv95", "vti", "20bal", "33gld", "lcbtiltled"
};
// Return data
vector<vector<double>> assets = {
{-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.09, 0.19, 0.18, -0.28, -0.40, 0.24, 0.08, -0.16, -0.02, 0.01,
0.16, -0.16, 0.20, 0.14, -0.01, 0.30, 0.14, 0.01, 0.07, 0.32,
-0.05, 0.37, 0.03, 0.00, 0.01, 0.34, 0.22, 0.32, 0.43, 0.29,
-0.28, -0.13, -0.24, 0.23, 0.04, 0.02, 0.07, 0.06, -0.39, 0.33,
0.15, -0.01, 0.15, 0.31, 0.13, 0.03, 0.05, 0.25, -0.04, 0.34,
0.39, 0.18, -0.35, 0.40, 0.31
},
{
-0.17, 0.28, 0.34, -0.18, -0.28, 0.25, 0.04, 0.01, 0.05, 0.06,
-0.04, -0.25, -0.31, 0.12, 0.12, 0.23, 0.11, 0.09, 0.34, 0.55,
-0.16, 0.54, 0.09, 0.68, -0.09, -0.06, 0.03, -0.11, -0.25, 0.59,
-0.31, -0.03, -0.08, 0.53, 0.22, 0.29, 0.29, 0.32, -0.52, 0.72,
0.18, -0.20, 0.17, -0.02, -0.02, -0.15, 0.10, 0.34, -0.16, 0.16,
0.16, -0.07, -0.24, 0.09, 0.05
},
{
-0.01, 0.10, 0.05, -0.23, -0.30, 0.13, 0.41, 0.14, 0.01, 0.20,
0.11, -0.02, 0.18, 0.26, 0.16, 0.16, 0.18, -0.08, 0.09, 0.04,
-0.20, 0.32, 0.12, 0.17, 0.00, 0.13, 0.30, 0.19, -0.18, -0.07,
0.21, 0.12, 0.01, 0.35, 0.27, 0.08, 0.31, -0.19, -0.37, 0.25,
0.26, 0.05, 0.16, 0.03, 0.28, 0.02, 0.07, 0.03, -0.07, 0.23,
-0.09, 0.33, -0.28, 0.10, 0.06
},
{
0.00, 0.13, 0.42, 0.55, 0.44, -0.26, -0.06, 0.15, 0.25, 1.00,
0.01, -0.36, 0.13, -0.17, -0.21, 0.04, 0.17, 0.18, -0.17, -0.06,
-0.09, -0.09, -0.07, 0.15, -0.05, 0.00, -0.06, -0.20, -0.03, -0.02,
-0.08, 0.00, 0.21, 0.19, 0.02, 0.13, 0.20, 0.25, 0.02, 0.22,
0.27, 0.05, 0.07, -0.25, 0.00, -0.12, 0.07, 0.10, -0.03, 0.16,
0.22, -0.09, -0.06, 0.11, 0.21
},
// {
// -0.02, 0.16, 0.04, -0.04, -0.10, 0.08, -0.05, 0.16, 0.15, -0.10,
// -0.16, -0.16, -0.23, 0.09, -0.02, 0.04, 0.43, 0.48, 0.17, -0.02,
// -0.02, 0.12, 0.14, -0.02, 0.24, -0.03, 0.24, 0.18, 0.02, 0.22,
// -0.24, 0.01, 0.00, 0.28, 0.24, 0.19, -0.04, 0.06, 0.03, 0.04,
// 0.05, -0.03, -0.01, 0.17, 0.05, 0.14, -0.08, 0.02, 0.10, -0.04,
// 0.11, 0.20, -0.18, -0.41, 0.11, -0.08
// }
// {-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.03,0.15,0.18,-0.07,-0.14,0.12,0.18,0.04,0.07,0.28,0.06,-0.13,0.17,0.13,0.01,0.22,0.18,0.02,0.07,0.14,-0.13,0.23,0.06,0.17,-0.02,0.16,0.12,0.12,0.04,0.07,-0.03,0.01,-0.02,0.29,0.13,0.08,0.17,0.03,-0.23,0.27,0.21,0.02,0.13,0.07,0.12,-0.03,0.09,0.13,-0.08,0.22,0.14,0.11,-0.23,0.16,0.13},
//{-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 = 2; duration <= 31; duration += 111) {
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+JiBwb3J0Zm9saW9zLCBQb3J0Zm9saW8gY3VycmVudCwgaW50IHBvcywgaW50IHRvdGFsV2VpZ2h0LCBpbnQgbWF4QXNzZXRzKSB7CiAgICBpZiAocG9zID09IGN1cnJlbnQuc2l6ZSgpKSB7CiAgICAgICAgaWYgKHRvdGFsV2VpZ2h0ID09IDEwMCkgewogICAgICAgICAgICBpbnQgbm9uWmVybyA9IGNvdW50X2lmKGN1cnJlbnQuYmVnaW4oKSwgY3VycmVudC5lbmQoKSwgW10oaW50IHcpIHsgcmV0dXJuIHcgPiAwOyB9KTsKICAgICAgICAgICAgaWYgKG5vblplcm8gPD0gbWF4QXNzZXRzKSB7CiAgICAgICAgICAgICAgICBwb3J0Zm9saW9zLnB1c2hfYmFjayhjdXJyZW50KTsKICAgICAgICAgICAgfQogICAgICAgIH0KICAgICAgICByZXR1cm47CiAgICB9CiAgICBmb3IgKGludCB3ID0gMDsgdyA8PSAxMDAgLSB0b3RhbFdlaWdodDsgdyArPSAyKSB7Ly9jb25zdCBpbnQgc3RlcCA9IDEwOwogICAgICAgIGN1cnJlbnRbcG9zXSA9IHc7CiAgICAgICAgZ2VuZXJhdGVQb3J0Zm9saW9zKHBvcnRmb2xpb3MsIGN1cnJlbnQsIHBvcyArIDEsIHRvdGFsV2VpZ2h0ICsgdywgbWF4QXNzZXRzKTsKICAgIH0KfQoKaW50IG1haW4oKSB7CiAgICBjb25zdCBpbnQgbnVtQXNzZXRzID0gNjsKICAgIGNvbnN0IGludCBtYXhBc3NldHMgPSA0OwogICAgCgogICAgLy8gQXNzZXQgbmFtZXMKICAgIHZlY3RvcjxzdHJpbmc+IGFzc2V0TmFtZXMgPSB7CiAgICAJInNjdiIsICJsY2ciLCAiZW0iLCAicmVpdCIsICJnbGQiICwgImV1IGx0YiIKICAgICAgICAvLyJzY3Y2MExjZzE1IiwgInNjdjk1IiwgInZ0aSIsICIyMGJhbCIsICIzM2dsZCIsICJsY2J0aWx0bGVkIgogICAgfTsKCiAgICAvLyBSZXR1cm4gZGF0YQogICAgdmVjdG9yPHZlY3Rvcjxkb3VibGU+PiBhc3NldHMgPSB7CiAgICAJCiAgICAJICAgICAgICAgey0wLjAzLDAuMTMsMC4wNiwtMC4yOSwtMC4zMSwwLjQzLDAuNDYsMC4wOCwwLjA3LDAuMjEsMC4xMiwwLjA2LDAuMzEsMC4zOCwwLjAyLDAuMzEsMC4xMiwtMC4wOCwwLjIzLDAuMTQsLTAuMjIsMC4zNywwLjI0LDAuMTksLTAuMDIsMC4yNCwwLjE3LDAuMzEsLTAuMDYsMC4wMiwwLjA0LDAuMTEsLTAuMTMsMC40MSwwLjE3LDAuMDQsMC4xNSwtMC4wOSwtMC4zMiwwLjMzLDAuMjMsLTAuMDYsMC4xOCwwLjMyLDAuMDksLTAuMDYsMC4yMywwLjA5LC0wLjE1LDAuMjAsMC4wMywwLjE5LC0wLjE2LDAuMTEsMC4wOH0sCgogICAgCQogICAgCXsKICAgIC0wLjA5LCAwLjE5LCAwLjE4LCAtMC4yOCwgLTAuNDAsIDAuMjQsIDAuMDgsIC0wLjE2LCAtMC4wMiwgMC4wMSwKICAgIDAuMTYsIC0wLjE2LCAwLjIwLCAwLjE0LCAtMC4wMSwgMC4zMCwgMC4xNCwgMC4wMSwgMC4wNywgMC4zMiwKICAgIC0wLjA1LCAwLjM3LCAwLjAzLCAwLjAwLCAwLjAxLCAwLjM0LCAwLjIyLCAwLjMyLCAwLjQzLCAwLjI5LAogICAgLTAuMjgsIC0wLjEzLCAtMC4yNCwgMC4yMywgMC4wNCwgMC4wMiwgMC4wNywgMC4wNiwgLTAuMzksIDAuMzMsCiAgICAwLjE1LCAtMC4wMSwgMC4xNSwgMC4zMSwgMC4xMywgMC4wMywgMC4wNSwgMC4yNSwgLTAuMDQsIDAuMzQsCiAgICAwLjM5LCAwLjE4LCAtMC4zNSwgMC40MCwgMC4zMQp9LAoKewogICAgLTAuMTcsIDAuMjgsIDAuMzQsIC0wLjE4LCAtMC4yOCwgMC4yNSwgMC4wNCwgMC4wMSwgMC4wNSwgMC4wNiwKICAgIC0wLjA0LCAtMC4yNSwgLTAuMzEsIDAuMTIsIDAuMTIsIDAuMjMsIDAuMTEsIDAuMDksIDAuMzQsIDAuNTUsCiAgICAtMC4xNiwgMC41NCwgMC4wOSwgMC42OCwgLTAuMDksIC0wLjA2LCAwLjAzLCAtMC4xMSwgLTAuMjUsIDAuNTksCiAgICAtMC4zMSwgLTAuMDMsIC0wLjA4LCAwLjUzLCAwLjIyLCAwLjI5LCAwLjI5LCAwLjMyLCAtMC41MiwgMC43MiwKICAgIDAuMTgsIC0wLjIwLCAwLjE3LCAtMC4wMiwgLTAuMDIsIC0wLjE1LCAwLjEwLCAwLjM0LCAtMC4xNiwgMC4xNiwKICAgIDAuMTYsIC0wLjA3LCAtMC4yNCwgMC4wOSwgMC4wNQp9LAoKewogICAgLTAuMDEsIDAuMTAsIDAuMDUsIC0wLjIzLCAtMC4zMCwgMC4xMywgMC40MSwgMC4xNCwgMC4wMSwgMC4yMCwKICAgIDAuMTEsIC0wLjAyLCAwLjE4LCAwLjI2LCAwLjE2LCAwLjE2LCAwLjE4LCAtMC4wOCwgMC4wOSwgMC4wNCwKICAgIC0wLjIwLCAwLjMyLCAwLjEyLCAwLjE3LCAwLjAwLCAwLjEzLCAwLjMwLCAwLjE5LCAtMC4xOCwgLTAuMDcsCiAgICAwLjIxLCAwLjEyLCAwLjAxLCAwLjM1LCAwLjI3LCAwLjA4LCAwLjMxLCAtMC4xOSwgLTAuMzcsIDAuMjUsCiAgICAwLjI2LCAwLjA1LCAwLjE2LCAwLjAzLCAwLjI4LCAwLjAyLCAwLjA3LCAwLjAzLCAtMC4wNywgMC4yMywKICAgIC0wLjA5LCAwLjMzLCAtMC4yOCwgMC4xMCwgMC4wNgp9LAoKewowLjAwLCAwLjEzLCAwLjQyLCAwLjU1LCAwLjQ0LCAtMC4yNiwgLTAuMDYsIDAuMTUsIDAuMjUsIDEuMDAsCjAuMDEsIC0wLjM2LCAwLjEzLCAtMC4xNywgLTAuMjEsIDAuMDQsIDAuMTcsIDAuMTgsIC0wLjE3LCAtMC4wNiwKLTAuMDksIC0wLjA5LCAtMC4wNywgMC4xNSwgLTAuMDUsIDAuMDAsIC0wLjA2LCAtMC4yMCwgLTAuMDMsIC0wLjAyLAotMC4wOCwgMC4wMCwgMC4yMSwgMC4xOSwgMC4wMiwgMC4xMywgMC4yMCwgMC4yNSwgMC4wMiwgMC4yMiwKMC4yNywgMC4wNSwgMC4wNywgLTAuMjUsIDAuMDAsIC0wLjEyLCAwLjA3LCAwLjEwLCAtMC4wMywgMC4xNiwKMC4yMiwgLTAuMDksIC0wLjA2LCAwLjExLCAwLjIxCn0sCgovLyB7Ci8vICAgICAtMC4wMiwgMC4xNiwgMC4wNCwgLTAuMDQsIC0wLjEwLCAwLjA4LCAtMC4wNSwgMC4xNiwgMC4xNSwgLTAuMTAsCi8vICAgICAtMC4xNiwgLTAuMTYsIC0wLjIzLCAwLjA5LCAtMC4wMiwgMC4wNCwgMC40MywgMC40OCwgMC4xNywgLTAuMDIsCi8vICAgICAtMC4wMiwgMC4xMiwgMC4xNCwgLTAuMDIsIDAuMjQsIC0wLjAzLCAwLjI0LCAwLjE4LCAwLjAyLCAwLjIyLAovLyAgICAgLTAuMjQsIDAuMDEsIDAuMDAsIDAuMjgsIDAuMjQsIDAuMTksIC0wLjA0LCAwLjA2LCAwLjAzLCAwLjA0LAovLyAgICAgMC4wNSwgLTAuMDMsIC0wLjAxLCAwLjE3LCAwLjA1LCAwLjE0LCAtMC4wOCwgMC4wMiwgMC4xMCwgLTAuMDQsCi8vICAgICAwLjExLCAwLjIwLCAtMC4xOCwgLTAuNDEsIDAuMTEsIC0wLjA4Ci8vIH0KCiAgICAJCi8vICAgICAgICAgey0wLjA0LDAuMTQsMC4xNSwtMC4xNCwtMC4xOSwwLjI2LDAuMjgsMC4wNCwwLjA4LDAuMjgsMC4wOCwtMC4wNiwwLjI0LDAuMjIsLTAuMDEsMC4yNiwwLjE0LC0wLjAyLDAuMTQsMC4xNiwtMC4xNiwwLjI5LDAuMTUsMC4xOCwtMC4wMywwLjIwLDAuMTMsMC4yMCwwLjAyLDAuMDgsLTAuMDQsMC4wNCwtMC4wNywwLjMzLDAuMTIsMC4wNiwwLjE1LDAuMDEsLTAuMjYsMC4zMSwwLjIyLC0wLjAzLDAuMTUsMC4xOSwwLjA4LC0wLjA2LDAuMTYsMC4xMywtMC4xMSwwLjIxLDAuMTMsMC4xMiwtMC4xOCwwLjE1LDAuMTJ9LAovLyAgICAgICAgIHstMC4wMywwLjEzLDAuMDYsLTAuMjksLTAuMzEsMC40MywwLjQ2LDAuMDgsMC4wNywwLjIxLDAuMTIsMC4wNiwwLjMxLDAuMzgsMC4wMiwwLjMxLDAuMTIsLTAuMDgsMC4yMywwLjE0LC0wLjIyLDAuMzcsMC4yNCwwLjE5LC0wLjAyLDAuMjQsMC4xNywwLjMxLC0wLjA2LDAuMDIsMC4wNCwwLjExLC0wLjEzLDAuNDEsMC4xNywwLjA0LDAuMTUsLTAuMDksLTAuMzIsMC4zMywwLjIzLC0wLjA2LDAuMTgsMC4zMiwwLjA5LC0wLjA2LDAuMjMsMC4wOSwtMC4xNSwwLjIwLDAuMDMsMC4xOSwtMC4xNiwwLjExLDAuMDh9LAovLyAgICAgICAgIHstMC4wMiwwLjExLDAuMTIsLTAuMjUsLTAuMzYsMC4zMCwwLjIxLC0wLjExLC0wLjAxLDAuMDUsMC4xNiwtMC4xMSwwLjE5LDAuMTksMC4wMSwwLjI4LDAuMTUsLTAuMDIsMC4xMiwwLjI0LC0wLjEwLDAuMjksMC4wNiwwLjA4LC0wLjAxLDAuMzQsMC4xOSwwLjMwLDAuMjQsMC4xOSwtMC4xMywtMC4wOSwtMC4yNSwwLjMwLDAuMDgsMC4wMiwwLjEyLDAuMDEsLTAuMzcsMC4yNiwwLjE2LC0wLjAyLDAuMTQsMC4zMSwwLjEyLDAuMDAsMC4xMSwwLjE5LC0wLjA3LDAuMjgsMC4xOSwwLjE4LC0wLjI0LDAuMjIsMC4yMH0sCiB7LTAuMDMsMC4xNSwwLjE4LC0wLjA3LC0wLjE0LDAuMTIsMC4xOCwwLjA0LDAuMDcsMC4yOCwwLjA2LC0wLjEzLDAuMTcsMC4xMywwLjAxLDAuMjIsMC4xOCwwLjAyLDAuMDcsMC4xNCwtMC4xMywwLjIzLDAuMDYsMC4xNywtMC4wMiwwLjE2LDAuMTIsMC4xMiwwLjA0LDAuMDcsLTAuMDMsMC4wMSwtMC4wMiwwLjI5LDAuMTMsMC4wOCwwLjE3LDAuMDMsLTAuMjMsMC4yNywwLjIxLDAuMDIsMC4xMywwLjA3LDAuMTIsLTAuMDMsMC4wOSwwLjEzLC0wLjA4LDAuMjIsMC4xNCwwLjExLC0wLjIzLDAuMTYsMC4xM30sICAgICAgICAKLy97LTAuMDEsMC4xMSwwLjE5LDAuMDMsLTAuMDUsMC4xMywwLjIzLDAuMDcsMC4xMCwwLjQ0LDAuMDgsLTAuMTMsMC4yMCwwLjE0LC0wLjA0LDAuMTksMC4xNiwwLjAyLDAuMDYsMC4wOCwtMC4xNSwwLjE3LDAuMDgsMC4xNiwtMC4wMywwLjE3LDAuMTEsMC4xMiwwLjAwLDAuMDIsLTAuMDEsMC4wMiwtMC4wMiwwLjMwLDAuMTEsMC4wNywwLjE5LDAuMDMsLTAuMjEsMC4yNSwwLjIzLDAuMDAsMC4xMywwLjA4LDAuMDksLTAuMDYsMC4xMywwLjEwLC0wLjA4LDAuMjAsMC4xMCwwLjExLC0wLjE0LDAuMTEsMC4xNH0sCi8vICAgICAgICAgey0wLjAzLDAuMTMsMC4xMiwtMC4yMiwtMC4zMCwwLjI1LDAuMjQsLTAuMDIsMC4wMCwwLjA3LDAuMDgsLTAuMDksMC4xNCwwLjIwLDAuMDcsMC4yNiwwLjE1LC0wLjAyLDAuMTYsMC4yNCwtMC4xMywwLjMzLDAuMTAsMC4yMSwtMC4wMywwLjIyLDAuMTYsMC4yMSwwLjA1LDAuMTUsLTAuMDUsLTAuMDIsLTAuMTIsMC4zMiwwLjE0LDAuMDgsMC4xNywwLjAyLC0wLjMyLDAuMjksMC4xNywtMC4wMSwwLjE0LDAuMTcsMC4xMywtMC4wMywwLjExLDAuMTYsLTAuMDksMC4yMywwLjExLDAuMTQsLTAuMjQsMC4xNCwwLjExfQovLyAgICAgCgogICAgCQogICAgfTsKCiAgICBpbnQgbnVtWWVhcnMgPSBhc3NldHNbMF0uc2l6ZSgpOwoKICAgIGZvciAoaW50IGR1cmF0aW9uID0gMjsgZHVyYXRpb24gPD0gMzE7IGR1cmF0aW9uICs9IDExMSkgewogICAgICAgIGNvdXQgPDwgIlxuPT09PT09PT09PSBEdXJhdGlvbjogIiA8PCBkdXJhdGlvbiA8PCAiIHllYXJzID09PT09PT09PT0iIDw8IGVuZGw7CgogICAgICAgIHZlY3RvcjxQb3J0Zm9saW8+IHBvcnRmb2xpb3M7CiAgICAgICAgZ2VuZXJhdGVQb3J0Zm9saW9zKHBvcnRmb2xpb3MsIFBvcnRmb2xpbyhudW1Bc3NldHMsIDApLCAwLCAwLCBtYXhBc3NldHMpOwoKICAgICAgICB2ZWN0b3I8dHVwbGU8UG9ydGZvbGlvLCBkb3VibGUsIGRvdWJsZSwgZG91YmxlLCBkb3VibGU+PiByZXN1bHRzOwoKICAgICAgICBmb3IgKGNvbnN0IFBvcnRmb2xpbyYgcCA6IHBvcnRmb2xpb3MpIHsKICAgICAgICAgICAgdmVjdG9yPGRvdWJsZT4gY2Fnckxpc3Q7CiAgICAgICAgICAgIGZvciAoaW50IHN0YXJ0ID0gMDsgc3RhcnQgPD0gbnVtWWVhcnMgLSBkdXJhdGlvbjsgKytzdGFydCkgewogICAgICAgICAgICAgICAgdmVjdG9yPGRvdWJsZT4gc3ViUmV0dXJuczsKICAgICAgICAgICAgICAgIGZvciAoaW50IGkgPSAwOyBpIDwgZHVyYXRpb247ICsraSkgewogICAgICAgICAgICAgICAgICAgIHN1YlJldHVybnMucHVzaF9iYWNrKHBvcnRmb2xpb1JldHVybihhc3NldHMsIHAsIHN0YXJ0ICsgaSkpOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgZG91YmxlIGNhZ3IgPSBsdW1wU3VtQ0FHUihzdWJSZXR1cm5zKTsKICAgICAgICAgICAgICAgIGNhZ3JMaXN0LnB1c2hfYmFjayhjYWdyKTsKICAgICAgICAgICAgfQoKICAgICAgICAgICAgZG91YmxlIHAxNSA9IHBlcmNlbnRpbGUxNShjYWdyTGlzdCk7CiAgICAgICAgICAgIGRvdWJsZSBwbWluID0gKm1pbl9lbGVtZW50KGNhZ3JMaXN0LmJlZ2luKCksIGNhZ3JMaXN0LmVuZCgpKTsKICAgICAgICAgICAgZG91YmxlIHBtYXggPSAqbWF4X2VsZW1lbnQoY2Fnckxpc3QuYmVnaW4oKSwgY2Fnckxpc3QuZW5kKCkpOwogICAgICAgICAgICBkb3VibGUgcG1lZCA9IG1lZGlhbihjYWdyTGlzdCk7CgogICAgICAgICAgICByZXN1bHRzLmVtcGxhY2VfYmFjayhwLCBwMTUsIHBtaW4sIHBtYXgsIHBtZWQpOwogICAgICAgIH0KCiAgICAgICAgc29ydChyZXN1bHRzLmJlZ2luKCksIHJlc3VsdHMuZW5kKCksIFtdKGNvbnN0IGF1dG8mIGEsIGNvbnN0IGF1dG8mIGIpIHsKICAgICAgICAgICAgcmV0dXJuIGdldDwxPihhKSA+IGdldDwxPihiKTsKICAgICAgICB9KTsKCiAgICAgICAgY291dCA8PCBmaXhlZCA8PCBzZXRwcmVjaXNpb24oMik7CgogICAgICAgIC8vIFN1bW1hcnkgdGFibGUKICAgICAgICBjb3V0IDw8ICJcblRvcCAyIFBvcnRmb2xpb3MgU3VtbWFyeSAoQm90dG9tIDE1dGggUGVyY2VudGlsZSBDQUdSKToiIDw8IGVuZGw7CiAgICAgICAgY291dCA8PCBsZWZ0IDw8IHNldHcoMTApIDw8ICIjIiA8PCBzZXR3KDQwKSA8PCAiUG9ydGZvbGlvIiA8PCBzZXR3KDEwKSA8PCAiUDE1JSIgPDwgc2V0dygxMCkgPDwgIk1pbiIgPDwgc2V0dygxMCkgPDwgIk1heCIgPDwgc2V0dygxMCkgPDwgIk1lZGlhbiIgPDwgZW5kbDsKICAgICAgICBjb3V0IDw8IHN0cmluZyg4MCwgJy0nKSA8PCBlbmRsOwoKICAgICAgICBmb3IgKGludCBpID0gMDsgaSA8IDIgJiYgaSA8IHJlc3VsdHMuc2l6ZSgpOyArK2kpIHsKICAgICAgICAgICAgYXV0byBbd2VpZ2h0cywgcDE1LCBwbWluLCBwbWF4LCBwbWVkXSA9IHJlc3VsdHNbaV07CiAgICAgICAgICAgIHN0cmluZyBwb3J0U3RyOwogICAgICAgICAgICBmb3IgKGludCBqID0gMDsgaiA8IG51bUFzc2V0czsgKytqKSB7CiAgICAgICAgICAgICAgICBpZiAod2VpZ2h0c1tqXSA+IDApIHsKICAgICAgICAgICAgICAgICAgICBwb3J0U3RyICs9IGFzc2V0TmFtZXNbal0gKyAiOiIgKyB0b19zdHJpbmcod2VpZ2h0c1tqXSkgKyAiJSAiOwogICAgICAgICAgICAgICAgfQogICAgICAgICAgICB9CiAgICAgICAgICAgIGNvdXQgPDwgbGVmdCA8PCBzZXR3KDEwKSA8PCAoaSArIDEpIDw8IHNldHcoNDApIDw8IHBvcnRTdHIgPDwgc2V0dygxMCkgPDwgcDE1ICogMTAwIDw8IHNldHcoMTApIDw8IHBtaW4gKiAxMDAgPDwgc2V0dygxMCkgPDwgcG1heCAqIDEwMCA8PCBzZXR3KDEwKSA8PCBwbWVkICogMTAwIDw8IGVuZGw7CiAgICAgICAgfQoKICAgICAgICAvLyAvLyBEZXRhaWxlZCBicmVha2Rvd24KICAgICAgICAvLyBmb3IgKGludCBpID0gMDsgaSA8IDIgJiYgaSA8IHJlc3VsdHMuc2l6ZSgpOyArK2kpIHsKICAgICAgICAvLyAgICAgYXV0byBbd2VpZ2h0cywgcDE1LCBwbWluLCBwbWF4LCBwbWVkXSA9IHJlc3VsdHNbaV07CiAgICAgICAgLy8gICAgIGNvdXQgPDwgIlxuUG9ydGZvbGlvICIgPDwgKGkgKyAxKSA8PCAiOlxuIjsKICAgICAgICAvLyAgICAgY291dCA8PCBsZWZ0IDw8IHNldHcoMTUpIDw8ICJBc3NldCIgPDwgc2V0dygxMCkgPDwgIldlaWdodFxuIjsKICAgICAgICAvLyAgICAgY291dCA8PCAiLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIjsKICAgICAgICAvLyAgICAgZm9yIChpbnQgaiA9IDA7IGogPCBudW1Bc3NldHM7ICsraikgewogICAgICAgIC8vICAgICAgICAgaWYgKHdlaWdodHNbal0gPiAwKSB7CiAgICAgICAgLy8gICAgICAgICAgICAgY291dCA8PCBsZWZ0IDw8IHNldHcoMTUpIDw8IGFzc2V0TmFtZXNbal0gPDwgc2V0dygxMCkgPDwgd2VpZ2h0c1tqXSA8PCAiXG4iOwogICAgICAgIC8vICAgICAgICAgfQogICAgICAgIC8vICAgICB9CiAgICAgICAgLy8gICAgIGNvdXQgPDwgIi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiI7CiAgICAgICAgLy8gICAgIGNvdXQgPDwgIkJvdHRvbSAxNXRoICUgQ0FHUiA6ICIgPDwgcDE1ICogMTAwIDw8ICIlXG4iOwogICAgICAgIC8vICAgICBjb3V0IDw8ICJNaW4gQ0FHUiAgICAgICAgICAgOiAiIDw8IHBtaW4gKiAxMDAgPDwgIiVcbiI7CiAgICAgICAgLy8gICAgIGNvdXQgPDwgIk1heCBDQUdSICAgICAgICAgICA6ICIgPDwgcG1heCAqIDEwMCA8PCAiJVxuIjsKICAgICAgICAvLyAgICAgY291dCA8PCAiTWVkaWFuIENBR1IgICAgICAgIDogIiA8PCBwbWVkICogMTAwIDw8ICIlXG4iOwogICAgICAgIC8vIH0KICAgIH0KCiAgICByZXR1cm4gMDsKfQo=