'/
SET OF SPECIFIC FUNCTIONS DEPENDING ON THE Model.Type of ANnet FUNCTION

This script contains a set of functions to be used by an ANnet function
  depending on the specific Model.Type

Specifications:
fTimeDelayData --> Function: returns the delayed input dataset
fCloseLoopData --> Function: returns the input data for a close-loop net
Input     --> Original input dataset, with no delay
Target    --> Original tarhet dataset (optional)
ModelType --> Type of ANnet model to be considered
TimeDelay --> Number of time step delays

Author: Arthur Hrast Essenfelder
E-mail: arthur.essenfelder@unive.it

Version: 1.0
Last update on: 04/12/2015
/'

#TimeDelayData Function ================================================
fTimeDelayData = function(Input, Target, ModelType, TimeDelay) {
  
  # Loading data -------------------------------------------------------
  X = Input;
  d = TimeDelay;
  i = ncol(X);
  k = nrow(X);
  
  # Loading Target data if necessary
  if (ModelType == "NAR" || ModelType == "NARX") { 
    if (missing(Target)) { stop("Missing Target"); }
    else { y = Target; j = ncol(y); }
  }
  # --------------------------------------------------------------------
  
  
  # Check if there is a delay to be applied, and if so, apply it -------
  if (d > 0) {
    if (ModelType == "Delayed Input-Output") { # ----------
      Xtmp = matrix(0., nrow=k-d, ncol=(d+1)*i);
      count = 1
      for (ii in 1:i) { #Data from original Input
        for (dd in d:0) {
          Xtmp[,count] = X[(dd+1):(k+dd-d),ii]
          count = count + 1
        }
      }
    } # ---------------------------------------------------
    else if (ModelType == "NAR") { # ----------------------
      Xtmp = matrix(0., nrow=k-d, ncol=d*j);
      count = 1
      for (jj in 1:j) { #Data from original Target
        for (dd in d:1) {
          Xtmp[,count] = y[(dd):(k+dd-d-1),jj]
          count = count + 1
        }
      }
    } # ---------------------------------------------------
    else if (ModelType == "NARX") { # ---------------------
      Xtmp = matrix(0., nrow=k-d, ncol=((d+1)*i+d*j));
      count = 1
      for (ii in 1:i) { #Data from original Input
        for (dd in d:0) {
          Xtmp[,count] = X[(dd+1):(k+dd-d),ii]
          count = count + 1
        }
      }
      for (jj in 1:j) { #Data from original Target
        for (dd in d:1) {
          Xtmp[,count] = y[(dd):(k+dd-d-1),jj]
          count = count + 1
        }
      }
    } # ---------------------------------------------------
    else { stop("Invalid ModelType"); }
  } else { #If not, return the same inputs
    return(X);
  }
  # --------------------------------------------------------------------
  
  # Return the time-delayed Input dataset
  return(Xtmp);
  
}
# ======================================================================



#CloseLoopData Function ================================================
fCloseLoopData = function(IndexArray, Input, 
                          ModelType, TimeDelay, 
                          ActivationFunctionNames, 
                          ActFunParameter.a, ActFunParameter.b,
                          WeightsOutput, WeightBiasOutput, BiasOutput,
                          WeightsHidden2, WeightBiasHidden2, BiasHidden2,
                          WeightsHidden1, WeightBiasHidden1, BiasHidden1) {
  
  # Loading data -------------------------------------------------------
  id        = IndexArray;
  X         = Input;
  d         = TimeDelay;
  strActFun = ActivationFunctionNames;
  a         = ActFunParameter.a;
  b         = ActFunParameter.b;
  Wo        = WeightsOutput;
  WBo       = WeightBiasOutput;
  Bo        = BiasOutput;
  Wh2       = WeightsHidden2;
  WBh2      = WeightBiasHidden2;
  Bh2       = BiasHidden2;
  # --------------------------------------------------------------------
  
  # Gettig some useful information -------------------------------------
  i = ncol(X);
  j = ncol(Wo);
  k = nrow(X);
  # --------------------------------------------------------------------
  
  # Checking if function call is for 1 or 2-hidden layered net ---------
  if        (missing(WeightsHidden1) && 
             missing(WeightBiasHidden1) && 
             missing(BiasHidden1)) {
    Wh1  = NULL;
    WBh1 = NULL;
    Bh1  = NULL;
  } else if (missing(WeightsHidden1) || 
             missing(WeightBiasHidden1) || 
             missing(BiasHidden1)) {
    stop("Missing arguments");
  } else {
    Wh1  = WeightsHidden1;
    WBh1 = WeightBiasHidden1;
    Bh1  = BiasHidden1;
  }
  # --------------------------------------------------------------------
   
  # Pre-Checks and pre-processing --------------------------------------
  # Checking if there is a delay to be considered
  if (d == 0) { return(X); }
  
  # Storing the original index of the inputs
  id_original = matrix(1:k, nrow=k, ncol=1);
  
  # Returning the inputs to the id ordering
  X           = cbind(id, id_original, X);
  X           = X[ order(as.integer(X[, 1]),decreasing=FALSE),];
  id_original = X[,2];
  X           = X[,3:(i+2)];
  # --------------------------------------------------------------------
  
  # Calculating the Close-Loop Input data ------------------------------
  for (kk in 1:(k-1)) {
    
    # Feedforward -----------------------------------------
    if (is.null(Wh1)) { #If 1-hidden-layered
      # Hidden Layer 2
      Ah2 = X[kk,] %*% Wh2 + Bh2[kk,] %*% t(WBh2);
      Yh2 = f(Ah2,  strActFun[1], a[1], b[1]);
      # Output Layer
      Ao  = Yh2    %*% Wo  + Bo[kk,]  %*% t(WBo);
      O   = f(Ao,   strActFun[2], a[2], b[2]);
    } else {            #If 2-hidden-layered
      # Hidden Layer 1
      Ah1 = X[kk,] %*% Wh1 + Bh1[kk,] %*% t(WBh1);
      Yh1 = f(Ah1,  strActFun[1], a[1], b[1]);
      # Hidden Layer 2
      Ah2 = Yh1    %*% Wh2 + Bh2[kk,] %*% t(WBh2);
      Yh2 = f(Ah2,  strActFun[2], a[2], b[2]);
      # Output Layer
      Ao  = Yh2    %*% Wo  + Bo[kk,]  %*% t(WBo);
      O   = f(Ao,   strActFun[3], a[3], b[3]);
    }
    # -----------------------------------------------------
    
    # Update the next Input row (for each j) --------------
    for (jj in 1:j) {
      # Checking if delay is larger than one time step
      if (d > 1) {
        X[kk+1,(i+d*(jj-j-1)+2):(i+d*(jj-j))] = X[kk,(i+d*(jj-j-1)+1):(i+d*(jj-j)-1)];
      }
      X[kk+1,(i+d*(jj-j-1)+1)] = O[jj];
    }
    # -----------------------------------------------------
  }
  # --------------------------------------------------------------------
  
  # Returning the indexation to the original order
  X = cbind(id_original, X);
  X = X[ order(as.integer(X[, 1]),decreasing=FALSE),];
  X = X[,2:(i+1)];
  
  # Returning the updated Input close-loop
  return(X);
  
}
# ======================================================================