%% Main script: Sweep different parameter pairs
% Default parameters
defaults.mu1     = 0.5;
defaults.mu2     = 1.0;
defaults.sigma1  = 0.1;
defaults.sigma2  = 0.1;
defaults.weight1 = 0.5;

% Parameter pair definitions
%{
param_pairs = {
    'mu1',     linspace(0.7, 1.0, 40), ...
    'mu2',     linspace(1.0, 1.3, 40);
    
    'mu1',     linspace(0.7, 1.0, 40), ...
    'weight1', linspace(0.2, 0.8, 40);
    
    'sigma1',  linspace(0.05, 0.2, 40), ...
    'sigma2',  linspace(0.05, 0.2, 40);
    
    'mu1',     linspace(0.7, 1.0, 40), ...
    'sigma1',  linspace(0.05, 0.2, 40);
    
    'mu2',     linspace(1.0, 1.3, 40), ...
    'weight1', linspace(0.2, 0.8, 40);
};
%}
param_pairs = {
    'mu1',     linspace(0.1, 1.5, 40), ...
    'mu2',     linspace(0.1, 1.5, 40);
};

% Cumulant index to visualize (2=variance, 3=skewness, 4=kurtosis)
cumulant_to_plot = 4;

% Run sweep for each pair
for i = 1:size(param_pairs,1)
    param1_name = param_pairs{i,1};
    param1_vals = param_pairs{i,2};
    param2_name = param_pairs{i,3};
    param2_vals = param_pairs{i,4};
    
    fprintf('Sweeping %s and %s...\n', param1_name, param2_name);
    
    Z = sweepBimodalCumulants(param1_name, param1_vals, ...
                          param2_name, param2_vals, ...
                          defaults, cumulant_to_plot);
end

%%
% Parameters
N_total = 10000;
mu1     = 0.5; 
sigma1  = 0.1;
mu2     = 1.0; 
sigma2  = 0.1;
weight1 = 0.7;

% Generate data
data    = generateBimodalDistribution(N_total, mu1, mu2, sigma1, sigma2, weight1);

% Plot histogram
figure(3);
clf
set(gcf, 'Color', 'w', 'Position', [100 100 950 750]);
histogram(data, 'Normalization', 'pdf', 'EdgeColor', 'none', 'FaceAlpha', 0.5);
hold on;

% Overlay smooth density estimate
[xi, f] = ksdensity(data);
plot(f, xi, 'r-', 'LineWidth', 2);

% Labels and title
xlabel('Value');
ylabel('Probability Density');
legend('Histogram', 'Smoothed Density');
grid on;

%%
N = size(Z, 1);

main_diag_values = diag(Z);
anti_diag_values = diag(flipud(Z));

param1_diag_main = param1_vals;
param2_diag_main = param2_vals;

param1_diag_anti = param1_vals;
param2_diag_anti = flip(param2_vals);

% For example, plot the main diagonal cumulants:
figure(4); 
set(gcf, 'Color', 'w', 'Position', [100 100 950 750]);
plot(1:N, main_diag_values, '-o');
xlabel('Index along diagonal');
ylabel('$\kappa_4$', 'Interpreter', 'latex');
title('$\kappa_4$ along anti-diagonal', 'Interpreter', 'latex');

% Plot anti-diagonal cumulants:
figure(5); 
set(gcf, 'Color', 'w', 'Position', [100 100 950 750]);
plot(1:N, anti_diag_values, '-o');
xlabel('Index along anti-diagonal');
ylabel('$\kappa_4$', 'Interpreter', 'latex');
title('$\kappa_4$ along anti-diagonal', 'Interpreter', 'latex');

%% === Helper: Bimodal Distribution ===
function data = generateBimodalDistribution(N_total, mu1, mu2, sigma1, sigma2, weight1)
%GENERATEBIMODALDISTRIBUTION Generates a single bimodal distribution.
%
%   data = generateBimodalDistribution(N_total, mu1, mu2, sigma1, sigma2, weight1)
%
%   Inputs:
%       N_total  - total number of samples
%       mu1, mu2 - means of the two modes
%       sigma1, sigma2 - standard deviations of the two modes
%       weight1  - fraction of samples from mode 1 (between 0 and 1)
%
%   Output:
%       data     - shuffled samples from the bimodal distribution

    % Validate weight
    weight1 = min(max(weight1, 0), 1);
    weight2 = 1 - weight1;

    % Determine number of samples for each mode
    N1 = round(N_total * weight1);
    N2 = N_total - N1;

    % Generate samples
    mode1_samples = normrnd(mu1, sigma1, [N1, 1]);
    mode2_samples = normrnd(mu2, sigma2, [N2, 1]);

    % Combine and shuffle
    data = [mode1_samples; mode2_samples];
    data = data(randperm(length(data)));
end

%% === Helper: Cumulant Calculation ===
function kappa = computeCumulants(data, max_order)
    data = data(:);
    mu   = mean(data);
    centered = data - mu;
    
    % Preallocate
    c = zeros(1, max_order);
    kappa = zeros(1, max_order);
    
    % Compute central moments up to max_order
    for n = 1:max_order
        c(n) = mean(centered.^n);
    end

    % Assign cumulants based on available order
    if max_order >= 1, kappa(1) = mu; end
    if max_order >= 2, kappa(2) = c(2); end
    if max_order >= 3, kappa(3) = c(3); end
    if max_order >= 4, kappa(4) = c(4) - 3*c(2)^2; end
    if max_order >= 5, kappa(5) = c(5) - 10*c(3)*c(2); end
    if max_order >= 6
        kappa(6) = c(6) - 15*c(4)*c(2) - 10*c(3)^2 + 30*c(2)^3;
    end
end

%% === Helper: Cumulant Calculation ===
function Z = sweepBimodalCumulants(param1_name, param1_vals, ...
                                   param2_name, param2_vals, ...
                                   fixed_params, ...
                                   cumulant_index)
%SWEEPBIMODALCUMULANTS Sweep 2 parameters and return a chosen cumulant.
%
%   Z = sweepBimodalCumulants(...)
%   Returns a matrix Z of cumulant values at each grid point.

    % Setup grid
    [P1, P2]  = meshgrid(param1_vals, param2_vals);
    Z         = zeros(size(P1));

    N_samples = 1000;
    maxOrder  = max(4, cumulant_index);

    for i = 1:numel(P1)
        % Copy fixed parameters
        params = fixed_params;

        % Override swept parameters
        params.(param1_name) = P1(i);
        params.(param2_name) = P2(i);

        % Generate and compute cumulants
        data  = generateBimodalDistribution(N_samples, ...
             params.mu1, params.mu2, ...
             params.sigma1, params.sigma2, ...
             params.weight1);

        kappa = computeCumulants(data, maxOrder);
        Z(i)  = kappa(cumulant_index);
    end

    % Plot full heatmap
    figure;
    set(gcf, 'Color', 'w', 'Position', [100 100 950 750]);
    imagesc(param1_vals, param2_vals, Z);
    set(gca, 'YDir', 'normal');
    xlabel(param1_name);
    ylabel(param2_name);
    title(['Cumulant \kappa_', num2str(cumulant_index)]);
    colorbar;
    axis tight;

    % Optional binary colormap (red = ≥0, blue = <0)
    figure;
    set(gcf, 'Color', 'w', 'Position', [100 100 950 750]);
    imagesc(param1_vals, param2_vals, Z);
    set(gca, 'YDir', 'normal');
    xlabel(param1_name);
    ylabel(param2_name);
    title(['Binary color split of \kappa_', num2str(cumulant_index)]);
    clim([-1 1]);
    colormap([0 0 1; 1 0 0]);  % Blue (neg), Red (pos & zero)
    colorbar;
    axis tight;
end
