classdef (Abstract) LinConstrWarmStart < matlab.mixin.CustomDisplay
%LINCONSTRWARMSTART A base class for a warm starting solvers that only deal
%with linear constraints.
% 
%   WS = LINCONSTRWARMSTART(X0,OPTIONS,MAXLINEAREQUALITIES,
%   MAXLINEARINEQUALITIES) sets internal properties specified in the
%   constructor.  It is recommended that the 'MaxLinearEqualities' and
%   'MaxLinearInequalities' are set if code generation is required.
%   
%   LinConstrWarmStart properties:
%     Read-only:
%      X                      - Initial point to solve the next problem in
%                               the sequence.
%      Options                - Algorithm-specific options for the solver.
%      MaxLinearEqualities    - Upper bound on the number of linear
%                               equality constraints over all quadratic
%                               programming problems solved using warm
%                               start.
%      MaxLinearInequalities  - Upper bound on the number of linear
%                               inequality constraints over all quadratic
%                               programming problems solved using warm
%                               start.
%
%
%   See also OPTIMWARMSTART, OPTIMOPTIONS
        
    %   Copyright 2020 The MathWorks, Inc.
    
    properties (SetAccess = protected, GetAccess = public)
        X
        Options
    end

    % Code generation options
    properties (Hidden, SetAccess = protected, GetAccess = public)
        MaxLinearEqualities
        MaxLinearInequalities
        Version
    end
    
    properties (Access = protected)
        SupportsCoder
    end

    properties (Abstract, Constant, Access = protected)
       SolverName
       NumSolveOutputs
    end

    methods (Abstract, Access = protected)
       % Implementation-specific warm start call.
       [out, varargout] = performWarmStart(obj, varargin);
    end

    methods
        function obj = LinConstrWarmStart(x0, options, MaxLinearEqualities, MaxLinearInequalities)

            % Set the read-only properties as needed.
            obj.X = x0(:);
            obj.Options = options;
            obj.MaxLinearEqualities = MaxLinearEqualities;
            obj.MaxLinearInequalities = MaxLinearInequalities;

            % Note that all coder products require MATLAB Coder to work.
            obj.SupportsCoder = license('test', 'matlab_coder');
        end
    end

    methods (Hidden)
        
        % If serialized and sent to another machine, we want to re-check
        % the license.
        function wsout = loadobj(obj)
            wsout = obj;
            wsout.SupportsCoder = license('test', 'matlab_coder');
        end

       % Disallow users to convert class into a struct.
        function struct(~)
            error(message('optimlib:warmstart:StructConversionUnsupported'));
        end

        % Calls the protected warm start routine of the subclass and
        % returns relevant data to the solver that is using the object.
        function [out, varargout] = solve(obj, mEq, mIneq, varargin)

            % Check whether or not code generation may have issues.
            if (mIneq > obj.MaxLinearInequalities && obj.SupportsCoder)
               warning(message('optimlib:warmstart:ExceededMaxLinIneq', obj.SolverName));
            end

            if (mEq > obj.MaxLinearEqualities && obj.SupportsCoder)
               warning(message('optimlib:warmstart:ExceededMaxLinEq', obj.SolverName));
            end

            varargout = cell(obj.NumSolveOutputs, 1);
            [out, varargout{:}] = performWarmStart(obj, varargin{:});
        end
    end
    
    
    methods (Access = protected)
        
        % Display the footer for users to view code generation 
        function algFooter = getFooter(obj)
            % Only display these properties if users can actually generate
            % code.  Otherwise, these are useless.
            if ~obj.SupportsCoder
                algFooter = "";
                return;
            end

            % We need to do '\n' vs newline here because newline breaks the
            % link in the first branch and the '\n' character is displayed
            % literally in the second branch.  Trying to do fancy stuff
            % like sprintf on line breaks doesn't work.
            indentPadding = blanks(4);

            if (feature('hotlinks') && ~isdeployed)
                showOptsStr = [indentPadding, '  MaxLinearEqualities: %i \\n',...
                               indentPadding, 'MaxLinearInequalities: %i \\n',...
                               '\\n'];
                showOptsStr = sprintf(showOptsStr, obj.MaxLinearEqualities, obj.MaxLinearInequalities);
                linkCmdStartTag = sprintf('<a href="matlab: fprintf(''%s'')">', showOptsStr);
                linkCmdEndTag = '</a>';
                algFooter = [indentPadding, linkCmdStartTag, 'Code generation limitations', linkCmdEndTag, newline];
            else
                showOptsStr = [indentPadding, '  MaxLinearEqualities: %i', newline,...
                               indentPadding, 'MaxLinearInequalities: %i', newline,...
                               newline];
                showOptsStr = sprintf(showOptsStr, obj.MaxLinearEqualities, obj.MaxLinearInequalities);
                algFooter = [indentPadding, 'Code generation limitations', newline, newline, showOptsStr, newline];
            end

        end
    end


end

