// File: misohena[P]drawstars/drawstars.cpp
// Function: bool drawStars<GaiaStar, main::$_0>(Image<ColorRGB<double>>&, char const*, main::$_0)
bool drawStars(ImageType &canvas, const char *filename, F funcDrawStars)
{
    const unsigned int canvasW = canvas.width();
    const unsigned int canvasH = canvas.height();

    File inputFile = File(filename, "rb");
    if(!inputFile){
        return false;
    }

    // prepare threads
    const unsigned int numThreads = std::thread::hardware_concurrency();
    printf("numThreads=%u\n", numThreads);

    std::unique_ptr<std::thread[]> threads(new std::thread[numThreads]);
    std::unique_ptr<ImageType[]> canvasForThreads(new ImageType[numThreads]);
    for(unsigned int ti = 0; ti < numThreads; ++ti){
        canvasForThreads[ti] = ImageType(canvasW, canvasH);
    }

    // read objects and render it
    const std::size_t OBJECT_BYTES = sizeof(StarType);
    const std::size_t BUFFER_OBJECTS = 1000000;
    const std::size_t BUFFER_BYTES = BUFFER_OBJECTS * OBJECT_BYTES;
    const std::unique_ptr<char[]> buffer_bytes(new char[BUFFER_BYTES]);
    for(;;){
        // read astronomical objects
        const std::size_t numObjectsRead = std::fread(
            buffer_bytes.get(),
            OBJECT_BYTES,
            BUFFER_OBJECTS, inputFile);

        // divide objects for threads
        const std::size_t numObjectsPerThread = (numObjectsRead + numThreads - 1) / numThreads;

        std::size_t numObjectsUnassigned = numObjectsRead;
        for(unsigned int ti = 0; ti < numThreads && numObjectsUnassigned > 0; ++ti){
            ImageType &threadCanvas = canvasForThreads[ti];
            const std::size_t threadNumObj = std::min(numObjectsUnassigned, numObjectsPerThread);
            const char * const threadHeadObj = buffer_bytes.get() + (numObjectsRead - numObjectsUnassigned) * OBJECT_BYTES;
            numObjectsUnassigned -= threadNumObj;
            if(threadNumObj > 0){
                threads[ti] = std::thread([=, &threadCanvas](){funcDrawStars(threadCanvas, Range<StarType>(threadHeadObj, threadNumObj));});
            }
        }

        for(unsigned int ti = 0; ti < numThreads; ++ti){
            if(threads[ti].joinable()){
                threads[ti].join();
            }
        }

        if(numObjectsRead < BUFFER_OBJECTS){
            break;
        }
    }

    // accumurate results of all threads
    for(unsigned int ti = 0; ti < numThreads; ++ti){
        auto srcPtr = canvasForThreads[ti].begin();
        auto dstPtr = canvas.begin();
        for(std::size_t count = canvasW * canvasH; count; --count){
            *dstPtr++ += *srcPtr++;
        }
    }
    return true;
}

// Assembly:
subq $0x2f8, %rsp            # imm = 0x2F8
leaq 0x300(%rsp), %rax
movq %rax, 0xe0(%rsp)
movq %rdi, 0x2e8(%rsp)
movq %rsi, 0x2e0(%rsp)
movq 0x2e8(%rsp), %rdi
callq 0x11c00
movl %eax, 0x2dc(%rsp)
movq 0x2e8(%rsp), %rdi
callq 0x11c10
movl %eax, 0x2d8(%rsp)
movq 0x2e0(%rsp), %rsi
leaq 0x2d0(%rsp), %rdi
leaq 0x14206(%rip), %rdx     # 0x1e54c
callq 0x11bb0
leaq 0x2d0(%rsp), %rdi
callq 0x11bf0
cmpq $0x0, %rax
jne 0xa376
movb $0x0, 0x2f7(%rsp)
movl $0x1, 0x2cc(%rsp)
jmp 0xab55
callq 0x4480
movl %eax, 0x2c8(%rsp)
movl 0x2c8(%rsp), %esi
leaq 0x141bf(%rip), %rdi     # 0x1e54f
xorl %eax, %eax
callq 0x4050
jmp 0xa399
movl 0x2c8(%rsp), %eax
movq %rax, 0xd0(%rsp)
leaq 0x8(,%rax,8), %rdi
callq 0x4030
movq %rax, 0xd8(%rsp)
jmp 0xa3bf
movq 0xd0(%rsp), %rax
movq 0xd8(%rsp), %rcx
movq %rax, (%rcx)
addq $0x8, %rcx
movq %rcx, 0xc8(%rsp)
cmpq $0x0, %rax
je 0xa444
movq 0xc8(%rsp), %rax
movq 0xd0(%rsp), %rdx
shlq $0x3, %rdx
movq %rax, %rcx
addq %rdx, %rcx
movq %rcx, 0xb8(%rsp)
movq %rax, 0xc0(%rsp)
movq 0xc0(%rsp), %rdi
movq %rdi, 0xb0(%rsp)
callq 0x1ab70
movq 0xb0(%rsp), %rax
movq 0xb8(%rsp), %rcx
addq $0x8, %rax
cmpq %rcx, %rax
movq %rax, 0xc0(%rsp)
jne 0xa40e
movq 0xc8(%rsp), %rsi
leaq 0x2b0(%rsp), %rdi
callq 0x1ab80
movl 0x2c8(%rsp), %eax
movl %eax, %edi
movq %rdi, 0x98(%rsp)
shlq $0x4, %rdi
orq $0x8, %rdi
movq %rdi, 0xa0(%rsp)
callq 0x4030
movq %rax, 0xa8(%rsp)
jmp 0xa489
movq 0x98(%rsp), %rax
movq 0xa8(%rsp), %rcx
movq %rax, (%rcx)
addq $0x8, %rcx
movq %rcx, 0x90(%rsp)
cmpq $0x0, %rax
je 0xa50a
movq 0x90(%rsp), %rax
movq 0x98(%rsp), %rdx
shlq $0x4, %rdx
movq %rax, %rcx
addq %rdx, %rcx
movq %rcx, 0x80(%rsp)
movq %rax, 0x88(%rsp)
movq 0x88(%rsp), %rdi
movq %rdi, 0x78(%rsp)
callq 0x1a770
jmp 0xa4ec
movq 0x80(%rsp), %rcx
movq 0x78(%rsp), %rax
addq $0x10, %rax
cmpq %rcx, %rax
movq %rax, 0x88(%rsp)
jne 0xa4d8
movq 0x90(%rsp), %rsi
leaq 0x2a8(%rsp), %rdi
callq 0x1abb0
movl $0x0, 0x2a4(%rsp)
movl 0x2a4(%rsp), %eax
cmpl 0x2c8(%rsp), %eax
jae 0xa688
movl 0x2dc(%rsp), %esi
movl 0x2d8(%rsp), %edx
leaq 0x290(%rsp), %rdi
callq 0x108f0
jmp 0xa55b
movl 0x2a4(%rsp), %eax
movl %eax, %esi
leaq 0x2a8(%rsp), %rdi
callq 0x1abe0
movq %rax, 0x70(%rsp)
jmp 0xa578
movq 0x70(%rsp), %rdi
leaq 0x290(%rsp), %rsi
callq 0x10bc0
leaq 0x290(%rsp), %rdi
callq 0x10c10
movl 0x2a4(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x2a4(%rsp)
jmp 0xa52a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x2c0(%rsp)
movl %eax, 0x2bc(%rsp)
jmp 0xab73
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x2c0(%rsp)
movl %eax, 0x2bc(%rsp)
jmp 0xab46
movq 0x90(%rsp), %rcx
movq %rax, %rsi
movq 0x78(%rsp), %rax
movq %rsi, 0x2c0(%rsp)
movl %edx, 0x2bc(%rsp)
cmpq %rax, %rcx
movq %rax, 0x68(%rsp)
je 0xa62f
movq 0x68(%rsp), %rdi
addq $-0x10, %rdi
movq %rdi, 0x60(%rsp)
callq 0x10c10
movq 0x90(%rsp), %rcx
movq 0x60(%rsp), %rax
cmpq %rcx, %rax
movq %rax, 0x68(%rsp)
jne 0xa605
movq 0xa0(%rsp), %rsi
movq 0xa8(%rsp), %rdi
callq 0x4680
jmp 0xab46
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x2c0(%rsp)
movl %eax, 0x2bc(%rsp)
jmp 0xab39
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x2c0(%rsp)
movl %eax, 0x2bc(%rsp)
leaq 0x290(%rsp), %rdi
callq 0x10c10
jmp 0xab39
movq $0x18, 0x288(%rsp)
movq $0xf4240, 0x280(%rsp) # imm = 0xF4240
movq $0x16e3600, 0x278(%rsp) # imm = 0x16E3600
movl $0x16e3600, %edi        # imm = 0x16E3600
callq 0x4030
movq %rax, 0x58(%rsp)
jmp 0xa6bd
movq 0x58(%rsp), %rsi
leaq 0x270(%rsp), %rdi
callq 0x1ac20
leaq 0x270(%rsp), %rdi
callq 0x1ac50
movq %rax, 0x48(%rsp)
leaq 0x2d0(%rsp), %rdi
callq 0x11bf0
movq 0x48(%rsp), %rdi
movq %rax, %rcx
movl $0x18, %esi
movl $0xf4240, %edx          # imm = 0xF4240
callq 0x4510
movq %rax, 0x50(%rsp)
jmp 0xa70c
movq 0x50(%rsp), %rax
movq %rax, 0x268(%rsp)
movq 0x268(%rsp), %rax
movl 0x2c8(%rsp), %ecx
addq %rcx, %rax
subq $0x1, %rax
movl 0x2c8(%rsp), %ecx
xorl %edx, %edx
divq %rcx
movq %rax, 0x260(%rsp)
movq 0x268(%rsp), %rax
movq %rax, 0x258(%rsp)
movl $0x0, 0x254(%rsp)
movl 0x254(%rsp), %ecx
xorl %eax, %eax
cmpl 0x2c8(%rsp), %ecx
movb %al, 0x47(%rsp)
jae 0xa784
cmpq $0x0, 0x258(%rsp)
seta %al
movb %al, 0x47(%rsp)
movb 0x47(%rsp), %al
testb $0x1, %al
jne 0xa791
jmp 0xa927
movl 0x254(%rsp), %eax
movl %eax, %esi
leaq 0x2a8(%rsp), %rdi
callq 0x1abe0
movq %rax, 0x38(%rsp)
jmp 0xa7ae
movq 0x38(%rsp), %rax
movq %rax, 0x248(%rsp)
leaq 0x258(%rsp), %rdi
leaq 0x260(%rsp), %rsi
callq 0x113c0
movq (%rax), %rax
movq %rax, 0x240(%rsp)
leaq 0x270(%rsp), %rdi
callq 0x1ac50
movq 0x268(%rsp), %rcx
subq 0x258(%rsp), %rcx
imulq $0x18, %rcx, %rcx
addq %rcx, %rax
movq %rax, 0x238(%rsp)
movq 0x240(%rsp), %rcx
movq 0x258(%rsp), %rax
subq %rcx, %rax
movq %rax, 0x258(%rsp)
cmpq $0x0, 0x240(%rsp)
jbe 0xa90f
movq 0xe0(%rsp), %rsi
movq 0x248(%rsp), %rax
movq %rax, 0x140(%rsp)
leaq 0x148(%rsp), %rdi
movl $0xd4, %edx
callq 0x4300
movq 0x238(%rsp), %rax
movq %rax, 0x220(%rsp)
movq 0x240(%rsp), %rax
movq %rax, 0x228(%rsp)
leaq 0x230(%rsp), %rdi
leaq 0x140(%rsp), %rsi
callq 0xc650
jmp 0xa892
movl 0x254(%rsp), %eax
movl %eax, %esi
leaq 0x2b0(%rsp), %rdi
callq 0x1ac80
movq %rax, 0x30(%rsp)
jmp 0xa8af
movq 0x30(%rsp), %rdi
leaq 0x230(%rsp), %rsi
callq 0x1acc0
leaq 0x230(%rsp), %rdi
callq 0x1ad00
jmp 0xa90f
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x2c0(%rsp)
movl %eax, 0x2bc(%rsp)
jmp 0xab2c
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x2c0(%rsp)
movl %eax, 0x2bc(%rsp)
leaq 0x230(%rsp), %rdi
callq 0x1ad00
jmp 0xab2c
jmp 0xa911
movl 0x254(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x254(%rsp)
jmp 0xa75e
movl $0x0, 0x13c(%rsp)
movl 0x13c(%rsp), %eax
cmpl 0x2c8(%rsp), %eax
jae 0xa9af
movl 0x13c(%rsp), %eax
movl %eax, %esi
leaq 0x2b0(%rsp), %rdi
callq 0x1ac80
movq %rax, 0x28(%rsp)
jmp 0xa95f
movq 0x28(%rsp), %rdi
callq 0x1ad20
testb $0x1, %al
jne 0xa96f
jmp 0xa99a
movl 0x13c(%rsp), %eax
movl %eax, %esi
leaq 0x2b0(%rsp), %rdi
callq 0x1ac80
movq %rax, 0x20(%rsp)
jmp 0xa98c
movq 0x20(%rsp), %rdi
callq 0x45a0
jmp 0xa998
jmp 0xa99a
jmp 0xa99c
movl 0x13c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x13c(%rsp)
jmp 0xa932
cmpq $0xf4240, 0x268(%rsp) # imm = 0xF4240
jae 0xa9bf
jmp 0xa9c4
jmp 0xa6cf
movl $0x0, 0x138(%rsp)
movl 0x138(%rsp), %eax
cmpl 0x2c8(%rsp), %eax
jae 0xaaf0
movl 0x138(%rsp), %eax
movl %eax, %esi
leaq 0x2a8(%rsp), %rdi
callq 0x1abe0
movq %rax, 0x18(%rsp)
jmp 0xaa00
movq 0x18(%rsp), %rdi
callq 0x1a8a0
movq %rax, 0x130(%rsp)
movq 0x2e8(%rsp), %rdi
callq 0x1a8a0
movq %rax, 0x128(%rsp)
movl 0x2dc(%rsp), %eax
imull 0x2d8(%rsp), %eax
movl %eax, %eax
movq %rax, 0x120(%rsp)
cmpq $0x0, 0x120(%rsp)
je 0xaad8
movq 0x130(%rsp), %rax
movq %rax, %rcx
addq $0x18, %rcx
movq %rcx, 0x130(%rsp)
movq 0x10(%rax), %rcx
movq %rcx, 0x110(%rsp)
movups (%rax), %xmm0
movaps %xmm0, 0x100(%rsp)
movq 0x128(%rsp), %rsi
movq %rsi, %rax
addq $0x18, %rax
movq %rax, 0x128(%rsp)
movq 0x110(%rsp), %rcx
movq %rsp, %rax
movq %rcx, 0x10(%rax)
movaps 0x100(%rsp), %xmm0
movups %xmm0, (%rax)
leaq 0xe8(%rsp), %rdi
callq 0x1ad60
jmp 0xaabd
jmp 0xaabf
movq 0x120(%rsp), %rax
addq $-0x1, %rax
movq %rax, 0x120(%rsp)
jmp 0xaa40
jmp 0xaada
movl 0x138(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x138(%rsp)
jmp 0xa9cf
movb $0x1, 0x2f7(%rsp)
movl $0x1, 0x2cc(%rsp)
leaq 0x270(%rsp), %rdi
callq 0x1adc0
leaq 0x2a8(%rsp), %rdi
callq 0x1ae20
leaq 0x2b0(%rsp), %rdi
callq 0x1ae80
jmp 0xab55
leaq 0x270(%rsp), %rdi
callq 0x1adc0
leaq 0x2a8(%rsp), %rdi
callq 0x1ae20
leaq 0x2b0(%rsp), %rdi
callq 0x1ae80
jmp 0xab73
leaq 0x2d0(%rsp), %rdi
callq 0x11d20
movb 0x2f7(%rsp), %al
andb $0x1, %al
addq $0x2f8, %rsp            # imm = 0x2F8
retq
leaq 0x2d0(%rsp), %rdi
callq 0x11d20
movq 0x2c0(%rsp), %rdi
callq 0x45f0
nopl (%rax)