// File: ashagraev[P]covariation/main.cpp
// Function: main
int main() {
    double interestingMeans[] = { 100000, 10000000 };

    for (const double mean : interestingMeans) {
        const double xMean = mean;
        const double yMean = mean;

        double xDiff = 1;
        double yDiff = 1;

        const double actualCovariation = xDiff * yDiff;

        std::vector<std::shared_ptr<ICovariationCalculator>> calculators;
        calculators.push_back(std::shared_ptr<TDummyCovariationCalculator>(new TDummyCovariationCalculator()));
        calculators.push_back(std::shared_ptr<TKahanCovariationCalculator>(new TKahanCovariationCalculator()));
        calculators.push_back(std::shared_ptr<TWelfordCovariationCalculator>(new TWelfordCovariationCalculator()));

        TPrinter printer("mean: " + std::to_string(mean));
        printer.AddColumn("Count");
        for (auto&& calculator : calculators) {
            printer.AddColumn(calculator->Name());
        }

        std::vector<double> maxErrors(calculators.size(), 0.);

        const size_t count = 10000000;
        for (size_t i = 0; i < count; ++i) {
            if (i && (i % (count / 100) == 0)) {
                printer.AddRow();
                printer.AddToRow(i);
                for (size_t calculatorIdx = 0; calculatorIdx < calculators.size(); ++calculatorIdx) {
                    const double calculatedCovariation = calculators[calculatorIdx]->Covariation();
                    const double error = Error(actualCovariation, calculatedCovariation) * 100;
                    printer.AddToRow(error);

                    maxErrors[calculatorIdx] = std::max(maxErrors[calculatorIdx], error);
                }
            }

            xDiff = -xDiff;
            yDiff = -yDiff;

            for (auto&& calculator : calculators) {
                calculator->Add(xMean + xDiff, yMean + yDiff);
            }
        }

        printer.AddRow();
        printer.AddToRow("MaxError");
        for (size_t i = 0; i < calculators.size(); ++i) {
            printer.AddToRow(maxErrors[i]);
        }

        printer.Print();
        printf("\n\n");
    }

    return 0;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x2d0, %rsp            # imm = 0x2D0
movl $0x0, -0x4(%rbp)
movq 0x5b17(%rip), %rax      # 0x8040
movq %rax, -0x20(%rbp)
movq 0x5b14(%rip), %rax      # 0x8048
movq %rax, -0x18(%rbp)
leaq -0x20(%rbp), %rax
movq %rax, -0x28(%rbp)
movq -0x28(%rbp), %rax
movq %rax, -0x30(%rbp)
movq -0x28(%rbp), %rax
addq $0x10, %rax
movq %rax, -0x38(%rbp)
movq -0x30(%rbp), %rax
cmpq -0x38(%rbp), %rax
je 0x2e6c
movq -0x30(%rbp), %rax
movsd (%rax), %xmm0
movsd %xmm0, -0x40(%rbp)
movsd -0x40(%rbp), %xmm0
movsd %xmm0, -0x48(%rbp)
movsd -0x40(%rbp), %xmm0
movsd %xmm0, -0x50(%rbp)
movabsq $0x3ff0000000000000, %rax # imm = 0x3FF0000000000000
movq %rax, -0x58(%rbp)
movq %rax, -0x60(%rbp)
movsd -0x58(%rbp), %xmm0
movsd -0x60(%rbp), %xmm1
mulsd %xmm1, %xmm0
movsd %xmm0, -0x68(%rbp)
leaq -0x80(%rbp), %rdi
callq 0x2e90
movl $0x40, %edi
callq 0x21d0
movq %rax, -0x268(%rbp)
jmp 0x25c4
movq -0x268(%rbp), %rdi
xorps %xmm0, %xmm0
movaps %xmm0, 0x30(%rdi)
movaps %xmm0, 0x20(%rdi)
movaps %xmm0, 0x10(%rdi)
movaps %xmm0, (%rdi)
callq 0x2ee0
movq -0x268(%rbp), %rsi
leaq -0xa0(%rbp), %rdi
callq 0x2f30
jmp 0x25f7
leaq -0x90(%rbp), %rdi
movq %rdi, -0x270(%rbp)
leaq -0xa0(%rbp), %rsi
callq 0x2f60
movq -0x270(%rbp), %rsi
leaq -0x80(%rbp), %rdi
callq 0x2eb0
jmp 0x2623
leaq -0x90(%rbp), %rdi
callq 0x2f90
leaq -0xa0(%rbp), %rdi
callq 0x2fb0
movl $0x40, %edi
callq 0x21d0
movq %rax, -0x278(%rbp)
jmp 0x264e
movq -0x278(%rbp), %rdi
xorps %xmm0, %xmm0
movaps %xmm0, 0x30(%rdi)
movaps %xmm0, 0x20(%rdi)
movaps %xmm0, 0x10(%rdi)
movaps %xmm0, (%rdi)
callq 0x2fd0
jmp 0x266e
movq -0x278(%rbp), %rsi
leaq -0xd0(%rbp), %rdi
callq 0x3040
jmp 0x2683
leaq -0xc0(%rbp), %rdi
movq %rdi, -0x280(%rbp)
leaq -0xd0(%rbp), %rsi
callq 0x3070
movq -0x280(%rbp), %rsi
leaq -0x80(%rbp), %rdi
callq 0x2eb0
jmp 0x26af
leaq -0xc0(%rbp), %rdi
callq 0x2f90
leaq -0xd0(%rbp), %rdi
callq 0x30a0
movl $0x28, %edi
callq 0x21d0
movq %rax, -0x288(%rbp)
jmp 0x26da
movq -0x288(%rbp), %rdi
xorps %xmm0, %xmm0
movaps %xmm0, 0x10(%rdi)
movaps %xmm0, (%rdi)
movq $0x0, 0x20(%rdi)
callq 0x30c0
movq -0x288(%rbp), %rsi
leaq -0xf0(%rbp), %rdi
callq 0x3120
jmp 0x270d
leaq -0xe0(%rbp), %rdi
movq %rdi, -0x290(%rbp)
leaq -0xf0(%rbp), %rsi
callq 0x3150
movq -0x290(%rbp), %rsi
leaq -0x80(%rbp), %rdi
callq 0x2eb0
jmp 0x2739
leaq -0xe0(%rbp), %rdi
callq 0x2f90
leaq -0xf0(%rbp), %rdi
callq 0x3180
movsd -0x40(%rbp), %xmm0
leaq -0x180(%rbp), %rdi
callq 0x31f0
jmp 0x2764
leaq 0x58f9(%rip), %rsi      # 0x8064
leaq -0x160(%rbp), %rdi
leaq -0x180(%rbp), %rdx
callq 0x31a0
jmp 0x2780
leaq -0x140(%rbp), %rdi
leaq -0x160(%rbp), %rsi
callq 0x3240
jmp 0x2795
leaq -0x160(%rbp), %rdi
callq 0x20f0
leaq -0x180(%rbp), %rdi
callq 0x20f0
leaq -0x1a1(%rbp), %rdi
movq %rdi, -0x298(%rbp)
callq 0x2270
movq -0x298(%rbp), %rdx
leaq 0x589d(%rip), %rsi      # 0x806b
leaq -0x1a0(%rbp), %rdi
callq 0x2220
jmp 0x27dc
leaq -0x140(%rbp), %rdi
leaq -0x1a0(%rbp), %rsi
callq 0x3290
jmp 0x27f1
leaq -0x1a0(%rbp), %rdi
callq 0x20f0
leaq -0x1a1(%rbp), %rdi
callq 0x21f0
leaq -0x80(%rbp), %rax
movq %rax, -0x1b0(%rbp)
movq -0x1b0(%rbp), %rdi
callq 0x32c0
movq %rax, -0x1b8(%rbp)
movq -0x1b0(%rbp), %rdi
callq 0x32f0
movq %rax, -0x1c0(%rbp)
leaq -0x1b8(%rbp), %rdi
leaq -0x1c0(%rbp), %rsi
callq 0x3320
testb $0x1, %al
jne 0x2856
jmp 0x2a40
leaq -0x1b8(%rbp), %rdi
callq 0x3360
movq %rax, -0x1c8(%rbp)
movq -0x1c8(%rbp), %rdi
callq 0x3380
movq %rax, %rsi
movq (%rsi), %rax
movq 0x10(%rax), %rax
leaq -0x1e8(%rbp), %rdi
callq *%rax
jmp 0x288a
leaq -0x140(%rbp), %rdi
leaq -0x1e8(%rbp), %rsi
callq 0x3290
jmp 0x289f
leaq -0x1e8(%rbp), %rdi
callq 0x20f0
leaq -0x1b8(%rbp), %rdi
callq 0x33a0
jmp 0x283a
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
jmp 0x2e61
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0x90(%rbp), %rdi
callq 0x2f90
leaq -0xa0(%rbp), %rdi
callq 0x2fb0
jmp 0x2e61
movq -0x278(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
callq 0x21a0
jmp 0x2e61
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0xc0(%rbp), %rdi
callq 0x2f90
leaq -0xd0(%rbp), %rdi
callq 0x30a0
jmp 0x2e61
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0xe0(%rbp), %rdi
callq 0x2f90
leaq -0xf0(%rbp), %rdi
callq 0x3180
jmp 0x2e61
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
jmp 0x29b2
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0x160(%rbp), %rdi
callq 0x20f0
leaq -0x180(%rbp), %rdi
callq 0x20f0
jmp 0x2e61
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
jmp 0x29f5
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0x1a0(%rbp), %rdi
callq 0x20f0
leaq -0x1a1(%rbp), %rdi
callq 0x21f0
jmp 0x2e55
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
jmp 0x2e55
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0x1e8(%rbp), %rdi
callq 0x20f0
jmp 0x2e55
leaq -0x80(%rbp), %rdi
callq 0x33c0
movq %rax, -0x2a8(%rbp)
movq $0x0, -0x208(%rbp)
leaq -0x209(%rbp), %rdi
movq %rdi, -0x2a0(%rbp)
callq 0x33e0
movq -0x2a8(%rbp), %rsi
movq -0x2a0(%rbp), %rcx
leaq -0x200(%rbp), %rdi
leaq -0x208(%rbp), %rdx
callq 0x3400
jmp 0x2a91
leaq -0x209(%rbp), %rdi
callq 0x3480
movq $0x989680, -0x218(%rbp) # imm = 0x989680
movq $0x0, -0x220(%rbp)
cmpq $0x989680, -0x220(%rbp) # imm = 0x989680
jae 0x2d77
cmpq $0x0, -0x220(%rbp)
je 0x2c7a
movq -0x220(%rbp), %rax
movl $0x186a0, %ecx          # imm = 0x186A0
xorl %edx, %edx
divq %rcx
cmpq $0x0, %rdx
jne 0x2c7a
leaq -0x140(%rbp), %rdi
callq 0x34a0
jmp 0x2afb
leaq -0x140(%rbp), %rdi
leaq -0x220(%rbp), %rsi
callq 0x3510
jmp 0x2b10
movq $0x0, -0x228(%rbp)
movq -0x228(%rbp), %rax
movq %rax, -0x2b0(%rbp)
leaq -0x80(%rbp), %rdi
callq 0x33c0
movq %rax, %rcx
movq -0x2b0(%rbp), %rax
cmpq %rcx, %rax
jae 0x2c78
movq -0x228(%rbp), %rsi
leaq -0x80(%rbp), %rdi
callq 0x3620
movq %rax, %rdi
callq 0x3380
movq %rax, %rdi
movq (%rdi), %rax
movq 0x8(%rax), %rax
callq *%rax
movsd %xmm0, -0x2b8(%rbp)
jmp 0x2b73
movsd -0x2b8(%rbp), %xmm0
movsd %xmm0, -0x230(%rbp)
movsd -0x68(%rbp), %xmm0
movsd -0x230(%rbp), %xmm1
callq 0x24d0
movsd 0x54b3(%rip), %xmm1     # 0x8050
mulsd %xmm1, %xmm0
movsd %xmm0, -0x238(%rbp)
leaq -0x140(%rbp), %rdi
leaq -0x238(%rbp), %rsi
callq 0x3640
jmp 0x2bbe
movq -0x228(%rbp), %rsi
leaq -0x200(%rbp), %rdi
callq 0x3790
movq %rax, %rdi
leaq -0x238(%rbp), %rsi
callq 0x3750
movq %rax, -0x2c0(%rbp)
jmp 0x2be9
movq -0x2c0(%rbp), %rax
movsd (%rax), %xmm0
movsd %xmm0, -0x2c8(%rbp)
movq -0x228(%rbp), %rsi
leaq -0x200(%rbp), %rdi
callq 0x3790
movsd -0x2c8(%rbp), %xmm0
movsd %xmm0, (%rax)
movq -0x228(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x228(%rbp)
jmp 0x2b1b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0x209(%rbp), %rdi
callq 0x3480
jmp 0x2e55
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xa8(%rbp)
movl %eax, -0xac(%rbp)
leaq -0x200(%rbp), %rdi
callq 0x3970
jmp 0x2e55
jmp 0x2c7a
movsd -0x58(%rbp), %xmm0
movq %xmm0, %rax
movabsq $-0x8000000000000000, %rcx # imm = 0x8000000000000000
xorq %rcx, %rax
movq %rax, %xmm0
movsd %xmm0, -0x58(%rbp)
movsd -0x60(%rbp), %xmm0
movq %xmm0, %rax
movabsq $-0x8000000000000000, %rcx # imm = 0x8000000000000000
xorq %rcx, %rax
movq %rax, %xmm0
movsd %xmm0, -0x60(%rbp)
leaq -0x80(%rbp), %rax
movq %rax, -0x240(%rbp)
movq -0x240(%rbp), %rdi
callq 0x32c0
movq %rax, -0x248(%rbp)
movq -0x240(%rbp), %rdi
callq 0x32f0
movq %rax, -0x250(%rbp)
leaq -0x248(%rbp), %rdi
leaq -0x250(%rbp), %rsi
callq 0x3320
testb $0x1, %al
jne 0x2d06
jmp 0x2d5e
leaq -0x248(%rbp), %rdi
callq 0x3360
movq %rax, -0x258(%rbp)
movq -0x258(%rbp), %rdi
callq 0x3380
movq %rax, %rdi
movsd -0x48(%rbp), %xmm0
movsd -0x58(%rbp), %xmm1
addsd %xmm1, %xmm0
movsd -0x50(%rbp), %xmm1
movsd -0x60(%rbp), %xmm2
addsd %xmm2, %xmm1
movq (%rdi), %rax
movq (%rax), %rax
callq *%rax
jmp 0x2d4e
jmp 0x2d50
leaq -0x248(%rbp), %rdi
callq 0x33a0
jmp 0x2ced
jmp 0x2d60
movq -0x220(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x220(%rbp)
jmp 0x2ab3
leaq -0x140(%rbp), %rdi
callq 0x34a0
jmp 0x2d85
leaq 0x52e5(%rip), %rsi      # 0x8071
leaq -0x140(%rbp), %rdi
callq 0x37b0
jmp 0x2d9a
movq $0x0, -0x260(%rbp)
movq -0x260(%rbp), %rax
movq %rax, -0x2d0(%rbp)
leaq -0x80(%rbp), %rdi
callq 0x33c0
movq %rax, %rcx
movq -0x2d0(%rbp), %rax
cmpq %rcx, %rax
jae 0x2e05
movq -0x260(%rbp), %rsi
leaq -0x200(%rbp), %rdi
callq 0x3790
movq %rax, %rsi
leaq -0x140(%rbp), %rdi
callq 0x3640
jmp 0x2def
jmp 0x2df1
movq -0x260(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x260(%rbp)
jmp 0x2da5
leaq -0x140(%rbp), %rdi
callq 0x38c0
jmp 0x2e13
leaq 0x5260(%rip), %rdi      # 0x807a
xorl %eax, %eax
callq 0x2030
jmp 0x2e23
leaq -0x200(%rbp), %rdi
callq 0x3970
leaq -0x140(%rbp), %rdi
callq 0x39d0
leaq -0x80(%rbp), %rdi
callq 0x3a10
movq -0x30(%rbp), %rax
addq $0x8, %rax
movq %rax, -0x30(%rbp)
jmp 0x2554
leaq -0x140(%rbp), %rdi
callq 0x39d0
leaq -0x80(%rbp), %rdi
callq 0x3a10
jmp 0x2e77
xorl %eax, %eax
addq $0x2d0, %rsp            # imm = 0x2D0
popq %rbp
retq
movq -0xa8(%rbp), %rdi
callq 0x2260
nopw %cs:(%rax,%rax)
nopl (%rax)