// File: /workspace/github_cpp_makefile_star2_O3/alexandres[P]terashuf/terashuf.cc
// Function: main
int main()
{
    char const *sepStr = std::getenv("SEP");
    const char sep = (sepStr != NULL && strlen(sepStr)) ? sepStr[0] : '\n';

    char *seedStr = std::getenv("SEED");
    const int seed = (seedStr != NULL && strlen(seedStr)) ? strtol(seedStr, NULL, 10) : time(NULL);
    std::mt19937_64 rng(seed);

    char *skipStr = std::getenv("SKIP");
    const int skipLines = (skipStr != NULL && strlen(skipStr)) ? strtol(skipStr, NULL, 10) : 0;

    char const *tmpDir = std::getenv("TMPDIR");
    if (tmpDir == NULL)
        tmpDir = "/tmp";
    char *tmpNameTemplate = (char *)malloc((strlen(tmpDir) + sizeof(TMP_NAME_TEMPLATE)) * sizeof(char));
    strcpy(tmpNameTemplate, tmpDir);
    strcat(tmpNameTemplate, TMP_NAME_TEMPLATE);

    char *memoryStr = std::getenv("MEMORY");
    const float memory = (memoryStr != NULL && strlen(memoryStr)) ? strtof(memoryStr, NULL) : 4;

    std::vector<ll> shufIndexes;

    const ll bufBytes = sizeof(char) * (ll)(memory * 1024. * 1024. * 1024.);
    char *buf = (char *)malloc(bufBytes);
    fprintf(stderr, "trying to allocate %zu bytes\n", bufBytes);
    if (buf == NULL)
    {
        fprintf(stderr, "failed to allocate buf memory\n");
        return 1;
    }

    fprintf(stderr, "\nstarting read\n");

    for (int i = 0; i < skipLines;)
    {
        char c = fgetc(stdin);
        if (c == EOF)
            return 0;
        if (fputc(c, stdout) == EOF)
        {
            fprintf(stderr, "failed to write SKIP lines. is disk full?");
            return 1;
        }
        if (c == sep)
            i++;
    }
    fprintf(stderr, "skipped %d lines\n", skipLines);

    ll totalBytesRead = 0, totalLinesRead = 0;
    std::vector<TmpFile> files;
    bool reachedEof = false;
    while (!reachedEof)
    {
        reachedEof = fillBufAndMarkLines(stdin, buf, bufBytes, shufIndexes, sep, memory);
        TmpFile tmpFile;
        // default output is stdout. if input fit in buffer, no need to use
        // temp files, just write to stdout.
        tmpFile.f = stdout;
        if (!reachedEof || files.size() > 0)
        {
            // haven't reached eof so have to call fillBuf again.
            // or we did did reach eof but there is already a temp file,
            // so we are already in temp file mode, not direct to stdout.
            tmpFile.path = (char *)malloc((strlen(tmpNameTemplate) + 1) * sizeof(char));
            strcpy(tmpFile.path, tmpNameTemplate);
            int fd = mkstemp(tmpFile.path);
            if (fd == -1)
            {
                fprintf(stderr, "failed to create fd tmp file %s\n",
                        tmpNameTemplate);
                return 1;
            }
            tmpFile.f = fdopen(fd, "wb+");
            if (tmpFile.f == NULL)
            {
                fprintf(stderr, "failed to create tmp file %s\n",
                        tmpFile.path);
                return 1;
            }
        }
        totalBytesRead += shufFlushBuf(buf, shufIndexes, rng, sep, tmpFile.f);
        totalLinesRead += shufIndexes.size();
        tmpFile.lines = shufIndexes.size();
        fprintf(stderr, "\rlines read: %zu, gb read: %zu", totalLinesRead,
                totalBytesRead / (1024 * 1024 * 1024));
        files.push_back(tmpFile);
    }

    // inform totalLines and totalBytesRead
    fprintf(stderr, "\nRead %zu lines, %zu bytes, have %d tmp files\n",
            totalLinesRead, totalBytesRead, (int)files.size());

    if (files.size() == 1)
    {
        fprintf(
            stderr,
            "\nShuffled directly to stdout without using tmpfiles, done!\n");
        return 0;
    }

    std::vector<ll> linesRemainingPerFile;

    for (auto &file : files)
    {
        rewind(file.f);
        // for bufferedFgetc
        file.buf = (char *)malloc(IO_CHUNK);
        file.bufPos = 0;
        file.bufN = 0;
        file.eof = false;
        linesRemainingPerFile.push_back(file.lines);
    }

    auto fenwickTree = FenwickTree(linesRemainingPerFile);

    ll totalBytesWritten = 0, linesRemaining = totalLinesRead, totalBytesWrittenForProgress = 0;

    fprintf(stderr, "\nstarting write to output\n");

    while (linesRemaining)
    {
        ll randLine = std::uniform_int_distribution<ll>{0, linesRemaining - 1}(rng);
        auto fileIdx = fenwickTree.findIndexAndDraw(randLine + 1);
        auto linesRemainingInFile = fenwickTree.getCountAtIndex(fileIdx);
        linesRemaining--;
        TmpFile &file = files[fileIdx];
        ll bytesRead = readLine(buf, sep, file);
        if (!linesRemainingInFile)
        {
            fclose(file.f);
            unlink(file.path);
        }
        ll bytesWritten = fwrite(buf, sizeof(char), bytesRead, stdout);
        if (bytesRead != bytesWritten)
        {
            fprintf(stderr, "\nFATAL ERROR: failed to write line to disk. is there space left?\n");
            return 1;
        }
        totalBytesWritten += bytesWritten;
        totalBytesWrittenForProgress += bytesWritten;
        // progress report
        if (!linesRemaining || totalBytesWrittenForProgress >= bufBytes)
        {
            fprintf(stderr, "\rlines written: %zu, gb written: %zu",
                    totalLinesRead - linesRemaining, totalBytesWritten / (1024 * 1024 * 1024));
            totalBytesWrittenForProgress = 0;
        }
    }
    fprintf(stderr, "\ndone\n");
    return 0;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xaa8, %rsp            # imm = 0xAA8
leaq 0x1fa5(%rip), %rdi      # 0x31dd
callq 0x1140
movb $0xa, %bpl
testq %rax, %rax
je 0x1250
movzbl (%rax), %eax
testb %al, %al
pushq $0xa
popq %rbp
cmovnel %eax, %ebp
leaq 0x1f8a(%rip), %rdi      # 0x31e1
callq 0x1140
testq %rax, %rax
je 0x1266
cmpb $0x0, (%rax)
jne 0x126f
xorl %edi, %edi
callq 0x10d0
jmp 0x127c
pushq $0xa
popq %rdx
movq %rax, %rdi
xorl %esi, %esi
callq 0x1180
movslq %eax, %rsi
movq %rsi, 0xe0(%rsp)
pushq $0x1
popq %rax
movabsq $0x5851f42d4c957f2d, %rcx # imm = 0x5851F42D4C957F2D
movq %rsi, %rdx
shrq $0x3e, %rdx
xorq %rsi, %rdx
imulq %rcx, %rdx
addq %rax, %rdx
movq %rdx, 0xe0(%rsp,%rax,8)
incq %rax
movq %rdx, %rsi
cmpq $0x138, %rax            # imm = 0x138
jne 0x1297
movq $0x138, 0xaa0(%rsp) # imm = 0x138
leaq 0x1f18(%rip), %rdi      # 0x31e6
callq 0x1140
xorl %r15d, %r15d
testq %rax, %rax
je 0x12f0
cmpb $0x0, (%rax)
je 0x12f0
pushq $0xa
popq %rdx
movq %rax, %rdi
xorl %esi, %esi
callq 0x1180
movq %rax, %r15
leaq 0x1ef4(%rip), %rdi      # 0x31eb
callq 0x1140
testq %rax, %rax
leaq 0x1eec(%rip), %rbx      # 0x31f2
cmovneq %rax, %rbx
movq %rbx, %rdi
callq 0x1070
leaq 0x13(%rax), %rdi
callq 0x1160
movq %rax, %r14
movq %rax, %rdi
movq %rbx, %rsi
callq 0x10e0
movq %r14, %rdi
callq 0x1070
movaps 0x20f8(%rip), %xmm0     # 0x3430
movups %xmm0, (%r14,%rax)
movq %r14, 0x10(%rsp)
movl $0x585858, 0xf(%r14,%rax) # imm = 0x585858
leaq 0x1ea5(%rip), %rdi      # 0x31f7
callq 0x1140
movss 0x1ca5(%rip), %xmm0     # 0x3004
movss %xmm0, 0x18(%rsp)
testq %rax, %rax
je 0x137f
cmpb $0x0, (%rax)
je 0x137f
movq %rax, %rdi
xorl %esi, %esi
callq 0x1170
movss %xmm0, 0x18(%rsp)
xorps %xmm0, %xmm0
andq $0x0, 0x80(%rsp)
movaps %xmm0, 0x70(%rsp)
cvtss2sd 0x18(%rsp), %xmm0
movsd 0x1c6a(%rip), %xmm1     # 0x3008
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
mulsd %xmm1, %xmm0
cvttsd2si %xmm0, %rax
movq %rax, %rcx
sarq $0x3f, %rcx
subsd 0x1c52(%rip), %xmm0     # 0x3010
cvttsd2si %xmm0, %rbx
andq %rcx, %rbx
orq %rax, %rbx
movq %rbx, %rdi
callq 0x1160
movq %rax, %r12
movq 0x3bfd(%rip), %r14      # 0x4fd8
movq (%r14), %rdi
leaq 0x1e19(%rip), %rsi      # 0x31fe
movq %rbx, 0x90(%rsp)
movq %rbx, %rdx
xorl %eax, %eax
callq 0x1190
movq (%r14), %rcx
movq %r12, 0x40(%rsp)
testq %r12, %r12
jne 0x141e
leaq 0x1e11(%rip), %rdi      # 0x321c
pushq $0x1e
popq %rsi
pushq $0x1
popq %rdx
callq 0x1200
pushq $0x1
popq %rbp
jmp 0x16a3
leaq 0x1e16(%rip), %rdi      # 0x323b
pushq $0xf
popq %rsi
pushq $0x1
popq %rdx
callq 0x1200
movl %ebp, 0x4c(%rsp)
movsbl %bpl, %ebp
testl %r15d, %r15d
jle 0x1490
movq 0x3b8c(%rip), %rbx      # 0x4fd0
movq 0x3b6d(%rip), %r12      # 0x4fb8
xorl %r14d, %r14d
movq (%rbx), %rdi
callq 0x11c0
movl %eax, %ecx
shll $0x18, %ecx
cmpl $0xff000000, %ecx       # imm = 0xFF000000
je 0x16a1
movsbl %al, %r13d
movq (%r12), %rsi
movl %r13d, %edi
callq 0x1150
cmpl $-0x1, %eax
je 0x16b7
xorl %eax, %eax
cmpl %ebp, %r13d
sete %al
addl %eax, %r14d
cmpl %r15d, %r14d
jl 0x144e
movq 0x3b41(%rip), %r14      # 0x4fd8
movq (%r14), %rdi
leaq 0x1dd4(%rip), %rsi      # 0x3275
movl %r15d, %edx
xorl %eax, %eax
callq 0x1190
xorpd %xmm0, %xmm0
leaq 0x20(%rsp), %rax
movapd %xmm0, (%rax)
andq $0x0, 0x10(%rax)
leaq 0x70(%rsp), %r15
xorl %ebx, %ebx
xorl %r12d, %r12d
movq 0x3b02(%rip), %rax      # 0x4fd0
movq (%rax), %rdi
movq 0x40(%rsp), %rsi
movq 0x90(%rsp), %rdx
movq %r15, %rcx
movl %ebp, %r8d
movss 0x18(%rsp), %xmm0
callq 0x1da8
movl %eax, %r13d
movq 0x3abf(%rip), %rax      # 0x4fb8
movq (%rax), %rax
movq %rax, 0x98(%rsp)
testb %r13b, %r13b
je 0x1515
movq 0x28(%rsp), %rcx
cmpq 0x20(%rsp), %rcx
je 0x158c
movb %r13b, 0xf(%rsp)
movq %r14, %r15
movq %rbx, %r14
movq 0x10(%rsp), %rbx
movq %rbx, %rdi
callq 0x1070
leaq 0x1(%rax), %rdi
callq 0x1160
movq %rax, %r13
movq %rax, 0xa0(%rsp)
movq %rax, %rdi
movq %rbx, %rsi
callq 0x10e0
movq %r13, %rdi
callq 0x1050
cmpl $-0x1, %eax
je 0x16cf
movl %eax, %edi
leaq 0x1d42(%rip), %rsi      # 0x32a8
callq 0x1120
movq %rax, 0x98(%rsp)
testq %rax, %rax
movq %r14, %rbx
movq %r15, %r14
leaq 0x70(%rsp), %r15
movb 0xf(%rsp), %r13b
je 0x16e0
movq 0x40(%rsp), %rdi
movq %r15, %rsi
leaq 0xe0(%rsp), %rdx
movl %ebp, %ecx
movq %rax, %r8
callq 0x1c7c
addq %rax, %rbx
movq 0x78(%rsp), %rax
subq 0x70(%rsp), %rax
sarq $0x3, %rax
addq %rax, %r12
movq %rax, 0xa8(%rsp)
movq (%r14), %rdi
movq %rbx, %rcx
shrq $0x1e, %rcx
leaq 0x1cf7(%rip), %rsi      # 0x32ca
movq %r12, %rdx
xorl %eax, %eax
callq 0x1190
movq 0x28(%rsp), %rsi
cmpq 0x30(%rsp), %rsi
jne 0x15fd
leaq 0x20(%rsp), %rdi
leaq 0x98(%rsp), %rdx
callq 0x2694
jmp 0x1639
movq 0xc8(%rsp), %rax
movq %rax, 0x30(%rsi)
movupd 0x98(%rsp), %xmm0
movupd 0xa8(%rsp), %xmm1
movups 0xb8(%rsp), %xmm2
movups %xmm2, 0x20(%rsi)
movupd %xmm1, 0x10(%rsi)
movupd %xmm0, (%rsi)
addq $0x38, %rsi
movq %rsi, 0x28(%rsp)
testb %r13b, %r13b
je 0x14c7
movq (%r14), %rdi
movq 0x28(%rsp), %rax
subq 0x20(%rsp), %rax
pushq $0x38
popq %rcx
cqto
idivq %rcx
leaq 0x1c8b(%rip), %rsi      # 0x32e9
xorl %ebp, %ebp
movq %r12, %rdx
movq %rbx, %rcx
movl %eax, %r8d
xorl %eax, %eax
callq 0x1190
movq 0x20(%rsp), %r15
movq 0x28(%rsp), %rbx
movq %rbx, %rax
subq %r15, %rax
cmpq $0x38, %rax
jne 0x1732
movq (%r14), %rcx
leaq 0x1c84(%rip), %rdi      # 0x3318
pushq $0x3b
popq %rsi
pushq $0x1
popq %rdx
callq 0x1200
jmp 0x16fc
xorl %ebp, %ebp
movl %ebp, %eax
addq $0xaa8, %rsp            # imm = 0xAA8
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x391a(%rip), %rax      # 0x4fd8
movq (%rax), %rcx
leaq 0x1b83(%rip), %rdi      # 0x324b
pushq $0x29
jmp 0x140d
movq (%r15), %rdi
leaq 0x1bae(%rip), %rsi      # 0x3287
movq 0x10(%rsp), %rdx
jmp 0x16f2
movq (%r14), %rdi
movq 0xa0(%rsp), %rdx
leaq 0x1bba(%rip), %rsi      # 0x32ac
xorl %eax, %eax
callq 0x1190
pushq $0x1
popq %rbp
movq 0x20(%rsp), %rdi
testq %rdi, %rdi
je 0x1713
movq 0x30(%rsp), %rsi
subq %rdi, %rsi
callq 0x1110
movq 0x70(%rsp), %rdi
testq %rdi, %rdi
je 0x16a3
movq 0x80(%rsp), %rsi
subq %rdi, %rsi
callq 0x1110
jmp 0x16a3
movq %r12, 0x10(%rsp)
xorpd %xmm0, %xmm0
movapd %xmm0, 0x50(%rsp)
andq $0x0, 0x60(%rsp)
cmpq %rbx, %r15
movl 0x4c(%rsp), %r12d
je 0x17b9
addq $0x10, %r15
leaq 0x50(%rsp), %r13
movq -0x10(%r15), %rdi
callq 0x1030
movl $0x10000, %edi          # imm = 0x10000
callq 0x1160
movq %rax, 0x8(%r15)
xorpd %xmm0, %xmm0
movupd %xmm0, 0x10(%r15)
movb $0x0, 0x20(%r15)
movq 0x58(%rsp), %rsi
cmpq 0x60(%rsp), %rsi
je 0x179d
movq (%r15), %rax
movq %rax, (%rsi)
addq $0x8, %rsi
movq %rsi, 0x58(%rsp)
jmp 0x17a8
movq %r13, %rdi
movq %r15, %rdx
callq 0x2574
leaq -0x10(%r15), %rax
addq $0x38, %r15
addq $0x38, %rax
cmpq %rbx, %rax
jne 0x175a
leaq 0x98(%rsp), %rdi
leaq 0x50(%rsp), %rsi
callq 0x20a2
movq 0x3806(%rip), %rax      # 0x4fd8
movq (%rax), %rcx
leaq 0x1b78(%rip), %rdi      # 0x3354
pushq $0x1a
popq %rsi
pushq $0x1
popq %rdx
callq 0x1200
cmpq $0x0, 0x10(%rsp)
jne 0x1812
movq 0x37e2(%rip), %rax      # 0x4fd8
movq (%rax), %rcx
leaq 0x1bd6(%rip), %rdi      # 0x33d6
pushq $0x6
popq %rsi
pushq $0x1
popq %rdx
callq 0x1200
xorl %ebp, %ebp
jmp 0x194e
xorl %eax, %eax
movq 0x10(%rsp), %r13
xorl %ebx, %ebx
movq %rax, 0x18(%rsp)
decq %r13
andq $0x0, 0xd0(%rsp)
movq %r13, 0xd8(%rsp)
leaq 0xd0(%rsp), %rdi
leaq 0xe0(%rsp), %rsi
movq %rdi, %rdx
callq 0x2372
incq %rax
leaq 0x98(%rsp), %rdi
movq %rax, %rsi
callq 0x2186
movslq 0xb4(%rsp), %rcx
cltq
addq %rax, %rcx
movq 0x98(%rsp), %rdx
movq (%rdx,%rcx,8), %r14
imulq $0x38, %rax, %rbp
addq 0x20(%rsp), %rbp
xorl %r15d, %r15d
movq %rbp, %rdi
callq 0x1d08
cmpb $-0x1, %al
je 0x18a3
movq 0x40(%rsp), %rcx
movb %al, (%rcx,%r15)
incq %r15
cmpb %r12b, %al
jne 0x1884
jmp 0x18a6
xorl %r15d, %r15d
testq %r14, %r14
jne 0x18bd
movq (%rbp), %rdi
callq 0x10f0
movq 0x8(%rbp), %rdi
callq 0x1080
movq 0x36f4(%rip), %rax      # 0x4fb8
movq (%rax), %rcx
movq 0x40(%rsp), %rdi
pushq $0x1
popq %rsi
movq %r15, %rdx
callq 0x1200
cmpq %rax, %r15
jne 0x192f
addq %r15, %rbx
testq %r13, %r13
movq 0x18(%rsp), %rax
je 0x1904
addq %r15, %rax
cmpq 0x90(%rsp), %rax
jae 0x1904
testq %r13, %r13
jne 0x181b
jmp 0x17ef
movq 0x36cd(%rip), %rax      # 0x4fd8
movq (%rax), %rdi
movq 0x10(%rsp), %rdx
subq %r13, %rdx
movq %rbx, %rcx
shrq $0x1e, %rcx
leaq 0x1a8d(%rip), %rsi      # 0x33b1
xorl %eax, %eax
callq 0x1190
xorl %eax, %eax
jmp 0x18f6
movq 0x36a2(%rip), %rax      # 0x4fd8
movq (%rax), %rcx
leaq 0x1a2f(%rip), %rdi      # 0x336f
pushq $0x41
popq %rsi
pushq $0x1
popq %rdx
callq 0x1200
pushq $0x1
popq %rbp
movq 0x98(%rsp), %rdi
testq %rdi, %rdi
je 0x196b
movq 0xa8(%rsp), %rsi
subq %rdi, %rsi
callq 0x1110
movq 0x50(%rsp), %rdi
testq %rdi, %rdi
je 0x16fc
movq 0x60(%rsp), %rsi
subq %rdi, %rsi
callq 0x1110
jmp 0x16fc
jmp 0x198d
movq %rax, %rbx
jmp 0x19b2
movq %rax, %rbx
movq 0x98(%rsp), %rdi
testq %rdi, %rdi
je 0x19b2
movq 0xa8(%rsp), %rsi
subq %rdi, %rsi
callq 0x1110
movq 0x50(%rsp), %rdi
testq %rdi, %rdi
je 0x19d2
movq 0x60(%rsp), %rsi
subq %rdi, %rsi
callq 0x1110
jmp 0x19d2
jmp 0x19cf
jmp 0x19cf
movq %rax, %rbx
movq 0x20(%rsp), %rdi
testq %rdi, %rdi
je 0x19e9
movq 0x30(%rsp), %rsi
subq %rdi, %rsi
callq 0x1110
movq 0x70(%rsp), %rdi
testq %rdi, %rdi
je 0x1a03
movq 0x80(%rsp), %rsi
subq %rdi, %rsi
callq 0x1110
movq %rbx, %rdi
callq 0x11e0