function output = call_cplexibm_miqp(interfacedata)

% Author Johan Löfberg

options = interfacedata.options;
model = yalmip2cplex(interfacedata);

if interfacedata.options.savedebug
    save cplexdebug model
end

solvertime = clock;
[x,fval,exitflag,output,lambda] = localSolverCall(model);
if output.cplexstatus == 4 | output.cplexstatus == 119
    % CPLEX reports infeasible OR unbounded
    % Remove objective and resolve
    model.H = model.H*0;
    model.f = model.f*0;
    [x,fval,exitflag,output,lambda] = localSolverCall(model);
    switch output.cplexstatus
        case {1,101,102} % It was ok, hence it must have been unbounded
            output.cplexstatus = 2;
        case {3,103,106} % Infeasible, so original was infeasible
            output.cplexstatus = 3; % Infeasible
        otherwise
            output.cplexstatus = 4; % I give up
    end
end
if interfacedata.getsolvertime solvertime = etime(clock,solvertime);else solvertime = 0;end

% Inconstency in early version of CPLEX
if str2num(interfacedata.solver.subversion)>=12.3
    the_sign = 1;
else
    the_sign = -1;
end
if ~isempty(lambda)
    D_struc = [the_sign*lambda.eqlin;the_sign*lambda.ineqlin];
else
    D_struc = [];    
end

if size(x,1) == length(model.f)
    if ~isempty(model.NegativeSemiVar)
        x(model.NegativeSemiVar,:) = -x(model.NegativeSemiVar,:);
    end
end

showprogress('Calling CPLEX-IBM',options.showprogress);
% Check, currently not exhaustive...
switch output.cplexstatus
    case {1,101,102}
        problem = 0;
    case {3,103,106}
        problem = 1; % Infeasible
    case {2,20,21,118}
        problem = 2; % Unbounded
    case {4,119}
        problem = 15;    
    case {10,11,104,105,107,108,111,112}
        problem = 3; % Iteration/time
    case 119
        problem = 15;
    case {5,6,109,110}
        problem = 4; % Numerics
    otherwise
        problem = -1;
end

infostr = yalmiperror(problem,'CPLEX-IBM');

% Save all data sent to solver?
if options.savesolverinput
    solverinput.model = model;
else
    solverinput = [];
end

% Save all data from the solver?
if options.savesolveroutput
    solveroutput.x = x;
    solveroutput.fval = fval;
    solveroutput.exitflag = exitflag;
    solveroutput.lambda = lambda;
    solveroutput.output = output;
else
    solveroutput = [];
end

if isempty(x)
    x = zeros(length(model.f),1);
end
% Standard interface
output.Primal      = x;
output.Dual        = D_struc;
output.Slack       = [];
output.problem     = problem;
output.infostr     = infostr;
output.solverinput = solverinput;
output.solveroutput= solveroutput;
output.solvertime  = solvertime;



function [x,fval,exitflag,output,lambda] = localSolverCall(model)

H = model.H;
f = model.f;
Aineq = model.Aineq;
bineq = model.bineq;
Aeq = model.Aeq;
beq = model.beq;
lb = model.lb;
ub = model.ub;
x0 = model.x0;
options.cplex = model.options;
options.verbose = model.verbose;
integer_variables = model.integer_variables;
binary_variables = model.binary_variables;
semicont_variables = model.semicont_variables;
K = model.K;
ctype = model.ctype;

fixedAineqBug = 0;
if isempty(Aineq) & isempty(Aeq)
    Aineq = zeros(1,length(f)) ;
    bineq = 1;
    fixedAineqBug = 1;
end
if isempty(integer_variables) & isempty(binary_variables) & isempty(semicont_variables) & isempty(K.sos.type)
    if options.verbose 
        if isempty(H)           
            [x,fval,exitflag,output,lambda] = cplexlp(f,Aineq,bineq,Aeq,beq,lb,ub,x0,options.cplex);
        else
            [x,fval,exitflag,output,lambda] = cplexqp(H,f,Aineq,bineq,Aeq,beq,lb,ub,x0,options.cplex);
        end
    else
        if isempty(H)
            evalc('[x,fval,exitflag,output,lambda] = cplexlp(f,Aineq,bineq,Aeq,beq,lb,ub,x0,options.cplex);');            
        else
            evalc('[x,fval,exitflag,output,lambda] = cplexqp(H,f,Aineq,bineq,Aeq,beq,lb,ub,x0,options.cplex);');
        end
    end
    if ~isempty(lambda) & fixedAineqBug
        lambda.ineqlin = [];
    end
else
    if options.verbose 
        if isempty(H)
            [x,fval,exitflag,output] = cplexmilp(f,Aineq,bineq,Aeq,beq,K.sos.type,K.sos.variables,K.sos.weight,lb,ub,ctype',x0,options.cplex);
        else
            [x,fval,exitflag,output] = cplexmiqp(H,f,Aineq,bineq,Aeq,beq,K.sos.type,K.sos.variables,K.sos.weight,lb,ub,ctype',x0,options.cplex);
        end
    else
        if isempty(H)
            evalc('[x,fval,exitflag,output] = cplexmilp(f,Aineq,bineq,Aeq,beq,K.sos.type,K.sos.variables,K.sos.weight,lb,ub,ctype'',x0,options.cplex);');            
        else
            evalc('[x,fval,exitflag,output] = cplexmiqp(H,f,Aineq,bineq,Aeq,beq,K.sos.type,K.sos.variables,K.sos.weight,lb,ub,ctype'',x0,options.cplex);');
        end
    end
    lambda = [];
end

function [x,fval,exitflag,output] = cplexmilpInternal(f,Aineq,bineq,Aeq,beq,sostype,sosvariables,sosweight,lb,ub,ctype,x0,options)

cplex = Cplex;
cplex.Model.sense = 'minimize';
cplex.Model.obj = f;
cplex.Model.lb = lb;
cplex.Model.ub = ub;
cplex.Model.ctype = ctype;
cplex.Model.A = [Aeq;Aineq];
cplex.Model.lhs = [beq;-inf(length(bineq),1)];
cplex.Model.rhs = [beq;bineq];
if options.mip.pool.intensity
    cplex.Param.mip.pool.intensity.Cur =  options.mip.pool.intensity;
    cplex.Param.mip.pool.capacity.Cur = options.mip.pool.capacity;
    cplex.Param.mip.pool.absgap.Cur = options.mip.pool.absgap;
    cplex.Param.mip.pool.relgap.Cur = options.mip.pool.relgap;
    cplex.Param.mip.pool.replace.Cur = options.mip.pool.replace;  
end
temp = cplex.solve();
output.cplexstatus = temp.status;
fval = cplex.Solution.objval;
x = cplex.Solution.x;
if options.mip.pool.intensity
    for i = 2:1:length(temp.pool.solution)
        x = [x temp.pool.solution(i).x];
    end            
end
exitflag = temp.statusstring;