// File: /workspace/github_cpp_makefile_star2_O2/xbai0624[P]dnn/src/ConstructLayer.cpp
// Function: ConstructLayer::BatchNormalization_GetGradientOnParameters_1DLayer()
void ConstructLayer::BatchNormalization_GetGradientOnParameters_1DLayer() //TO-DO: need to parallelize
{
    // gradients of cost function on \sigma_B^2, \mu_B, \gamma, \beta
    double epsilon = 1e-8;
    size_t batch_size = __p_data_interface->GetBatchSize();

    // mu_B
    // both cnn and fc have one average and variance
    Matrix & mu_B = __imageAverage_BN[0].OutputImageFromKernel[0]; // 1D layer has one kernel
    // sigma_B^2
    Matrix & sigma_B = __imageVariance_BN[0].OutputImageFromKernel[0];

    Matrix Var_inverse_half_cubic = sigma_B + epsilon;
    Var_inverse_half_cubic(inverse_half_cubic);
    Var_inverse_half_cubic = Var_inverse_half_cubic * (-1./2.);

    Matrix Var_inverse_square_root = sigma_B + epsilon;
    Var_inverse_square_root(inverse_square_root);
    Var_inverse_square_root = Var_inverse_square_root * (-1.);

    // 1) \partial{C}/\partial{\sigma_B^2}
    Matrix partial_C_over_partial_sigma_B2(__imageZ[0].OutputImageFromKernel[0].Dimension(), 0.);
    for(size_t i=0;i<batch_size;i++)
    {
        Matrix partial_C_over_partial_hat_x = __imageDelta[i].OutputImageFromKernel[0] ^ __gamma_BN[0];

        Matrix tmp = __imageZ[i].OutputImageFromKernel[0] - mu_B;
        tmp = tmp ^ partial_C_over_partial_hat_x;

        partial_C_over_partial_sigma_B2 = partial_C_over_partial_sigma_B2 + tmp;
    }
    partial_C_over_partial_sigma_B2 = partial_C_over_partial_sigma_B2 ^ Var_inverse_half_cubic;

    // \partial{C}/\partial{\sigma_B^2}  result
    __v_partial_C_over_partial_sigma_square.push_back(partial_C_over_partial_sigma_B2);

    // 2) \partial{C}/\partial{mu_B}
    Matrix part1(__imageZ[0].OutputImageFromKernel[0].Dimension(), 0.);
    Matrix part2(__imageZ[0].OutputImageFromKernel[0].Dimension(), 0.);
    for(size_t i=0;i<batch_size;i++)
    {
        Matrix partial_C_over_partial_hat_x = __imageDelta[i].OutputImageFromKernel[0] ^ __gamma_BN[0];
        part1 = part1 + partial_C_over_partial_hat_x;

        part2 = part2 + __imageZ[i].OutputImageFromKernel[0];
        part2 = part2 - mu_B;
    }
    part1 = part1 ^ Var_inverse_square_root;

    part2 = part2 * (-2./(double)batch_size);
    part2 = part2 ^ partial_C_over_partial_sigma_B2;
    Matrix partial_C_over_partial_mu_B = part1 + part2;

    // \partial{C}/\partial{mu_B}   result
    __v_partial_C_over_partial_mu.push_back(partial_C_over_partial_mu_B);

    // 3) \partial{C}/\partial{\gamma} and \partial{C}/\partial{beta}
    Matrix partial_C_over_partial_gamma(__imageZ[0].OutputImageFromKernel[0].Dimension(), 0.);
    Matrix partial_C_over_partial_beta(__imageZ[0].OutputImageFromKernel[0].Dimension(), 0.);
    for(size_t i=0;i<batch_size;i++)
    {
        Matrix tmp = __imageDelta[i].OutputImageFromKernel[0] ^ __imageZ_BN[i].OutputImageFromKernel[0];
        partial_C_over_partial_gamma = partial_C_over_partial_gamma +  tmp;

        partial_C_over_partial_beta = partial_C_over_partial_beta + __imageDelta[i].OutputImageFromKernel[0];
    }

    // \partial{C}/\partial{\gamma} and \partial{C}/\partial{beta} result
    __v_partial_C_over_partial_gamma.push_back(partial_C_over_partial_gamma);
    __v_partial_C_over_partial_beta.push_back(partial_C_over_partial_beta);
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x108, %rsp            # imm = 0x108
movq %rdi, %rbx
movq 0x400(%rdi), %rax
movslq (%rax), %rax
movq %rax, 0x20(%rsp)
movq 0x278(%rdi), %rax
movq 0x2a8(%rdi), %rcx
movq (%rax), %rax
movq %rax, 0x88(%rsp)
movq (%rcx), %r15
leaq 0xa8(%rsp), %r12
movsd 0x18d59(%rip), %xmm0    # 0x21528
movq %r12, %rdi
movq %r15, %rsi
callq 0x180cc
leaq 0xed8(%rip), %rsi       # 0x96b9
movq %r12, %rdi
callq 0x188a2
leaq 0x70(%rsp), %rdi
leaq 0xa8(%rsp), %rsi
movsd 0x18d32(%rip), %xmm0    # 0x21530
callq 0x182dc
leaq 0xa8(%rsp), %rdi
leaq 0x70(%rsp), %rsi
callq 0x125b4
leaq 0x70(%rsp), %rdi
callq 0x17a9a
leaq 0x70(%rsp), %rdi
movsd 0x18cfc(%rip), %xmm0    # 0x21528
movq %r15, %rsi
callq 0x180cc
leaq 0xecf(%rip), %rsi       # 0x970a
leaq 0x70(%rsp), %rdi
callq 0x188a2
leaq 0x40(%rsp), %rdi
leaq 0x70(%rsp), %rsi
movsd 0x18ce1(%rip), %xmm0    # 0x21538
callq 0x182dc
leaq 0x70(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0x125b4
leaq 0x40(%rsp), %rdi
callq 0x17a9a
movq %rbx, 0x18(%rsp)
movq 0x150(%rbx), %rax
movq (%rax), %rdi
callq 0x17a6a
leaq 0x40(%rsp), %rdi
xorpd %xmm0, %xmm0
movq %rax, %rsi
callq 0x1787a
xorl %r14d, %r14d
leaq 0x58(%rsp), %r12
movq %rsp, %r13
leaq 0x40(%rsp), %r15
movq 0x20(%rsp), %rbp
subq $0x1, %rbp
jb 0x8957
movq 0x18(%rsp), %rbx
movq 0x180(%rbx), %rax
movq 0x308(%rbx), %rdx
movq (%rax,%r14), %rsi
leaq 0x28(%rsp), %rdi
callq 0x17f64
movq 0x150(%rbx), %rax
movq (%rax,%r14), %rsi
movq %r12, %rdi
movq 0x88(%rsp), %rdx
callq 0x17eb0
movq %r13, %rdi
movq %r12, %rsi
leaq 0x28(%rsp), %rdx
callq 0x17f64
movq %r12, %rdi
movq %r13, %rsi
callq 0x125b4
movq %r13, %rdi
callq 0x17a9a
movq %r13, %rdi
movq %r15, %rbx
movq %r15, %rsi
movq %r12, %rdx
callq 0x17cc4
movq %rbx, %rdi
movq %r13, %rsi
callq 0x125b4
movq %r13, %rdi
callq 0x17a9a
movq %r12, %rdi
callq 0x17a9a
leaq 0x28(%rsp), %rdi
callq 0x17a9a
addq $0x18, %r14
jmp 0x88af
leaq 0x28(%rsp), %rdi
leaq 0x40(%rsp), %rsi
leaq 0xa8(%rsp), %rdx
callq 0x17f64
leaq 0x40(%rsp), %rdi
leaq 0x28(%rsp), %rsi
callq 0x125b4
movq 0x18(%rsp), %rbx
leaq 0x28(%rsp), %rdi
callq 0x17a9a
leaq 0x338(%rbx), %rdi
leaq 0x40(%rsp), %rsi
callq 0x1139c
movq 0x150(%rbx), %rax
movq (%rax), %rdi
callq 0x17a6a
leaq 0x28(%rsp), %rdi
xorpd %xmm0, %xmm0
movq %rax, %rsi
callq 0x1787a
movq 0x150(%rbx), %rax
movq (%rax), %rdi
callq 0x17a6a
leaq 0x58(%rsp), %rdi
xorpd %xmm0, %xmm0
movq %rax, %rsi
callq 0x1787a
xorl %esi, %esi
leaq 0x90(%rsp), %rbp
leaq 0x28(%rsp), %rbx
movq 0x20(%rsp), %r13
subq $0x1, %r13
jb 0x8ab1
movq 0x18(%rsp), %rcx
movq 0x180(%rcx), %rax
movq 0x308(%rcx), %rdx
movq %rsi, %r14
movq (%rax,%rsi), %rsi
movq %rsp, %r12
movq %r12, %rdi
callq 0x17f64
movq %rbp, %rdi
movq %rbx, %r15
movq %rbx, %rsi
movq %r12, %rdx
callq 0x17cc4
movq %r15, %rdi
movq %rbp, %rsi
callq 0x125b4
movq %rbp, %rdi
callq 0x17a9a
movq 0x18(%rsp), %rax
movq 0x150(%rax), %rax
movq (%rax,%r14), %rdx
movq %rbp, %rdi
leaq 0x58(%rsp), %r15
movq %r15, %rsi
callq 0x17cc4
movq %r15, %rdi
movq %rbp, %rsi
callq 0x125b4
movq %rbp, %rdi
callq 0x17a9a
movq %rbp, %rdi
movq %r15, %rsi
movq 0x88(%rsp), %rdx
callq 0x17eb0
movq %r15, %rdi
movq %rbp, %rsi
callq 0x125b4
movq %rbp, %rdi
callq 0x17a9a
movq %rsp, %rdi
callq 0x17a9a
movq %r14, %rsi
addq $0x18, %rsi
jmp 0x89f1
movq %rsp, %rdi
leaq 0x28(%rsp), %rsi
leaq 0x70(%rsp), %rdx
callq 0x17f64
leaq 0x28(%rsp), %rdi
movq %rsp, %rsi
callq 0x125b4
movq 0x18(%rsp), %rbx
movq %rsp, %rdi
callq 0x17a9a
movsd 0x20(%rsp), %xmm0
unpcklps 0x18536(%rip), %xmm0    # xmm0 = xmm0[0],mem[0],xmm0[1],mem[1]
subpd 0x1853e(%rip), %xmm0    # 0x21030
movapd %xmm0, %xmm1
unpckhpd %xmm0, %xmm1            # xmm1 = xmm1[1],xmm0[1]
addsd %xmm0, %xmm1
movsd 0x18a3a(%rip), %xmm0    # 0x21540
divsd %xmm1, %xmm0
movq %rsp, %rdi
leaq 0x58(%rsp), %rsi
callq 0x182dc
leaq 0x58(%rsp), %rdi
movq %rsp, %rsi
callq 0x125b4
movq %rsp, %rdi
callq 0x17a9a
movq %rsp, %rdi
leaq 0x58(%rsp), %rsi
leaq 0x40(%rsp), %rdx
callq 0x17f64
leaq 0x58(%rsp), %rdi
movq %rsp, %rsi
callq 0x125b4
movq %rsp, %rdi
callq 0x17a9a
movq %rsp, %rdi
leaq 0x28(%rsp), %rsi
leaq 0x58(%rsp), %rdx
callq 0x17cc4
leaq 0x350(%rbx), %rdi
movq %rsp, %rsi
callq 0x1139c
movq 0x150(%rbx), %rax
movq (%rax), %rdi
callq 0x17a6a
leaq 0x90(%rsp), %rdi
xorpd %xmm0, %xmm0
movq %rax, %rsi
callq 0x1787a
movq 0x150(%rbx), %rax
movq (%rax), %rdi
callq 0x17a6a
leaq 0xc0(%rsp), %rdi
xorpd %xmm0, %xmm0
movq %rax, %rsi
callq 0x1787a
xorl %ebp, %ebp
leaq 0xf0(%rsp), %r14
leaq 0xd8(%rsp), %r15
leaq 0x90(%rsp), %r12
leaq 0xc0(%rsp), %r13
subq $0x1, 0x20(%rsp)
jb 0x8c5a
movq 0x180(%rbx), %rax
movq 0x218(%rbx), %rcx
movq (%rax,%rbp), %rsi
movq (%rcx,%rbp), %rdx
movq %r14, %rdi
callq 0x17f64
movq %r15, %rdi
movq %r12, %rsi
movq %r14, %rdx
callq 0x17cc4
movq %r12, %rdi
movq %r15, %rsi
callq 0x125b4
movq %r15, %rdi
callq 0x17a9a
movq 0x180(%rbx), %rax
movq (%rax,%rbp), %rdx
movq %r15, %rdi
movq %r13, %rsi
callq 0x17cc4
movq %r13, %rdi
movq %r15, %rsi
callq 0x125b4
movq %r15, %rdi
callq 0x17a9a
movq %r14, %rdi
callq 0x17a9a
addq $0x18, %rbp
jmp 0x8bdc
leaq 0x368(%rbx), %rdi
leaq 0x90(%rsp), %rsi
callq 0x1139c
addq $0x380, %rbx            # imm = 0x380
leaq 0xc0(%rsp), %rsi
movq %rbx, %rdi
callq 0x1139c
leaq 0xc0(%rsp), %rdi
callq 0x17a9a
leaq 0x90(%rsp), %rdi
callq 0x17a9a
movq %rsp, %rdi
callq 0x17a9a
leaq 0x58(%rsp), %rdi
callq 0x17a9a
leaq 0x28(%rsp), %rdi
callq 0x17a9a
leaq 0x40(%rsp), %rdi
callq 0x17a9a
leaq 0x70(%rsp), %rdi
callq 0x17a9a
leaq 0xa8(%rsp), %rdi
callq 0x17a9a
addq $0x108, %rsp            # imm = 0x108
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
jmp 0x8db5
jmp 0x8db0
jmp 0x8db5
jmp 0x8db0
jmp 0x8db5
jmp 0x8db0
jmp 0x8db5
jmp 0x8db0
jmp 0x8dfd
jmp 0x8e02
jmp 0x8dfd
jmp 0x8dfd
jmp 0x8d4c
jmp 0x8d4c
jmp 0x8d34
jmp 0x8d4c
jmp 0x8d34
movq %rax, %rbx
jmp 0x8de8
jmp 0x8d7c
jmp 0x8d9a
jmp 0x8db5
jmp 0x8e02
jmp 0x8dfd
movq %rax, %rbx
jmp 0x8dde
jmp 0x8d58
jmp 0x8d5d
movq %rax, %rbx
jmp 0x8d6d
movq %rax, %rbx
leaq 0xd8(%rsp), %rdi
callq 0x17a9a
leaq 0xf0(%rsp), %rdi
callq 0x17a9a
jmp 0x8d7f
movq %rax, %rbx
leaq 0xc0(%rsp), %rdi
callq 0x17a9a
jmp 0x8d9d
jmp 0x8db5
jmp 0x8d9a
jmp 0x8db5
jmp 0x8d9a
jmp 0x8db0
jmp 0x8db5
movq %rax, %rbx
leaq 0x90(%rsp), %rdi
callq 0x17a9a
jmp 0x8db8
jmp 0x8db0
jmp 0x8db5
movq %rax, %rbx
jmp 0x8dc0
movq %rax, %rbx
movq %rsp, %rdi
callq 0x17a9a
leaq 0x58(%rsp), %rdi
callq 0x17a9a
leaq 0x28(%rsp), %rdi
callq 0x17a9a
leaq 0x40(%rsp), %rdi
callq 0x17a9a
leaq 0x70(%rsp), %rdi
callq 0x17a9a
leaq 0xa8(%rsp), %rdi
callq 0x17a9a
movq %rbx, %rdi
callq 0x43b0
movq %rax, %rbx
jmp 0x8dd4
movq %rax, %rbx
jmp 0x8dca
nop