%% Reset random stream for reproducibility.
rng(1,'twister');

%% Read data.

% % loss function with p-norm
% p = 2;
% 
% % Choose dataset and the desired overall return!
% dataName = 'SP500';     % SP500,   TSE,     NYSE,       DJIA,

datapath = ['./data/', dataName,'.txt'];
A = load(datapath);
A = A - 1;
[m,n] = size(A);
a = mean(A,1)'; % mean return
b = mean(A(:)); % target return (we choose as the mean of all)

if p == 2, Lips_f = max(eig(A'*A)); end % we use this only to compare against the standard constant step-size in the smooth setting

%% Prepare the output field
output.dataName = dataName;
output.p = p;
output.size = [m,n];

%% Solve with CVX
objcvx = @(x) (1/p)*sum(pow_abs(A*x - b,p));
cvx_begin
cvx_precision best
variable x_cvx(n,1)
minimize objcvx(x_cvx)
subject to;
a'*x_cvx >= b;
x_cvx >= 0;
sum(x_cvx) == 1;
cvx_end

output.CVX.optval = cvx_optval;
output.CVX.xcvx = x_cvx;

%% First-order oracles
ppnorm = @(x) (1/p)*sum(abs(x).^p); % ppnorm is (1/p)*norm(x,p)^p
subgradppnorm = @(x,p) evalsubgradppnorm(x,p);
obj = @(x) ppnorm(A*x - b);
subgrad = @(x) A'*subgradppnorm(A*x - b,p);
stochsubgrad = @(x) evalstochsubgrad(x,A,b,p);

%% Projection operators
proj1 = @(x,gamma) project_axb(a,b,x);
proj2 = @(x,gamma) project_simplex(x);

%% Settings
x0 = zeros(n,1);
gamma0 = 1;

%% Deterministic methods
maxit = 10;
[~,output.AdapTOS.info] = TOS(proj1,proj2,subgrad,obj,x0,maxit,gamma0,'adaptive');

%% Stochastic methods
epoch = 10;
output.StoAdapTOS.info = {};
for t = 1:20
    fprintf('Random trial %d:\n',t)
    [~,output.StoAdapTOS.info{t}] = TOS(proj1,proj2,stochsubgrad,obj,x0,epoch*m,gamma0,'adaptive');
end

%% Save results
dirName = ['./runs/portfolio_optimization/',dataName,'/'];
if ~exist(dirName,'dir'), mkdir(dirName); end
saveName = [dirName,'p=',num2str(p),'.mat'];
save(saveName,'output');

%% Functions
function x = project_simplex( y )

D = length(y);
x = sort(y,'descend');
cumsum_var = (cumsum(x)-1).*(1./(1:D)');
x = max(bsxfun(@minus,y,cumsum_var(sum(x>cumsum_var))),0);

end

function out = project_axb( a,b,x )
% projects onto <a,x> >= b

if a'*x >= b
    out = x;
else
    out = x - ((a'*x - b)./(a'*a))*a;
end

end

function u = evalstochsubgrad(x,A,b,p)

[m,n] = size(A);
ind = randi(m);
ai = A(ind,:);
u = m*ai'*evalsubgradppnorm(ai*x - b,p);

end

function u = evalsubgradppnorm(x,p)

y = x;
y(y==0) = 1;    % this is to prevent 1/0 in the below line
% if x_j = 0, then u_j = 0 is a subgrad
u = x.*abs(y).^(p-2);

end

