function varargout = pwq_yalmip(varargin)
%PWQ_YALMIP Defines a piecewise quadratic function using data from MPT
%
%Only intended for internal use in YALMIP
%
% Currently only a container for PWQ functions. Can not be
% used in actual optmization problem.

% Author Johan Löfberg
% $Id: pwq_yalmip.m,v 1.9 2008-01-15 14:19:01 joloef Exp $

switch class(varargin{1})

    case {'struct','cell'} % Should only be called internally

        if isa(varargin{2},'double')
            % Called from YALMIP to get double
            pwastruct = varargin{1};
            x = varargin{2};
            index = varargin{5};

            val = inf;
            for i = 1:length(pwastruct)
                [ii,jj] = isinside(pwastruct{i}.Pn,x);
                if ii
                    for k = 1:length(jj)
                        Q = pwastruct{i}.Ai{jj(k)};
                        if index>1 | min(size(pwastruct{i}.Bi{jj(k)}))>1
                            % FIX: Why?? Where is this feature used
                            val = min(val,x'*Q*x + reshape(pwastruct{i}.Bi{jj(k)}(index,:),1,[])*x+pwastruct{i}.Ci{jj(k)}(index));
                        else
                            val = min(val,x'*Q*x + reshape(pwastruct{i}.Bi{jj(k)},1,[])*x+pwastruct{i}.Ci{jj(k)});
                        end
                    end
                end
            end
            if isinf(val)
                val = nan;
            end
            varargout{1} = min(val);
            return
        end

        if nargin<3
            pwaclass = 'general'
        end

        if isa(varargin{1},'struct')
            varargin{1} = {varargin{1}};
        end

        % Put in standard format
        if ~isfield(varargin{1}{1},'Bi')
            if ~isfield(varargin{1}{1},'Fi')
                error('Wrong format on input to PWQ (requires Bi or Fi)');
            else
                for i = 1:length(varargin{1})
                    varargin{1}{1}.Ai = cell(1, varargin{1}{i}.Fi);
                    varargin{1}{i}.Bi = varargin{1}{i}.Fi
                    varargin{1}{i}.Ci = varargin{1}{i}.Gi
                end
            end
        end

        if ~isfield(varargin{1}{1},'Pfinal')
            error('Wrong format on input to PWQ (requires field Pn)');
        end

        % This will be a container for binary variables in the furture
        varargin{end+1} = binvar(length(varargin{1}{1}.Pn),1);

        % Create one variable for each row
        % Inefficient but the only way currently in YALMIP
        varargout{1} = [];
        varargin{end+1} = 1;

        for i = 1:length(varargin{1}{1}.Ci{1})
            varargin{end} = i;
            varargout{1} = [varargout{1};yalmip('define',mfilename,varargin{:})];
        end

    case 'char' % YALMIP sends 'model' when it wants the epigraph or hypograph


        switch varargin{1}

            case 'graph'

                varargout{1} = [];
                varargout{2} = struct('convexity','none','monotoncity','none','definiteness','none');
                varargout{3} = [];

            case {'integer','exact'}

                % FIX : Should create case for overlapping convex PWAs,
                % used in a nonconvex fashion...

                % Can only generate the first class of PWA functions
                t     = varargin{2};     % The YALMIP variables modelling this pwa
                pwq_struct = varargin{3};% MPT structure
                x     = varargin{4};     % Argument
                flag  = varargin{5};     % Type of PWA function
                d     = varargin{6};     % Binary for nonconvex cases
                index = varargin{7};     % Which row in Bix+Ci

                switch flag

                    case {'general','convex'}

                        if length(d)==1
                            % Don't introduce any binary variables when
                            % there is only one quadratic cost
                            F = set([]);
                            cost = x'*pwq_struct{1}.Ai{1}*x+pwq_struct{1}.Bi{1}*x+pwq_struct{1}.Ci{1};
                        else
                            n = length(x);
                            m = length(d);
                            z = sdpvar(n,m,'full');
                            F = set(sum(d) == 1);
                            cost = 0;
                            [Mm,mm] = derivebounds(x);
                            try
                                [aux,mx,Mx] = bounding_box(pwq_struct{1}.Pfinal);
                            catch
                                Mx = 10000*ones(length(x),1);
                                mx = -10000*ones(length(x),1);
                            end
                            Mx = min([Mx Mm],[],2);
                            mx = max([mx mm],[],2);
                            for i = 1:m
                                %    bounds(z(:,i),mx,Mx);
                                F = F + set(-(Mx-mx)*(1-d(i)) <= z(:,i)-x <= (Mx-mx)*(1-d(i)));
                                F = F + set(mx*d(i) <= z(:,i) <= Mx*d(i));
                                [H,K] = double(pwq_struct{1}.Pn(i));
                                [M,m]  = derivebounds(H*x-K);
                                F = F + set(H*x-K <= M*(1-d(i)));
                                cost = cost + z(:,i)'*pwq_struct{1}.Ai{i}*z(:,i)+pwq_struct{1}.Bi{i}(:)'*z(:,i)+d(i)*pwq_struct{1}.Ci{i};
                            end
                        end

                        varargout{1} = F;
                        varargout{2} = struct('convexity','none','monotoncity','none','definiteness','none','model','integer');
                        varargout{2}.replacer = cost;
                        varargout{3} = x;

                    case {'convexoverlapping'}

                        n = length(x);
                        cost = 0;
                        r = binvar(length(pwq_struct),1);
                        d = {};

                        % Derive bounds from polytopes
                        [aux,mx,Mx] = bounding_box(pwq_struct{1}.Pfinal);
                        for i = 2:length(pwq_struct)
                            [aux,L,U] = bounding_box(pwq_struct{i}.Pfinal);
                            Mx = max([Mx U],[],2);
                            mx = min([mx L],[],2);
                        end
                        bounds(x,mx,Mx);

                        % Some overlapping function is active
                        % (it will automatically be the smallest one)
                        F = set(sum(r) == 1);
                        for j = 1:length(pwq_struct)

                            if length(pwq_struct{j}.Pn) == 1
                                d{j} = r(j);
                            else
                                d{j} = binvar(length(pwq_struct{j}.Pn),1);
                                F = F + set(sum(d{j}) == r(j));
                            end
                            m = length(d{j});
                            z = sdpvar(n,m,'full');

                            for i = 1:m
                                bounds(z(:,i),mx,Mx);
                                F = F + set(-(Mx-mx)*(1-d{j}(i)) <= z(:,i)-x <= (Mx-mx)*(1-d{j}(i)));
                                F = F + set(mx*d{j}(i) <= z(:,i) <= Mx*d{j}(i));
                                [H,K] = double(pwq_struct{j}.Pn(i));
                                [M,m]  = derivebounds(H*x-K);
                                F = F + set(H*x-K <= M*(1-d{j}(i)));
                                cost = cost + z(:,i)'*pwq_struct{j}.Ai{i}*z(:,i)+pwq_struct{j}.Bi{i}*z(:,i)+d{j}(i)*pwq_struct{j}.Ci{i};
                            end
                        end

                        varargout{1} = F;
                        varargout{2} = struct('convexity','none','monotoncity','none','definiteness','none','model','integer');
                        varargout{2}.replacer = cost;
                        varargout{3} = x;

                    otherwise

                        varargout{1} = [];
                        varargout{2} = struct('convexity','convex','monotoncity','none','definiteness','none');
                        varargout{3} = [];
                        return
                end

            otherwise
                error('PWA_YALMIP called with CHAR argument?');
        end

    otherwise
        error('Strange type on first argument in PWQ_YALMIP');
end