// File: btbujiangjun[P]abcdl/include/utils/MnistHelper.h
// Function: abcdl::utils::MnistHelper<float>::read_mnist_file(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>> const&, int, unsigned long*)
inline std::unique_ptr<char[]> MnistHelper<T>::read_mnist_file(const std::string& path,
                                                               const int key,
                                                               size_t* out_rows){

    *out_rows = 0;

    std::ifstream file;
    file.open(path, std::ios::in | std::ios::binary | std::ios::ate);

    if(!file){
        std::cout << "Can't open file:" << path << std::endl;
        return {};
    }

    auto size = file.tellg();
    std::unique_ptr<char[]> buffer(new char[size]);

    //read the entire file to buffer
    file.seekg(0, std::ios::beg);
    file.read(buffer.get(), size);
    file.close();

    auto magic_num = read_header(buffer, 0);

    if(magic_num != key){
        std::cout << "Invalid magic number, probably not a MNIST file" << std::endl;
        return {};
    }

    auto count = read_header(buffer, 1);

    if(magic_num == 0x803){
        auto rows = read_header(buffer, 2);
        auto cols = read_header(buffer, 3);

        if(size < count * rows * cols + 16){
            std::cout << "File size ERROR" << std::endl;
            return {};
        }
    }else if( magic_num == 0x801){
        if(size < count + 8){
            std::cout << "File size ERROR" << std::endl;
            return {};
        }
    }

    *out_rows = count;

    return std::move(buffer);
}

// Assembly:
subq $0x318, %rsp            # imm = 0x318
movq %rdi, 0x88(%rsp)
movq %rdi, %rax
movq %rax, 0x90(%rsp)
movq %rdi, 0x310(%rsp)
movq %rsi, 0x308(%rsp)
movq %rdx, 0x300(%rsp)
movl %ecx, 0x2fc(%rsp)
movq %r8, 0x2f0(%rsp)
movq 0x308(%rsp), %rax
movq %rax, 0x98(%rsp)
movq 0x2f0(%rsp), %rax
movq $0x0, (%rax)
leaq 0xe8(%rsp), %rdi
callq 0x94a0
movq 0x300(%rsp), %rax
movq %rax, 0xa0(%rsp)
movl $0x8, %edi
movl $0x4, %esi
callq 0x194d0
movl %eax, 0xac(%rsp)
jmp 0x18e45
movl 0xac(%rsp), %edi
movl $0x2, %esi
callq 0x194d0
movl %eax, 0x84(%rsp)
jmp 0x18e5f
movl 0x84(%rsp), %edx
movq 0xa0(%rsp), %rsi
leaq 0xe8(%rsp), %rdi
callq 0x93b0
jmp 0x18e7d
movq 0xe8(%rsp), %rax
movq -0x18(%rax), %rax
leaq 0xe8(%rsp,%rax), %rdi
callq 0x91b0
movb %al, 0x83(%rsp)
jmp 0x18e9f
movb 0x83(%rsp), %al
testb $0x1, %al
jne 0x18eac
jmp 0x18f28
movq 0xb3105(%rip), %rdi     # 0xcbfb8
leaq 0x7535d(%rip), %rsi     # 0x8e217
callq 0x9420
movq %rax, 0x78(%rsp)
jmp 0x18ec6
movq 0x78(%rsp), %rdi
movq 0x300(%rsp), %rsi
callq 0x93d0
movq %rax, 0x70(%rsp)
jmp 0x18edf
movq 0x70(%rsp), %rdi
movq 0xb30ad(%rip), %rsi     # 0xcbf98
callq 0x9450
jmp 0x18ef2
movq 0x88(%rsp), %rdi
callq 0x194f0
movl $0x1, 0xd8(%rsp)
jmp 0x19288
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xe0(%rsp)
movl %eax, 0xdc(%rsp)
jmp 0x192a5
leaq 0xe8(%rsp), %rdi
callq 0x96e0
movq %rdx, 0x60(%rsp)
movq %rax, 0x68(%rsp)
jmp 0x18f41
movq 0x60(%rsp), %rax
movq 0x68(%rsp), %rcx
movq %rcx, 0xc8(%rsp)
movq %rax, 0xd0(%rsp)
leaq 0xc8(%rsp), %rdi
callq 0x19520
movq %rax, 0x58(%rsp)
jmp 0x18f6f
movq 0x58(%rsp), %rdi
testq %rdi, %rdi
movq $-0x1, %rax
cmovsq %rax, %rdi
callq 0x9030
movq %rax, 0x50(%rsp)
jmp 0x18f8e
movq 0x50(%rsp), %rsi
leaq 0xc0(%rsp), %rdi
callq 0x19530
xorl %edx, %edx
movl %edx, %esi
leaq 0xe8(%rsp), %rdi
callq 0x9570
jmp 0x18fb3
leaq 0xc0(%rsp), %rdi
callq 0x19350
movq %rax, 0x40(%rsp)
leaq 0xc8(%rsp), %rdi
callq 0x19520
movq %rax, 0x48(%rsp)
jmp 0x18fd9
movq 0x48(%rsp), %rdx
movq 0x40(%rsp), %rsi
leaq 0xe8(%rsp), %rdi
callq 0x9500
jmp 0x18ff2
leaq 0xe8(%rsp), %rdi
callq 0x90b0
jmp 0x19001
movq 0x98(%rsp), %rdi
xorl %eax, %eax
movl %eax, %edx
leaq 0xc0(%rsp), %rsi
callq 0x192e0
movl %eax, 0x3c(%rsp)
jmp 0x19020
movl 0x3c(%rsp), %eax
movl %eax, 0xbc(%rsp)
movl 0xbc(%rsp), %eax
cmpl 0x2fc(%rsp), %eax
je 0x190ab
movq 0xb2f76(%rip), %rdi     # 0xcbfb8
leaq 0x751df(%rip), %rsi     # 0x8e228
callq 0x9420
movq %rax, 0x30(%rsp)
jmp 0x19055
movq 0x30(%rsp), %rdi
movq 0xb2f37(%rip), %rsi     # 0xcbf98
callq 0x9450
jmp 0x19068
movq 0x88(%rsp), %rdi
callq 0x194f0
movl $0x1, 0xd8(%rsp)
jmp 0x1927b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0xe0(%rsp)
movl %eax, 0xdc(%rsp)
leaq 0xc0(%rsp), %rdi
callq 0x19470
jmp 0x192a5
movq 0x98(%rsp), %rdi
leaq 0xc0(%rsp), %rsi
movl $0x1, %edx
callq 0x192e0
movl %eax, 0x2c(%rsp)
jmp 0x190cb
movl 0x2c(%rsp), %eax
movl %eax, 0xb8(%rsp)
cmpl $0x803, 0xbc(%rsp) # imm = 0x803
jne 0x191c7
movq 0x98(%rsp), %rdi
leaq 0xc0(%rsp), %rsi
movl $0x2, %edx
callq 0x192e0
movl %eax, 0x28(%rsp)
jmp 0x19107
movq 0x98(%rsp), %rdi
movl 0x28(%rsp), %eax
movl %eax, 0xb4(%rsp)
leaq 0xc0(%rsp), %rsi
movl $0x3, %edx
callq 0x192e0
movl %eax, 0x24(%rsp)
jmp 0x19132
movl 0x24(%rsp), %eax
movl %eax, 0xb0(%rsp)
leaq 0xc8(%rsp), %rdi
callq 0x19520
movq %rax, 0x18(%rsp)
jmp 0x19151
movq 0x18(%rsp), %rax
movl 0xb8(%rsp), %ecx
imull 0xb4(%rsp), %ecx
imull 0xb0(%rsp), %ecx
addl $0x10, %ecx
movslq %ecx, %rcx
cmpq %rcx, %rax
jge 0x191c2
movq 0xb2e39(%rip), %rdi     # 0xcbfb8
leaq 0x750d2(%rip), %rsi     # 0x8e258
callq 0x9420
movq %rax, 0x10(%rsp)
jmp 0x19192
movq 0x10(%rsp), %rdi
movq 0xb2dfa(%rip), %rsi     # 0xcbf98
callq 0x9450
jmp 0x191a5
movq 0x88(%rsp), %rdi
callq 0x194f0
movl $0x1, 0xd8(%rsp)
jmp 0x1927b
jmp 0x19248
cmpl $0x801, 0xbc(%rsp) # imm = 0x801
jne 0x19246
leaq 0xc8(%rsp), %rdi
callq 0x19520
movq %rax, 0x8(%rsp)
jmp 0x191e8
movq 0x8(%rsp), %rax
movl 0xb8(%rsp), %ecx
addl $0x8, %ecx
movslq %ecx, %rcx
cmpq %rcx, %rax
jge 0x19244
movq 0xb2db2(%rip), %rdi     # 0xcbfb8
leaq 0x7504b(%rip), %rsi     # 0x8e258
callq 0x9420
movq %rax, (%rsp)
jmp 0x19218
movq (%rsp), %rdi
movq 0xb2d75(%rip), %rsi     # 0xcbf98
callq 0x9450
jmp 0x1922a
movq 0x88(%rsp), %rdi
callq 0x194f0
movl $0x1, 0xd8(%rsp)
jmp 0x1927b
jmp 0x19246
jmp 0x19248
movq 0x88(%rsp), %rdi
movslq 0xb8(%rsp), %rcx
movq 0x2f0(%rsp), %rax
movq %rcx, (%rax)
leaq 0xc0(%rsp), %rsi
callq 0x19560
movl $0x1, 0xd8(%rsp)
leaq 0xc0(%rsp), %rdi
callq 0x19470
leaq 0xe8(%rsp), %rdi
callq 0x90e0
movq 0x90(%rsp), %rax
addq $0x318, %rsp            # imm = 0x318
retq
leaq 0xe8(%rsp), %rdi
callq 0x90e0
movq 0xe0(%rsp), %rdi
callq 0x9700
nop