function output = callbaron(model)

% This sets up everything and more. Can be simplified significantly since
% baron handles its own computational tree etc
model = yalmip2nonlinearsolver(model);

% [Anonlinear*f(x) <= b;Anonlinear*f(x) == b]
cu = full([model.bnonlinineq;model.bnonlineq]);
cl = full([repmat(-inf,length(model.bnonlinineq),1);model.bnonlineq]);
Anonlinear = [ model.Anonlinineq; model.Anonlineq];

% Create string representing objective
obj = createmodelstring(model.c,model);
obj = strrep(obj,'sqrtm_internal','sqrt');
if nnz(model.Q)>0
    obj = [obj '+' createQstring(model.Q,model)];
end
if model.f > 0
    obj = [obj '+' num2str(model.f)];
end
% Append quadratic term
obj = ['@(x) ' obj];
obj = eval(obj);

% Create string representing nonlinear constraints
if length(cu)>0
    con = '[';
    remove = [];
    for i = 1:length(cu)
        if isinf(cl(i)) & isinf(cu(i))
            remove = [remove;i];
        else
            con = [con createmodelstring(Anonlinear(i,:),model) ';'];
        end
    end
    cl(remove) = [];
    cu(remove) = [];
    con = [con ']'];
    con = strrep(con,'sqrtm_internal','sqrt');
    con = ['@(x) ' con];
    con = eval(con);
else
    cu = [];
    cl = [];
    con = [];
end

% Linear constraints
ru = full([model.b;model.beq]);
rl = full([repmat(-inf,length(model.b),1);model.beq]);
A =  [model.A; model.Aeq];
if length(ru)>0
    remove = find(isinf(rl) & isinf(ru));
    A(remove,:)=[];
    rl(remove) = [];
    ru(remove) = [];
end

lb = model.lb;
ub = model.ub;

xtype = [];
xtype = repmat('C',length(lb),1);
xtype(model.binary_variables) = 'B';
xtype(model.integer_variables) = 'I';
x0 = model.x0;
opts = model.options.baron;
opts.prlevel = model.options.verbose;
if model.options.savedebug    
    save barondebug obj con A ru rl cl cu lb ub x0 opts
end

solvertime = clock;
[x,fval,exitflag,info,allsol] = baron(obj,A,rl,ru,lb,ub,con,cl,cu,xtype,x0,opts);
solvertime = etime(clock,solvertime);

% Check, currently not exhaustive...
switch exitflag
    case 1
        problem = 0;
    case 2
        problem = 1;
    case 3
        problem = 2;
    case {4,5}
        problem = 11;
    otherwise
        problem = 9;
end

% Save all data sent to solver?
if model.options.savesolverinput
    solverinput.obj = obj;
    solverinput.A = A;
    solverinput.rl = rl;
    solverinput.ru = ru;
    solverinput.lb = lb;
    solverinput.ub = ub;
    solverinput.con = con;
    solverinput.cl = cl;
    solverinput.cu = cu;
    solverinput.xtype = xtype;
    solverinput.opts = opts;
else
    solverinput = [];
end

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

if ~isempty(x)
    x = RecoverNonlinearSolverSolution(model,x);
end

% Standard interface
output = createoutput(x,[],[],problem,'BARON',solverinput,solveroutput,solvertime);



function z = createOneterm(i,model)

[evalPos,pos] = ismember(i,model.evalVariables);
if pos
    % This is a nonlinear operator
    map = model.evalMap{pos};
    % We might hqave vectorized things to compute several expressions at the same time
    j = find(map.computes == i);
    j = map.variableIndex(j);
    % we have f(x(j)), but have to map back to linear indicies
    jl = find(model.linearindicies == j);
    if isempty(jl)
        z =  [map.fcn '(' createmonomstring(model.monomtable(j,:),model)  ')'];
    else
        z =  [map.fcn '(x(' num2str(jl) '))'];
    end
else
    i = find(model.linearindicies == i);
    z =  ['x(' num2str(i) ')'];
end

function monomstring = createmonomstring(v,model)

monomstring = '';
for i = find(v)
    z = createOneterm(i,model);
    if v(i)==1
        monomstring = [monomstring z '*'];
    else
        monomstring = [monomstring z '^' num2str(v(i),12) '*'];
    end
end
monomstring = monomstring(1:end-1);


function string = createmodelstring(row,model)
string = '';
index = find(row);
for i = index(:)'
    monomstring = createmonomstring(model.monomtable(i,:),model);
    string = [string  num2str(row(i),12) '*' monomstring '+'];
end
string = string(1:end-1);
string = strrep(string,'+-','-');
string = strrep(string,')-1*',')-');
string = strrep(string,')+1*',')+');

function string = createQstring(Q,model)
% Special code for the quadratic term
string = '';
[ii,jj,c]= find(triu(Q));
for i = 1:length(ii)
    if ii(i)==jj(i)
        % Quadratic term
        monomstring = createmonomstring(sparse(1,ii(i),1,1,size(Q,1)),model);       
        string = [string  num2str(c(i),12) '*' monomstring '^2' '+'];
    else
        % Bilinear term
        monomstring1 = createmonomstring(sparse(1,ii(i),1,1,size(Q,1)),model);
        monomstring2 = createmonomstring(sparse(1,jj(i),1,1,size(Q,1)),model);
        string = [string  num2str(c(i),12) '*2*' monomstring1 '*' monomstring2 '+'];
    end
end
string = string(1:end-1);
string = strrep(string,'+-','-');
string = strrep(string,'-1*','-');
string = strrep(string,'+1*','+');


