// Crearea retelei
function network=NNcreate(N,K,M,activationFunction1,activationFunction2)
   network=tlist(["Retea feedforward","N","K","M","X0","Y0","X1","Y1","X2","Y2","W1","W2","f1","f2"],N,K,M,zeros(N+1,1),zeros(N+1,1),zeros(K,1),zeros(K+1,1),zeros(M,1),zeros(M,1),zeros(K,N+1),zeros(M,K+1),activationFunction1,activationFunction2);
endfunction

// construirea setului de antrenare
function tset=trainingSet(inputUnits)
   tset=tlist(["Training set","L","X","d"],0,zeros(1,100),zeros(1,100));
   // data input
   date1=csvRead("cursEuroZilnic.csv")'; 
   date2(1:length(date1))=date1(length(date1):-1:1); 
   tset.L=size(date2,2)-inputUnits;
   tset.X=zeros(inputUnits,tset.L);
   tset.d=zeros(1,tset.L);
   for i=1:tset.L
      tset.X(:,i)=date2(1,i:i+inputUnits-1)'; 
      tset.d(i)=date2(1,i+inputUnits);
   end    
endfunction   

// functie de activare:  logistica
function output=logistic(x)
   output=1/(1+exp(-x));
endfunction

// functie de activare:  liniara
function output=linear(x)
   output=x;
endfunction

// calcul semnal de iesire
function network=forward(network,X)
 network.X0=[-1;X]; network.Y0=network.X0;
 network.X1=network.W1*network.Y0;
 network.Y1=[-1; feval(network.X1,network.f1)]; 
 network.X2=network.W2*network.Y1;
 network.Y2=feval(network.X2,network.f2);
endfunction

// calcul eroare pentru setul de antrenare
function err=error_computation(network, tset)
err=0;
L=tset.L;
for i=1:L
     network=forward(network,tset.X(:,i));
     delta=(tset.d(i)-network.Y2).^2;
     err=err+sum(delta);
end
err=err/L;
endfunction

// antrenarea retelei
function network=BP(network,tset,pmax,Emax,eta)
L=tset.L;
// initializarea ponderilor cu valori aleatoare in [-1,1]
network.W1=2*rand(network.W1)-ones(network.W1);
network.W2=2*rand(network.W2)-ones(network.W2);
E=error_computation(network,tset); // calcul eroare
p=0;   // initializare contor epoca de antrenare
// ap=[]; aE=[];
while p<pmax & E>Emax
 // disp(p,"Epoca:");
  for l=1:L
     network=forward(network,tset.X(:,l)); 
     delta2=tset.d(l)-network.Y2;   // calcul eroare la nivel de iesire
     if (network.f2==logistic) delta2=delta2.*(network.Y2.*(ones(network.Y2)-network.Y2)); end;
     if (network.f2==tanh) delta2=delta2.*(ones(network.Y2)-network.Y2.^2); end;
     delta=network.W2'*delta2; // propagare eroare
     if (network.f1==logistic) delta=delta.*(network.Y1.*(ones(network.Y1)-network.Y1)); end;
     if (network.f1==tanh) delta=delta.*(ones(network.Y1)-network.Y1.^2); end;
     delta1=delta(2:length(delta)); // ignorarea componentei fictive
     network.W1=network.W1+eta*(delta1*network.Y0');
     network.W2=network.W2+eta*(delta2*network.Y1');
  end;
  E=error_computation(network,tset);
  if (modulo(p,10)==0)
      disp(E,"Eroare=",p,"p=");
  end    
//  ap=[ap p]; aE=[aE E]; plot(ap,aE);
  p=p+1;  // incrementarea contorului de epoca
 end 
endfunction

// network testing
function output=test(network,X)
  network=forward(network,X);
  output=network.Y2;
endfunction  

function tset=prediction(inputUnits,hiddenUnits,eta,epochs)
rn=NNcreate(inputUnits,hiddenUnits,1,logistic,linear); // crearea retelei
disp("dupa creare retea");
tset=trainingSet(inputUnits);           // construirea setului de antrenare
disp(tset);
rna=BP(rn,tset,epochs,0.0001,eta);  // antrenarea retelei
y=[];
for i=1:1:tset.L;
    rna=forward(rna,tset.X(:,i));
    y=[y rna.Y2];
//    disp(tset.X(:,i),"x=",tset.d(1,i),"d=",rna.Y2,"y=");
end
x=1:1:tset.L;
plot(x,tset.d,'b-',x,y,'r-');
endfunction

prediction(5,5,0.01,2000)
