// File: xbai0624[P]dnn/src/ConstructLayer.cpp
// Function: ConstructLayer::ForwardPropagateForBatch()
void ConstructLayer::ForwardPropagateForBatch()
{
    assert(__p_data_interface != nullptr);
    int sample_size = __p_data_interface->GetBatchSize();

    // ----step 1) forward propagate for Z image
#ifdef MULTI_THREAD
    auto process_range_for_z = [&](int start, int end)
    {
        for(int sample_index = start; sample_index<end; sample_index++)
        {
            ForwardPropagate_Z_ForSample(sample_index);
        }
    };

    std::vector<std::thread> vth;
    int Range[NTHREAD+1];
    for(int i=0;i<NTHREAD;i++)
        Range[i] = sample_size/NTHREAD * i;
    Range[NTHREAD] = sample_size;

    for(int i=0;i<NTHREAD;i++)
    {
        vth.push_back(std::thread(process_range_for_z, Range[i], Range[i+1]));
    }

    for(auto &i: vth)
        i.join();
#else
    for(int sample_index = 0; sample_index<sample_size; sample_index++)
    {
        ForwardPropagate_Z_ForSample(sample_index);
    }
#endif

    // ----step 2) this section is for batch normalization and related jobs
    // after batch propagation finished, do batch normalization
    if(__use_batch_normalization)
    {
        BatchNormalization_UpdateZ();

        // A-image and \sigma^\prime-image are calcualted after finishing all Z calculations in neurons
        // so if replace un-normalized Z values in neurons with normalized Z values
        // then the nueron will automatically use the normalized Z to 
        // calculate A and \sigma^\prime (In this design, the original un-normalized A and \sigma^\prime
        // will not be calculated, saving computation time)
#ifdef MULTI_THREAD
        auto process_range_for_replace_z = [&](int start, int end)
        {
            for(int sample_index = start; sample_index < end; sample_index++)
            {
                ReplaceNeuronZWithBatchNormalization(sample_index);
            }
        };

        vth.clear();
        for(int i=0;i<NTHREAD;i++)
        {
            vth.push_back(std::thread(process_range_for_replace_z, Range[i], Range[i+1]));
        }

        for(auto &i: vth)
            i.join();
#else
        for(int sample_index = 0;sample_index<sample_size;sample_index++)
        {
            ReplaceNeuronZWithBatchNormalization(sample_index);
        }
#endif
    }

    // test batch noarmlization update z
    //TestBatchNormalization_UpdateZ();

    // ----step 3) forward propagate for sigma^prime and A image
#ifdef MULTI_THREAD
    auto process_range_for_A_sigma_prime = [&](int start, int end)
    {
        for(int sample_index = start; sample_index<end; sample_index++)
        {
            ForwardPropagate_SigmaPrimeAndA_ForSample(sample_index);
        }
    };

    vth.clear();
    for(int i=0;i<NTHREAD;i++)
    {
        vth.push_back(std::thread(process_range_for_A_sigma_prime, Range[i], Range[i+1]));
    }

    for(auto &i: vth)
        i.join();
#else
    for(int sample_index = 0; sample_index<sample_size; sample_index++)
    {
        ForwardPropagate_SigmaPrimeAndA_ForSample(sample_index);
    }
#endif

    // progress
    //std::cout<<"forward propagate for batch finished. Layer: "<<GetID()<<std::endl;
}

// Assembly:
subq $0x118, %rsp            # imm = 0x118
movq %rdi, 0x110(%rsp)
movq 0x110(%rsp), %rax
movq %rax, 0x8(%rsp)
cmpq $0x0, 0x400(%rax)
je 0xc008
jmp 0xc027
leaq 0x464bf(%rip), %rdi     # 0x524ce
leaq 0x461bc(%rip), %rsi     # 0x521d2
movl $0x330, %edx            # imm = 0x330
leaq 0x464ca(%rip), %rcx     # 0x524ec
callq 0x4170
movq 0x8(%rsp), %rax
movq 0x400(%rax), %rdi
callq 0x1f310
movl %eax, %ecx
movq 0x8(%rsp), %rax
movl %ecx, 0x10c(%rsp)
movq %rax, 0x100(%rsp)
leaq 0xe8(%rsp), %rdi
callq 0x1fac0
movl $0x0, 0xbc(%rsp)
cmpl $0x8, 0xbc(%rsp)
jge 0xc0ab
movl 0x10c(%rsp), %eax
movl $0x8, %ecx
cltd
idivl %ecx
movl %eax, %ecx
imull 0xbc(%rsp), %ecx
movslq 0xbc(%rsp), %rax
movl %ecx, 0xc0(%rsp,%rax,4)
movl 0xbc(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xbc(%rsp)
jmp 0xc066
movl 0x10c(%rsp), %eax
movl %eax, 0xe0(%rsp)
movl $0x0, 0xb8(%rsp)
cmpl $0x8, 0xb8(%rsp)
jge 0xc177
movslq 0xb8(%rsp), %rax
leaq 0xc0(%rsp,%rax,4), %rdx
leaq 0xc4(%rsp,%rax,4), %rcx
leaq 0xb0(%rsp), %rdi
leaq 0x100(%rsp), %rsi
callq 0xc460
jmp 0xc101
leaq 0xe8(%rsp), %rdi
leaq 0xb0(%rsp), %rsi
callq 0x1fad0
jmp 0xc118
leaq 0xb0(%rsp), %rdi
callq 0x1fb00
movl 0xb8(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xb8(%rsp)
jmp 0xc0c4
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xa8(%rsp)
movl %eax, 0xa4(%rsp)
jmp 0xc440
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xa8(%rsp)
movl %eax, 0xa4(%rsp)
leaq 0xb0(%rsp), %rdi
callq 0x1fb00
jmp 0xc440
leaq 0xe8(%rsp), %rax
movq %rax, 0x98(%rsp)
movq 0x98(%rsp), %rdi
callq 0x1fb20
movq %rax, 0x90(%rsp)
movq 0x98(%rsp), %rdi
callq 0x1fb50
movq %rax, 0x88(%rsp)
leaq 0x90(%rsp), %rdi
leaq 0x88(%rsp), %rsi
callq 0x1fb80
testb $0x1, %al
jne 0xc1cc
jmp 0xc201
leaq 0x90(%rsp), %rdi
callq 0x1fbc0
movq %rax, 0x80(%rsp)
movq 0x80(%rsp), %rdi
callq 0x43c0
jmp 0xc1f0
jmp 0xc1f2
leaq 0x90(%rsp), %rdi
callq 0x1fbd0
jmp 0xc1b1
movq 0x8(%rsp), %rax
testb $0x1, 0x210(%rax)
je 0xc32b
movq 0x8(%rsp), %rdi
movq (%rdi), %rax
movq 0x190(%rax), %rax
callq *%rax
jmp 0xc226
movq 0x8(%rsp), %rax
movq %rax, 0x78(%rsp)
leaq 0xe8(%rsp), %rdi
callq 0x1fbf0
movl $0x0, 0x74(%rsp)
cmpl $0x8, 0x74(%rsp)
jge 0xc2c0
movslq 0x74(%rsp), %rax
leaq 0xc0(%rsp,%rax,4), %rdx
leaq 0xc4(%rsp,%rax,4), %rcx
leaq 0x68(%rsp), %rdi
leaq 0x78(%rsp), %rsi
callq 0xc530
jmp 0xc272
leaq 0xe8(%rsp), %rdi
leaq 0x68(%rsp), %rsi
callq 0x1fad0
jmp 0xc286
leaq 0x68(%rsp), %rdi
callq 0x1fb00
movl 0x74(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x74(%rsp)
jmp 0xc245
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xa8(%rsp)
movl %eax, 0xa4(%rsp)
leaq 0x68(%rsp), %rdi
callq 0x1fb00
jmp 0xc440
leaq 0xe8(%rsp), %rax
movq %rax, 0x60(%rsp)
movq 0x60(%rsp), %rdi
callq 0x1fb20
movq %rax, 0x58(%rsp)
movq 0x60(%rsp), %rdi
callq 0x1fb50
movq %rax, 0x50(%rsp)
leaq 0x58(%rsp), %rdi
leaq 0x50(%rsp), %rsi
callq 0x1fb80
testb $0x1, %al
jne 0xc300
jmp 0xc329
leaq 0x58(%rsp), %rdi
callq 0x1fbc0
movq %rax, 0x48(%rsp)
movq 0x48(%rsp), %rdi
callq 0x43c0
jmp 0xc31b
jmp 0xc31d
leaq 0x58(%rsp), %rdi
callq 0x1fbd0
jmp 0xc2eb
jmp 0xc32b
movq 0x8(%rsp), %rax
movq %rax, 0x40(%rsp)
leaq 0xe8(%rsp), %rdi
callq 0x1fbf0
movl $0x0, 0x3c(%rsp)
cmpl $0x8, 0x3c(%rsp)
jge 0xc3c2
movslq 0x3c(%rsp), %rax
leaq 0xc0(%rsp,%rax,4), %rdx
leaq 0xc4(%rsp,%rax,4), %rcx
leaq 0x30(%rsp), %rdi
leaq 0x40(%rsp), %rsi
callq 0xc600
jmp 0xc377
leaq 0xe8(%rsp), %rdi
leaq 0x30(%rsp), %rsi
callq 0x1fad0
jmp 0xc38b
leaq 0x30(%rsp), %rdi
callq 0x1fb00
movl 0x3c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x3c(%rsp)
jmp 0xc34a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xa8(%rsp)
movl %eax, 0xa4(%rsp)
leaq 0x30(%rsp), %rdi
callq 0x1fb00
jmp 0xc440
leaq 0xe8(%rsp), %rax
movq %rax, 0x28(%rsp)
movq 0x28(%rsp), %rdi
callq 0x1fb20
movq %rax, 0x20(%rsp)
movq 0x28(%rsp), %rdi
callq 0x1fb50
movq %rax, 0x18(%rsp)
leaq 0x20(%rsp), %rdi
leaq 0x18(%rsp), %rsi
callq 0x1fb80
testb $0x1, %al
jne 0xc402
jmp 0xc42b
leaq 0x20(%rsp), %rdi
callq 0x1fbc0
movq %rax, 0x10(%rsp)
movq 0x10(%rsp), %rdi
callq 0x43c0
jmp 0xc41d
jmp 0xc41f
leaq 0x20(%rsp), %rdi
callq 0x1fbd0
jmp 0xc3ed
leaq 0xe8(%rsp), %rdi
callq 0x1fc10
addq $0x118, %rsp            # imm = 0x118
retq
leaq 0xe8(%rsp), %rdi
callq 0x1fc10
movq 0xa8(%rsp), %rdi
callq 0x43f0
nopw (%rax,%rax)