//
// @(#) mail.r
//
// ([{
//

// necessarily global parameters:
//   fircoeff
//   nfir
//   nsaved
//   sigsave
//   firout
//   desired
//   iter

// initialize model filter
initfir = function () 
{
  global(niter, fircoeff, nfir, nsaved, sigsave, firout, ...
         desired, iter, ninput, input, weight1, theta1, ...
         weight2, theta2, ndf, nhidden, mu, esquare, output, ...
         nwsave, wsave, skiplimit)

  // model filter parameters
  // fircoeff = [0.2;-0.4;1;-0.6;0.4];	// model filter coefficients
  fircoeff = [1];
  nfir = size(fircoeff)[1];
  nsaved = 20;			        // signal delay line length, must
  // be >= nfir
  rand("default");
  sigsave = rand(nsaved, 1);		// initialize
  
}

// model filter time step
stepfir = function () 
{
  global(niter, fircoeff, nfir, nsaved, sigsave, firout, ...
         desired, iter, ninput, input, weight1, theta1, ...
         weight2, theta2, ndf, nhidden, mu, esquare, output, ...
         nwsave, wsave, skiplimit)
  
  // excitation signal generation
  rand("default");
  //	signal = (rand() > 0.5) * 0.8 + 0.1; // exp
  //	signal = (rand() > 0.5) * 1.8 - 0.9; // tanh
  signal = (rand() > 0.5) * 1.8 - 0.9; // simple
  
  // filter update
  sigsave = [signal; sigsave[1:(nsaved - 1)]];
  firout = sigsave[1:nfir]' * fircoeff;
  
  // nonlinearity
  // firout = tanh(3 * firout);
  // firout = 2 * sin(3 * firout) + firout;
  
  // noise
  rand("normal", 0, 0.1);
  firout = firout + rand();
  
  // impulse noise
  rand("default");
  if (rand() > 0.7) 
  {
    firout = firout + 5;
  }
  
  // what we would like to adapt to
  // desired = firout;
  desired = sigsave[3;];
  
}

bp2init = function () 
{
  global(niter, fircoeff, nfir, nsaved, sigsave, firout, ...
         desired, iter, ninput, input, weight1, theta1, ...
         weight2, theta2, ndf, nhidden, mu, esquare, output, ...
         nwsave, wsave, skiplimit)

  // backprop parameters
  ninput = 10;			// input vector length
  input = zeros(ninput, 1);	// initialize
  ndf = 0;			// number of feedback taps
  nhidden = 10;
  rand("default");
  weight1 = (2 * rand(nhidden, ninput ) - 1) * 0.1;
  theta1  = (2 * rand(nhidden, 1      ) - 1) * 0.1;
  weight2 = (2 * rand(1      , nhidden) - 1) * 0.1;
  theta2  = (2 * rand(1      , 1      ) - 1) * 0.1;
  mu = 1.0;

  // miscellanea
  niter = 20000;
  skiplimit = 0;
  nwsave = niter - skiplimit;
  wsave = zeros(nwsave, 5);
  esquare = 0;
  output = 0;		// so that we can shift the first value into input[]
  
  initfir();
  
}

bp2steps = function (fromiter, toiter, skiplimit) 
{
  global(niter, fircoeff, nfir, nsaved, sigsave, firout, ...
         desired, iter, ninput, input, weight1, theta1, ...
         weight2, theta2, ndf, nhidden, mu, esquare, output, ...
         nwsave, wsave, skiplimit)

  for (iter in fromiter:toiter) 
  {
    
    mu = 0.01 * (niter - iter + 1) / niter;
    
    stepfir();
    
    // reconstrunction filter data generation
    input = [firout; input[1:(ninput - 1)]];
    
    // forward pass
    s1 = weight1 * input + theta1;
    hidden = s1 ./ (1 + abs(s1)); // simple
    s2 = weight2 * hidden + theta2;
    output = s2 ./ (1 + abs(s2)); // simple
    
    // error vectors
    e2 = (mu * (1 ./ ((1 + abs(s2)) .* (1 + abs(s2))) + 0.05)) ...
          .* (desired - output); // simple
    e1 = (1 ./ ((1 + abs(s1)) .* (1 + abs(s1))) + 0.05) ...
          .* (weight2' * e2); // simple

    // weight update
    weight1 = weight1 + e1 * input';
    weight2 = weight2 + e2 * hidden';
    
    // threshold update
    theta1 = theta1 + e1;
    theta2 = theta2 + e2;
    
    err = desired - output;
    esquare = 0.995 * esquare + 0.005 * (err' * err);
    if (iter > skiplimit) 
    {
      wsave[(iter - skiplimit);] = [desired, firout, output, err, esquare];
    }
    
  }
  
}

bp2 = function () 
{
  global(niter, fircoeff, nfir, nsaved, sigsave, firout, ...
         desired, iter, ninput, input, weight1, theta1, ...
         weight2, theta2, ndf, nhidden, mu, esquare, output, ...
         nwsave, wsave, skiplimit)

  iter = 1;
  toiter = 0;
  while (iter < niter) 
  {
    fromiter = toiter + 1;
    toiter = fromiter + 999;
    if (toiter > niter) 
    {
      toiter = niter;
    }
    
    bp2steps(fromiter, toiter, skiplimit);
    
    printf("Iteration %d: esquare = %g,\n", iter, esquare);
    printf("   weight1^2 = %g, theta1^2 = %g,\n", ...
               trace(weight1 * weight1'), theta1' * theta1);
    printf("   weight2^2 = %g, theta2^2 = %g\n", ...
               weight2 * weight2', theta2' * theta2);
  }
  
  plotdata = [((skiplimit + 1):niter)', wsave];
  
  plgrid();
  pltitle ( "RLaB Neural Net Example (contributed)" );
  xlabel ( "" );
  ylabel ( "" );
  plot(plotdata[nwsave-100:nwsave;]);
  
}

