// File: xbai0624[P]dnn/src/Neuron.cpp
// Function: Neuron::UpdateDeltaFC(int)
void Neuron::UpdateDeltaFC(int sample_index)
{
    // back propagation delta for fully connected layer
    if(__sigmaPrime.size() <= 0) 
    {
        std::cout<<"Error: Neuron::UpdateDeltaFC() computing delta needs sigma^prime computed first."<<std::endl;
        std::cout<<"        "<<__delta.size()<<" deltas, "<<__sigmaPrime.size()<<" sigma^primes"<<std::endl;
        exit(0);
    }
    //cout<<"here..... Neuron::UpdateDeltaFC()"<<endl;

    Layer* __nextLayer = __layer->GetNextLayer();

    auto & __deltaNext = __nextLayer->GetImagesActiveDelta(); // no vectorization needed, b/c "fc->cnn" type connection is not used in backward direction
    //cout<<"delta images batch size: "<<__deltaNext.size()<<endl;
    Images & image_delta_Next = __deltaNext[sample_index]; // get current sample delta
    std::vector<Matrix> &deltaNext = image_delta_Next.OutputImageFromKernel;
    if( deltaNext.size() != 1 ) 
    {
        std::cout<<"Error: Delta matrix dimension not match in FC layer"<<std::endl;
        exit(0);
    }
    Matrix & delta = deltaNext[0];
    //cout<<delta<<endl;

    //cout<<"sample id: "<<sample_index<<endl;

    auto wv = __nextLayer->GetWeightMatrix();
    /* 
    // 1) method 1
    Matrix w = Matrix::ConcatenateMatrixByI(wv); // for large matrix, this line is too time consuming, use method 2 instead

    auto w_dim = w.Dimension();
    if( w_dim.second < __coord.i ) 
    {
        std::cout<<"Error: weight matrix dimension not match in FC layer"<<std::endl;
        std::cout<<"Number of kernels: "<<wv->size()<<std::endl;
        std::cout<<"Neuron coord: "<<__coord<<std::endl;
        exit(0);
    }

    // back propogate delta
    w = w.Transpose();

    //cout<<w<<endl;
    auto dim = w.Dimension();
    w = w.GetSection(__coord.i, __coord.i+1, 0, dim.second);

    Matrix deltaCurrentLayer = w*delta;
    if(deltaCurrentLayer.Dimension().first != 1 || deltaCurrentLayer.Dimension().second != 1) 
    {
        std::cout<<"Error: back propagation delta, matrix dimension not match in FC layer."<<std::endl;
        exit(0);
    }
    */

    // 2) method 2
    auto delta_dim = delta.Dimension();
    assert(delta_dim.second == 1);
    double delta_current = 0;
    for(size_t i=0;i<delta_dim.first;i++)
    {
        delta_current += delta[i][0] * ((*wv)[i])[0][__coord.i];
    }

    // get sigma^\prime for current sample
    double s_prime = __sigmaPrime[sample_index];

    //double v = deltaCurrentLayer[0][0]; // method 1
    double v = delta_current; // method 2
    v = v*s_prime;

    __delta[sample_index] = v;

    //cout<<"here..... Neuron::UpdateDeltaFC()   end"<<endl;
}

// Assembly:
subq $0x98, %rsp
movq %rdi, 0x90(%rsp)
movl %esi, 0x8c(%rsp)
movq 0x90(%rsp), %rdi
movq %rdi, 0x20(%rsp)
addq $0x60, %rdi
callq 0x21e20
cmpq $0x0, %rax
ja 0x48dbf
movq 0x222bb(%rip), %rdi     # 0x6afd8
leaq 0xd13c(%rip), %rsi      # 0x55e60
callq 0x4230
movq %rax, %rdi
movq 0x2227d(%rip), %rsi     # 0x6afb0
callq 0x4260
movq 0x22299(%rip), %rdi     # 0x6afd8
leaq 0xd07c(%rip), %rsi      # 0x55dc2
callq 0x4230
movq 0x20(%rsp), %rdi
movq %rax, 0x10(%rsp)
addq $0x30, %rdi
callq 0x21e20
movq 0x10(%rsp), %rdi
movq %rax, %rsi
callq 0x4080
movq %rax, %rdi
leaq 0xd056(%rip), %rsi      # 0x55dcb
callq 0x4230
movq 0x20(%rsp), %rdi
movq %rax, 0x18(%rsp)
addq $0x60, %rdi
callq 0x21e20
movq 0x18(%rsp), %rdi
movq %rax, %rsi
callq 0x4080
movq %rax, %rdi
leaq 0xd031(%rip), %rsi      # 0x55dd5
callq 0x4230
movq %rax, %rdi
movq 0x221fd(%rip), %rsi     # 0x6afb0
callq 0x4260
xorl %edi, %edi
callq 0x4290
movq 0x20(%rsp), %rax
movq 0x78(%rax), %rdi
movq (%rdi), %rax
callq *0x2c0(%rax)
movq %rax, 0x80(%rsp)
movq 0x80(%rsp), %rdi
movq (%rdi), %rax
callq *0xf8(%rax)
movq %rax, 0x78(%rsp)
movq 0x78(%rsp), %rdi
movslq 0x8c(%rsp), %rsi
callq 0x1fd40
movq %rax, 0x70(%rsp)
movq 0x70(%rsp), %rax
movq %rax, 0x68(%rsp)
movq 0x68(%rsp), %rdi
callq 0x1f9c0
cmpq $0x1, %rax
je 0x48e49
movq 0x221b1(%rip), %rdi     # 0x6afd8
leaq 0xd083(%rip), %rsi      # 0x55eb1
callq 0x4230
movq %rax, %rdi
movq 0x22173(%rip), %rsi     # 0x6afb0
callq 0x4260
xorl %edi, %edi
callq 0x4290
movq 0x68(%rsp), %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0x1f9e0
movq %rax, 0x60(%rsp)
movq 0x80(%rsp), %rdi
movq (%rdi), %rax
callq *0x220(%rax)
movq %rax, 0x58(%rsp)
movq 0x60(%rsp), %rdi
callq 0x3bae0
movq %rax, 0x48(%rsp)
movq %rdx, 0x50(%rsp)
cmpq $0x1, 0x50(%rsp)
jne 0x48e90
jmp 0x48eaf
leaq 0xd04e(%rip), %rdi      # 0x55ee5
leaq 0xcb38(%rip), %rsi      # 0x559d6
movl $0x24e, %edx            # imm = 0x24E
leaq 0xd051(%rip), %rcx      # 0x55efb
callq 0x4170
xorps %xmm0, %xmm0
movsd %xmm0, 0x40(%rsp)
movq $0x0, 0x38(%rsp)
movq 0x38(%rsp), %rax
cmpq 0x48(%rsp), %rax
jae 0x48f56
movq 0x60(%rsp), %rdi
movq 0x38(%rsp), %rsi
callq 0x3be50
movq %rax, %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0x1fd60
movsd (%rax), %xmm0
movsd %xmm0, 0x8(%rsp)
movq 0x58(%rsp), %rdi
movq 0x38(%rsp), %rsi
callq 0x1f9e0
movq %rax, %rdi
xorl %eax, %eax
movl %eax, %esi
callq 0x3be50
movq %rax, %rdi
movq 0x20(%rsp), %rax
movq 0x88(%rax), %rsi
callq 0x1fd60
movsd 0x8(%rsp), %xmm0
movsd (%rax), %xmm2
movsd 0x40(%rsp), %xmm1
mulsd %xmm2, %xmm0
addsd %xmm1, %xmm0
movsd %xmm0, 0x40(%rsp)
movq 0x38(%rsp), %rax
addq $0x1, %rax
movq %rax, 0x38(%rsp)
jmp 0x48ec1
movq 0x20(%rsp), %rdi
addq $0x60, %rdi
movslq 0x8c(%rsp), %rsi
callq 0x1fd60
movq 0x20(%rsp), %rdi
movsd (%rax), %xmm0
movsd %xmm0, 0x30(%rsp)
movsd 0x40(%rsp), %xmm0
movsd %xmm0, 0x28(%rsp)
movsd 0x28(%rsp), %xmm0
mulsd 0x30(%rsp), %xmm0
movsd %xmm0, 0x28(%rsp)
movsd 0x28(%rsp), %xmm0
movsd %xmm0, (%rsp)
addq $0x30, %rdi
movslq 0x8c(%rsp), %rsi
callq 0x1fd60
movsd (%rsp), %xmm0
movsd %xmm0, (%rax)
addq $0x98, %rsp
retq
nopw %cs:(%rax,%rax)