function [input, msg] = verifybounds(input, options)
% Verify bounds and linear constraints for surrogateopt solver.

%   Copyright 2018-2021 The MathWorks, Inc.
nvar = numel(input.lb);
if ~isfield(input,'intcon')
    input.intcon = [];
end
if (~isfield(input, 'Aineq') || isempty(input.Aineq)) && ...
   (~isfield(input, 'bineq') || isempty(input.bineq))
    input.Aineq = zeros(0,nvar);
    input.bineq = zeros(0,1);
end
if (~isfield(input, 'Aeq') || isempty(input.Aeq)) && ...
   (~isfield(input, 'beq') || isempty(input.beq))
    input.Aeq = zeros(0,nvar);
    input.beq = zeros(0,1);
end

% Check for non-double inputs    
msg = isoptimargdbl('SURROGATEOPT', {'LB','UB','INTCON','Aineq','bineq','Aeq','beq'}, ... 
    input.lb,input.ub,input.intcon,input.Aineq,input.bineq,input.Aeq,input.beq);
if ~isempty(msg)
    error('globaloptim:surrogateopt:NonDoubleInput',msg);
end

% Turn into column vectors
lb = input.lb(:);
ub = input.ub(:);
intcon = input.intcon;
lenlb = length(lb);
lenub = length(ub);
msg = '';

if lenlb == 0
     error(message('globaloptim:surrogateopt:EmptyLB'))
end

if lenub == 0
    error(message('globaloptim:surrogateopt:EmptyUB'))
end


% Check length
if lenlb ~= lenub
    error(message('globaloptim:surrogateopt:LBAndUBNotEqualLength'))
end

if any(isinf(ub)) || any(isnan(ub))
    error(message('globaloptim:surrogateopt:UpperBoundNotFinite'))
end

if any(isinf(lb)) || any(isnan(lb))
    error(message('globaloptim:surrogateopt:LowerBoundNotFinite'))
end

if ~isreal(lb) || ~isreal(ub)
    error(message('globaloptim:surrogateopt:BoundsNotReal'))
end

% Check feasibility of bounds
infeas = (lb > ub);
if any(infeas)
    count = full(sum(infeas));
    msg = getString(message('globaloptim:surrogateopt:LbGreaterThanUb',count));
    return
end

if ~isempty(intcon)
    intcon = unique(intcon);
    if max(intcon) > lenlb || min(intcon) < 1
        error(message('globaloptim:surrogateopt:IntconRange', lenlb))
    end
    % Bound shifting for integer variables
    lb(intcon) = ceil(lb(intcon));
    ub(intcon) = floor(ub(intcon));
    
    % Check feasibility of bounds after bound shifting again
    infeas = (lb > ub);
    if any(infeas)
        msg = getString(message('globaloptim:surrogateopt:LbGreaterThanUbForIntegerVars'));
        return
    end
    
end
input.lb = lb;
input.ub = ub;
input.intcon = intcon;
input.NullBasisAeq = [];
input.X0 = [];
input.EmptyNullSpace = false;

if ~isempty(input.Aineq) || ~isempty(input.Aeq) || ...
   ~isempty(input.bineq) || ~isempty(input.beq)
    type = 'linearconstraints';
    tolCon = options.LinearConstraintTolerance;
    verbosity = options.Verbosity;
    nvars = length(lb);
    X = (lb + ub)/2;
    % Linear constraints correction
    [input.X0,input.Aineq, input.bineq, ...
        input.Aeq, input.beq, ~,~,msg] = ...
        globaloptim.internal.preProcessLinearConstr(X, input.Aineq, input.bineq, ...
        input.Aeq, input.beq, input.lb, input.ub, tolCon, nvars,type, verbosity);
    
    if ~isempty(msg)
 
        % infeasible due to linear constraints
        return;
    end
    input.X0 = input.X0(:)';
    
    % Find the null space of equality constraints.
    input.NullBasisAeq = ...
        globaloptim.patternsearch.eqconstrnullspace(input.Aeq, nvars);
    
    input.EmptyNullSpace = ~isempty(input.NullBasisAeq) && ...
            norm(input.NullBasisAeq, 'Inf') <= eps*norm(input.Aeq, 'Inf');

    if input.EmptyNullSpace
        % Empty Null space means the solution is unique.
        % Test for fixed, integer may make this problem infeasible.
        
        if ~isempty(intcon) && ...
                ~all(round(input.x0(intcon)) == input.x0(intcon))
            % Integer infeasible
            msg = getString(message('globaloptim:surrogateopt:InfeasibleInteger'));
        end
        
    end
    
end
