function om = add_constraints(om, name, AorN, l, u, varsets)
%ADD_CONSTRAINTS  Adds a set of constraints to the model
%   om = add_constraints(om, name, N, 'non-linear');
%   om = add_constraints(om, name, A, l, u);
%   om = add_constraints(om, name, A, l, u, varsets);
%
%   e.g.
%     om = add_constraints(om, 'Pmis', nb, 'non-linear');
%     om = add_constraints(om, 'vl', Avl, lvl, uvl, {'Pg', 'Qg'});
%
%   For non-linear constraints, the 3rd argument, N, is the number
%   of constraints in the set.
%
%   Linear constraints are of the form l <= A * x <= u, where
%   x is a vector made of of the vars specified in varsets (in
%   the order given). This allows the A matrix to be defined only
%   in terms of the relevant variables without the need to manually
%   create a lot of zero columns. If varsests is empty, x is taken
%   to be the full vector of all optimization variables. If l or 
%   u are empty, they are assumed to be appropriately sized vectors
%   of -Inf and Inf, respectively.

%   MATPOWER
%   $Id: add_constraints.m,v 1.3 2009/11/04 15:58:44 ray Exp $
%   by Ray Zimmerman, PSERC Cornell
%   Copyright (c) 2008 by Power System Engineering Research Center (PSERC)
%   See http://www.pserc.cornell.edu/matpower/ for more info.

if nargin < 5       %% non-linear
    %% prevent duplicate named constraint sets
    if isfield(om.nln.idx.N, name)
        error('@opf_model/add_constraints: non-linear constraint set named ''%s'' already exists', name);
    end

    %% add info about this non-linear constraint set
    om.nln.idx.i1.(name) = om.nln.N + 1;    %% starting index
    om.nln.idx.iN.(name) = om.nln.N + AorN; %% ending index
    om.nln.idx.N.(name)  = AorN;            %% number of constraints
    
    %% update number of non-linear constraints and constraint sets
    om.nln.N  = om.nln.idx.iN.(name);
    om.nln.NS = om.nln.NS + 1;

    %% put name in ordered list of constraint sets
    om.nln.order{om.nln.NS} = name;
else                %% linear
    %% prevent duplicate named constraint sets
    if isfield(om.lin.idx.N, name)
        error('@opf_model/add_constraints: linear constraint set named ''%s'' already exists', name);
    end

    if nargin < 6
        varsets = {};
    end
    [N, M] = size(AorN);
    if isempty(l)                   %% default l is -Inf
        l = -Inf * ones(N, 1);
    end
    if isempty(u)                   %% default u is Inf
        u = Inf * ones(N, 1);
    end
    if isempty(varsets)
        varsets = om.var.order;
    end

    %% check sizes
    if size(l, 1) ~= N || size(u, 1) ~= N
        error('@opf_model/add_constraints: sizes of A, l and u must match');
    end
    nv = 0;
    for k = 1:length(varsets)
        nv = nv + om.var.idx.N.(varsets{k});
    end
    if M ~= nv
        error('@opf_model/add_constraints: number of columns of A does not match\nnumber of variables, A is %d x %d, nv = %d\n', N, M, nv);
    end

    %% add info about this linear constraint set
    om.lin.idx.i1.(name)  = om.lin.N + 1;   %% starting index
    om.lin.idx.iN.(name)  = om.lin.N + N;   %% ending index
    om.lin.idx.N.(name)   = N;              %% number of constraints
    om.lin.data.A.(name)  = AorN;
    om.lin.data.l.(name)  = l;
    om.lin.data.u.(name)  = u;
    om.lin.data.vs.(name) = varsets;
    
    %% update number of vars and var sets
    om.lin.N  = om.lin.idx.iN.(name);
    om.lin.NS = om.lin.NS + 1;
    
    %% put name in ordered list of var sets
    om.lin.order{om.lin.NS} = name;
end
