function [Problem] = logistic_regression(x_train, y_train, x_test, y_test, varargin)
 
% Inputs:
%       x_train     train data matrix of x of size dxn.
%       y_train     train data vector of y of size 1xn.
%       x_test      test data matrix of x of size dxn.
%       y_test      test data vector of y of size 1xn.
%       lambda      l2-regularized parameter. 
% Output:
%       Problem     problem instance. 
%
%
% The problem of interest is defined as
%
%           min f(w) = 1/n * sum_i^n f_i(w),           
%           where 
%           f_i(w) = log(1 + exp(-y_i' .* (w'*x_i))) + lambda/2 * w^2.


    if nargin < 5
        lambda = 0.1;
    else
        lambda = varargin{1};
    end

    d = size(x_train, 1);
    n_train = length(y_train);
    n_test = length(y_test);      
    
    Problem.name = @() 'logistic_regression';    
    Problem.dim = @() d;
    Problem.samples = @() n_train;
    Problem.lambda = @() lambda;
    Problem.classes = @() 2;  
    Problem.hessain_w_independent = @() false;
    Problem.x_norm = @() sum(x_train.^2,1);
    Problem.x = @() x_train;

    Problem.cost = @cost;
    function f = cost(w)
        f = -sum(log(sigmoid(y_train.*(w'*x_train))),2)/n_train + lambda * (w'*w) / 2;
    end

    Problem.cost_batch = @cost_batch;
    function f = cost_batch(w, indices)
        
        f = -sum(log(sigmoid(y_train(indices).*(w'*x_train(:,indices))))/n_train,2) + lambda * (w'*w) / 2;
        
    end

    Problem.grad = @grad;
    function g = grad(w, indices)
        e = exp(-1*y_train(indices)'.*(x_train(:,indices)'*w));
        s = e./(1+e);
        g = -(1/length(indices))*((s.*y_train(indices)')'*x_train(:,indices)')';
        g = full(g) + lambda * w;
        
    end

    Problem.full_grad = @full_grad;
    function g = full_grad(w)
        g = grad(w, 1:n_train);
    end

    Problem.ind_grad = @ind_grad;
    function g = ind_grad(w, indices)
         
        g = -ones(d,1) * y_train(indices).*x_train(:,indices) * diag(ones(1,length(indices))-sigmoid(y_train(indices).*(w'*x_train(:,indices))))+ lambda* repmat(w, [1 length(indices)]);
         
    end
    Problem.ind_grad2 = @ind_grad2;
    function gg = ind_grad2(w, indices)
        len=length(indices);
        gg=zeros(d,len);
        for i=1: len
            gg(:,i)=ind_grad(w, indices(i));
        end
    end
    Problem.grad3 = @grad3;
    function gg = grad3(w, indices)
        len=length(indices);
        gg=zeros(d,1);
        for i=1: len
            gg=gg+ind_grad(w(:,i), indices(i));
        end
        gg=gg./len;
    end
end

