#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 += 1) {//const int step = 10;
        current[pos] = w;
        if(w > 68) return;
        // if(pos==2 && w > 40) return;
         if(pos == 3 && w> 6) return;
        // if(pos ==5 && w>40) return;
        if((pos != 0)&& w> 22 )return;
        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" , "20bal"
        //"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 = 15; 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;
}
