#include "full_connected.hpp"

using namespace Layer;

void
FullConnected::init(Real m,Real d){
  default_random_engine generator;
  normal_distribution<Real> distribution(m,d);
  for(size_t i=0;i<n_out;++i){
    b[i]=distribution(generator);
  }
  for(size_t i=0;i<n_out*n_in;++i){
    w[i]=distribution(generator);
  }
}

void
FullConnected::init_standard(){
  default_random_engine generator;
  normal_distribution<Real> distribution(0,1);
  for(size_t i=0;i<n_out;++i){
    b[i]=distribution(generator);
  }
  for(size_t i=0;i<n_out*n_in;++i){
    normal_distribution<Real> distribution2(0,1/sqrt(n_in));
    w[i]=distribution2(generator);
  }
}

Vector
FullConnected::feed_forward(Vector x){
  x_in=x;
  for(size_t i=0;i<n_out;++i){
    Real temp=b[i];
    for(size_t j=0;j<n_in;++j){
      temp+=w[indice2(i,j,n_in)]*x[j];
    }
    x_out[i]=temp;
  }
  return x_out;
}

void
FullConnected::init_nabla(){
  for(size_t i=0;i<n_out;++i){
    nabla_b[i]=0;
  }
  for(size_t i=0;i<n_out*n_in;++i){
    nabla_w[i]=0;
  }
}

Vector
FullConnected::back_propagation(Vector d){
  for(size_t i=0;i<n_in;++i){
    Real temp=0;
    for(size_t j=0;j<n_out;++j){
      temp+=w[indice2(j,i,n_in)]*d[j];
    }
    delta[i]=temp;
  }
  //Update nabla_b
  for(size_t i=0;i<n_out;++i){
    nabla_b[i]+=d[i];
  }
  //Update nabla_w
  for(size_t i=0;i<n_out;++i){
    for(size_t j=0;j<n_in;++j){
      nabla_w[indice2(i,j,n_in)]+=d[i]*x_in[j];
    }
  }
  return delta;
}

void
FullConnected::update(Real eta){
  //Update b
  for(size_t i=0;i<n_out;++i){
    b[i]-=eta*nabla_b[i];
  }
  //Update w
  for(size_t i=0;i<n_out*n_in;++i){
    w[i]-=eta*nabla_w[i];
  }
}
