// File: sokolcom[P]tp-blockchain-task06/uint256_t/uint128_t.cpp
// Function: uint128_t::operator*(uint128_t const&) const
uint128_t uint128_t::operator*(const uint128_t & rhs) const{
    // split values into 4 32-bit parts
    uint64_t top[4] = {UPPER >> 32, UPPER & 0xffffffff, LOWER >> 32, LOWER & 0xffffffff};
    uint64_t bottom[4] = {rhs.UPPER >> 32, rhs.UPPER & 0xffffffff, rhs.LOWER >> 32, rhs.LOWER & 0xffffffff};
    uint64_t products[4][4];

    // multiply each component of the values
    for(int y = 3; y > -1; y--){
        for(int x = 3; x > -1; x--){
            products[3 - x][y] = top[x] * bottom[y];
        }
    }

    // first row
    uint64_t fourth32 = (products[0][3] & 0xffffffff);
    uint64_t third32  = (products[0][2] & 0xffffffff) + (products[0][3] >> 32);
    uint64_t second32 = (products[0][1] & 0xffffffff) + (products[0][2] >> 32);
    uint64_t first32  = (products[0][0] & 0xffffffff) + (products[0][1] >> 32);

    // second row
    third32  += (products[1][3] & 0xffffffff);
    second32 += (products[1][2] & 0xffffffff) + (products[1][3] >> 32);
    first32  += (products[1][1] & 0xffffffff) + (products[1][2] >> 32);

    // third row
    second32 += (products[2][3] & 0xffffffff);
    first32  += (products[2][2] & 0xffffffff) + (products[2][3] >> 32);

    // fourth row
    first32  += (products[3][3] & 0xffffffff);

    // move carry to next digit
    third32  += fourth32 >> 32;
    second32 += third32  >> 32;
    first32  += second32 >> 32;

    // remove carry from current digit
    fourth32 &= 0xffffffff;
    third32  &= 0xffffffff;
    second32 &= 0xffffffff;
    first32  &= 0xffffffff;

    // combine components
    return uint128_t((first32 << 32) | second32, (third32 << 32) | fourth32);
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x120, %rsp            # imm = 0x120
movq %rdi, -0x18(%rbp)
movq %rsi, -0x20(%rbp)
movq -0x18(%rbp), %rcx
movq 0x8(%rcx), %rax
shrq $0x20, %rax
movq %rax, -0x40(%rbp)
movabsq $0xffffffff, %rax       # imm = 0xFFFFFFFF
andq 0x8(%rcx), %rax
movq %rax, -0x38(%rbp)
movq (%rcx), %rax
shrq $0x20, %rax
movq %rax, -0x30(%rbp)
movabsq $0xffffffff, %rax       # imm = 0xFFFFFFFF
andq (%rcx), %rax
movq %rax, -0x28(%rbp)
movq -0x20(%rbp), %rax
movq 0x8(%rax), %rax
shrq $0x20, %rax
movq %rax, -0x60(%rbp)
movq -0x20(%rbp), %rcx
movabsq $0xffffffff, %rax       # imm = 0xFFFFFFFF
andq 0x8(%rcx), %rax
movq %rax, -0x58(%rbp)
movq -0x20(%rbp), %rax
movq (%rax), %rax
shrq $0x20, %rax
movq %rax, -0x50(%rbp)
movq -0x20(%rbp), %rcx
movabsq $0xffffffff, %rax       # imm = 0xFFFFFFFF
andq (%rcx), %rax
movq %rax, -0x48(%rbp)
movl $0x3, -0xe4(%rbp)
cmpl $-0x1, -0xe4(%rbp)
jle 0x45a5
movl $0x3, -0xe8(%rbp)
cmpl $-0x1, -0xe8(%rbp)
jle 0x4592
movslq -0xe8(%rbp), %rax
movq -0x40(%rbp,%rax,8), %rdx
movslq -0xe4(%rbp), %rax
imulq -0x60(%rbp,%rax,8), %rdx
movl $0x3, %eax
subl -0xe8(%rbp), %eax
movslq %eax, %rcx
leaq -0xe0(%rbp), %rax
shlq $0x5, %rcx
addq %rcx, %rax
movslq -0xe4(%rbp), %rcx
movq %rdx, (%rax,%rcx,8)
movl -0xe8(%rbp), %eax
addl $-0x1, %eax
movl %eax, -0xe8(%rbp)
jmp 0x4538
jmp 0x4594
movl -0xe4(%rbp), %eax
addl $-0x1, %eax
movl %eax, -0xe4(%rbp)
jmp 0x4525
movl -0xc8(%rbp), %eax
movq %rax, -0xf0(%rbp)
movl -0xd0(%rbp), %eax
movl -0xc4(%rbp), %ecx
addq %rcx, %rax
movq %rax, -0xf8(%rbp)
movl -0xd8(%rbp), %eax
movl -0xcc(%rbp), %ecx
addq %rcx, %rax
movq %rax, -0x100(%rbp)
movl -0xe0(%rbp), %eax
movl -0xd4(%rbp), %ecx
addq %rcx, %rax
movq %rax, -0x108(%rbp)
movl -0xa8(%rbp), %eax
movl %eax, %ecx
movq -0xf8(%rbp), %rax
addq %rcx, %rax
movq %rax, -0xf8(%rbp)
movl -0xb0(%rbp), %ecx
movl -0xa4(%rbp), %eax
addq %rax, %rcx
movq -0x100(%rbp), %rax
addq %rcx, %rax
movq %rax, -0x100(%rbp)
movl -0xb8(%rbp), %ecx
movl -0xac(%rbp), %eax
addq %rax, %rcx
movq -0x108(%rbp), %rax
addq %rcx, %rax
movq %rax, -0x108(%rbp)
movl -0x88(%rbp), %eax
movl %eax, %ecx
movq -0x100(%rbp), %rax
addq %rcx, %rax
movq %rax, -0x100(%rbp)
movl -0x90(%rbp), %ecx
movl -0x84(%rbp), %eax
addq %rax, %rcx
movq -0x108(%rbp), %rax
addq %rcx, %rax
movq %rax, -0x108(%rbp)
movl -0x68(%rbp), %eax
movl %eax, %ecx
movq -0x108(%rbp), %rax
addq %rcx, %rax
movq %rax, -0x108(%rbp)
movl -0xec(%rbp), %eax
movl %eax, %ecx
movq -0xf8(%rbp), %rax
addq %rcx, %rax
movq %rax, -0xf8(%rbp)
movl -0xf4(%rbp), %eax
movl %eax, %ecx
movq -0x100(%rbp), %rax
addq %rcx, %rax
movq %rax, -0x100(%rbp)
movl -0xfc(%rbp), %eax
movl %eax, %ecx
movq -0x108(%rbp), %rax
addq %rcx, %rax
movq %rax, -0x108(%rbp)
movl $0x0, -0xec(%rbp)
movl $0x0, -0xf4(%rbp)
movl $0x0, -0xfc(%rbp)
movl $0x0, -0x104(%rbp)
movq -0x108(%rbp), %rax
shlq $0x20, %rax
movq -0x100(%rbp), %rcx
orq %rcx, %rax
movq %rax, -0x110(%rbp)
movq -0xf8(%rbp), %rax
shlq $0x20, %rax
movq -0xf0(%rbp), %rcx
orq %rcx, %rax
movq %rax, -0x118(%rbp)
leaq -0x10(%rbp), %rdi
leaq -0x110(%rbp), %rsi
leaq -0x118(%rbp), %rdx
callq 0x5920
movq -0x10(%rbp), %rax
movq -0x8(%rbp), %rdx
addq $0x120, %rsp            # imm = 0x120
popq %rbp
retq
nop