classdef outrata_1990_ex2
    %{
        J.V. Outrata
        On the numerical solution of a class of Stackelberg problems
        Zeitschrift für Operations Research
        Volume 34, pages 255–277, (1990)
    %}
    properties(Constant)
        name = 'outrata_1990_ex2';
        category = 'textbook';
        subcategory = '';
        datasets = {
            'outrata_1990_ex2a.json';
            'outrata_1990_ex2b.json';
            'outrata_1990_ex2c.json';
            'outrata_1990_ex2d.json';
            'outrata_1990_ex2e.json';
            'outrata_1990_ex2f.json';
        };
        paths = fullfile('bolib3', 'data', 'nonlinear', outrata_1990_ex2.datasets);
    end


    methods(Static)

        % Upper-level objective function (quadratic)
        % minimise 1/2 ((y1 - 3)^2 + (y2 - 4)^2)
        function val = F(~, y, ~)
            val = 0.5*((y(1) - 3)^2 + (y(2) - 4)^2);
        end
        
        % Upper-level inequality constraints (bounds)
        % x >= 0
        function val = G(x, ~, ~)
            val = x(:);
        end
        
        % Upper-level equality constraints (none)
        function val = H(~, ~, ~)
            val = [];
        end
        
        % Lower-level objective function (nonconvex)
        function val = f(x, y, data)
            H_matrix = data.H_matrix + x*data.H_matrix_x_coefficients;
            val = 0.5*(y'*H_matrix*y) - (3 + 1.333*x)*y(1) - x*y(2);
        end
        
        % Lower-level inequality constraints (quadratic)
        % + (0.333 - 0.1x)y1 - y2 + x >= 0,
        % - y1 + (0.333 + 0.1x)y2 + 2 >= 0,
        % y1                          >= 0,
        % y2                          >= 0.
        function val = g(x, y, data)
            y_coef = [
                [0.333, -1];
                [-1, 0.333]
            ];
            val = [
                y_coef*y + x*data.ineq_xy_coefficients*y + x*data.ineq_x_coefficients + data.ineq_constant;
                y
            ];
        end
        
        % Lower-level equality constraints (none)
        function val = h(~, ~, ~)
            val = [];
        end

        % This bilevel program must be parameterized by data.
        function data = read_data(filepath)
            % Read JSON file
            fid = fopen(filepath, 'r');
            raw = fread(fid, inf, 'uint8=>char')';
            fclose(fid);
            data = jsondecode(raw);

            % Define required keys and expected shapes
            required_fields = {
                'H_matrix', [2, 2];
                'H_matrix_x_coefficients', [2, 2];
                'ineq_xy_coefficients', [2, 2];
                'ineq_x_coefficients', [2, 1];
                'ineq_constant', [2, 1];
            };

            % Validate and convert each field
            for i = 1:size(required_fields, 1)
                key = required_fields{i, 1};
                expected_shape = required_fields{i, 2};

                if ~isfield(data, key)
                    error('Missing required field "%s" in "%s".', key, filepath);
                end

                % Validate numeric
                if ~isnumeric(data.(key))
                    error('Field "%s" should be numeric.', key);
                end

                % Validate dimentions
                if ~isequal(size(data.(key)), expected_shape)
                    error('Field "%s" should have dimensions [%d %d].', ...
                          key, expected_shape(1), expected_shape(2));
                end
            end
        end

        % Key are the function/variable names
        % Values are their dimention
        function n = dimention(key, data)
            n = dictionary( ...
                'x', 1, ...
                'y', 2, ...
                'F', 1, ...
                'G', 1, ...
                'H', 0, ...
                'f', 1, ...
                'g', 4, ...
                'h', 0 ...
            );
            if isKey(n,key)
                n = n(key);
            end
        end
    end
end