// File: /workspace/github_cpp_makefile_star2_O3/Eshaancoding[P]AutoDiff/src/Variable/grad.cpp
// Function: Variable::grad(op, double, bool, double, bool)
vector<double> Variable::grad (op last_operation, double ERROR_NODE, bool top_node, double other_value, bool left) {
    static vector<double> GRAD_OUTPUT = {};
    static vector<int> ID_ARRAY = {};
    static vector<int> input_index = {};
    static int INDEX = 0;
    double error = ERROR_NODE;

    if (top_node) {
        this->eval(); // get values
        GRAD_OUTPUT.clear();
        ID_ARRAY.clear();
        input_index.clear();
        INDEX = 0;
        // we already computed error_node (1), pass to sub nodes
    } 
    else {
        // compute error_node
        error *= this->dir_eval(this->value, last_operation, other_value, left);
    }
    // pass error node to sub nodes and execute
    if (this->operation == op::clip) {
        if (this->arg->value >= this->child[0]->value && 
            this->arg->value <= this->child[1]->value) {
            // in range of clip
            this->arg->grad(this->operation, error, false);    
        } else {
            this->arg->grad(this->operation, 0, false);
        }
    }
    else if (this->operation == op::min) {
        // determine what child
        if (this->child[0]->value >= this->child[1]->value) {
            this->child[0]->grad(this->operation, 0, false, this->child[1]->value, false);
            this->child[1]->grad(this->operation, error, false, this->child[0]->value, true); 
        } else {
            this->child[0]->grad(this->operation, error, false, this->child[1]->value, false); 
            this->child[1]->grad(this->operation, 0, false, this->child[0]->value, true); 
        }
    } 
    else if (this->operation == op::max) {
        if (this->child[0]->value >= this->child[1]->value) {
            this->child[0]->grad(this->operation, error, false, this->child[1]->value, true); 
            this->child[1]->grad(this->operation, 0, false, this->child[0]->value, false); 
        } else {
            this->child[0]->grad(this->operation, 0, false, this->child[1]->value, true); 
            this->child[1]->grad(this->operation, error, false, this->child[0]->value, false); 
        }
    }
    else if (this->child[0] != NULL) {
        if (this->child[1] != NULL) {
            // operation is a two parameter function (e.i plus, multiply, subtract, etc...) 
            this->child[0]->grad(this->operation, error, false, this->child[1]->value, true);
            this->child[1]->grad(this->operation, error, false, this->child[0]->value, false);
        } else {
            // operation is a one parameter function (e.i sin, cos, atan)
            this->child[0]->grad(this->operation, error, false);
        }
    } else if (this->operation == op::input) {
        // append to grad_output
        bool find = false;
        for (int i = 0; i < ID_ARRAY.size(); i++) {
            if (this->id == ID_ARRAY[i]) {
                find = true;
                GRAD_OUTPUT[input_index[i]] += error;
                break;
            }
        }
        if (!find) {
            input_index.push_back(INDEX); 
            ID_ARRAY.push_back(this->id);                
            INDEX++;
            GRAD_OUTPUT.push_back(error);
        }
    }
    if (top_node) {
        return GRAD_OUTPUT;
    } else {
        return {};
    }
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x158, %rsp            # imm = 0x158
movl %r8d, %r12d
movl %ecx, %r14d
movl %edx, %r13d
movq %rsi, %r15
movq %rdi, %rbx
movb 0xa761(%rip), %al       # 0xf1e8
testb %al, %al
movsd %xmm0, -0x30(%rbp)
je 0x4f53
movb 0xa76e(%rip), %al       # 0xf208
testb %al, %al
je 0x4f6c
movb 0xa780(%rip), %al       # 0xf228
testb %al, %al
je 0x4f85
movsd %xmm0, -0x40(%rbp)
testb %r14b, %r14b
je 0x4b1d
movq %r15, %rdi
movl $0x1, %esi
callq 0x427e
movq 0xa702(%rip), %rax      # 0xf1d0
cmpq %rax, 0xa703(%rip)      # 0xf1d8
je 0x4ade
movq %rax, 0xa6fa(%rip)      # 0xf1d8
movq 0xa70b(%rip), %rax      # 0xf1f0
cmpq %rax, 0xa70c(%rip)      # 0xf1f8
je 0x4af5
movq %rax, 0xa703(%rip)      # 0xf1f8
movq 0xa714(%rip), %rax      # 0xf210
cmpq %rax, 0xa715(%rip)      # 0xf218
movsd -0x30(%rbp), %xmm0
je 0x4b11
movq %rax, 0xa707(%rip)      # 0xf218
movl $0x0, 0xa715(%rip)      # 0xf230
jmp 0x4b40
movsd (%r15), %xmm0
movzbl %r12b, %edx
movl %r13d, %esi
callq 0x48ae
movsd -0x30(%rbp), %xmm1
mulsd %xmm0, %xmm1
movsd %xmm1, -0x40(%rbp)
movapd %xmm1, %xmm0
movl 0x8(%r15), %r12d
cmpl $0x8, %r12d
je 0x4c18
cmpl $0x9, %r12d
je 0x4baf
cmpl $0xa, %r12d
jne 0x4c7c
movq 0x10(%r15), %rax
movq 0x28(%r15), %r12
movsd (%r12), %xmm2
ucomisd (%rax), %xmm2
jb 0x4b84
movq 0x18(%r15), %rax
movsd (%rax), %xmm1
ucomisd %xmm2, %xmm1
jae 0x4df7
leaq 0x5747(%rip), %rdi      # 0xa2d2
callq 0x2050
movapd %xmm0, %xmm1
leaq -0x160(%rbp), %r15
xorpd %xmm0, %xmm0
movq %r15, %rdi
movq %r12, %rsi
movl $0xa, %edx
jmp 0x4e42
movq 0x10(%r15), %rsi
movq 0x18(%r15), %rax
movsd (%rsi), %xmm2
movsd (%rax), %xmm1
ucomisd %xmm1, %xmm2
jae 0x4ce8
movsd %xmm0, -0x30(%rbp)
leaq -0xb8(%rbp), %r12
xorpd %xmm0, %xmm0
movq %r12, %rdi
movl $0x9, %edx
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x4a5e
movq (%r12), %rdi
testq %rdi, %rdi
je 0x4bfc
callq 0x2170
movq 0x18(%r15), %rsi
movl 0x8(%r15), %edx
movq 0x10(%r15), %rax
movsd (%rax), %xmm1
leaq -0xa0(%rbp), %r15
jmp 0x4cde
movq 0x10(%r15), %rsi
movq 0x18(%r15), %rax
movsd (%rsi), %xmm2
movsd (%rax), %xmm1
ucomisd %xmm1, %xmm2
jae 0x4d3a
leaq -0x118(%rbp), %r12
movq %r12, %rdi
movl $0x8, %edx
xorl %ecx, %ecx
xorl %r8d, %r8d
callq 0x4a5e
movq (%r12), %rdi
testq %rdi, %rdi
je 0x4c59
callq 0x2170
movq 0x18(%r15), %rsi
movl 0x8(%r15), %edx
movq 0x10(%r15), %rax
movsd (%rax), %xmm1
leaq -0x100(%rbp), %r15
xorpd %xmm0, %xmm0
movq %r15, %rdi
jmp 0x4e42
movq 0x10(%r15), %r13
testq %r13, %r13
je 0x4d89
movq 0x18(%r15), %rax
testq %rax, %rax
movsd %xmm0, -0x30(%rbp)
je 0x4e20
movsd (%rax), %xmm1
leaq -0x58(%rbp), %rdi
movq %r13, %rsi
movl %r12d, %edx
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x4a5e
leaq -0x58(%rbp), %rax
movq (%rax), %rdi
testq %rdi, %rdi
je 0x4cc7
callq 0x2170
movq 0x18(%r15), %rsi
movl 0x8(%r15), %edx
movq 0x10(%r15), %rax
movsd (%rax), %xmm1
leaq -0x88(%rbp), %r15
movq %r15, %rdi
movsd -0x30(%rbp), %xmm0
jmp 0x4d30
leaq -0xe8(%rbp), %r12
movq %r12, %rdi
movl $0x9, %edx
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x4a5e
movq (%r12), %rdi
testq %rdi, %rdi
je 0x4d12
callq 0x2170
movq 0x18(%r15), %rsi
movl 0x8(%r15), %edx
movq 0x10(%r15), %rax
movsd (%rax), %xmm1
leaq -0xd0(%rbp), %r15
xorpd %xmm0, %xmm0
movq %r15, %rdi
xorl %ecx, %ecx
xorl %r8d, %r8d
jmp 0x4e4a
movsd %xmm0, -0x30(%rbp)
leaq -0x148(%rbp), %r12
xorpd %xmm0, %xmm0
movq %r12, %rdi
movl $0x8, %edx
xorl %ecx, %ecx
xorl %r8d, %r8d
callq 0x4a5e
movq (%r12), %rdi
testq %rdi, %rdi
je 0x4d6a
callq 0x2170
movq 0x18(%r15), %rsi
movl 0x8(%r15), %edx
movq 0x10(%r15), %rax
movsd (%rax), %xmm1
leaq -0x130(%rbp), %r15
movq %r15, %rdi
jmp 0x4e3d
testl %r12d, %r12d
jne 0x4e5c
movq 0xa45f(%rip), %rsi      # 0xf1f8
movq 0xa450(%rip), %rax      # 0xf1f0
movq %rsi, %rcx
subq %rax, %rcx
je 0x4dcb
sarq $0x2, %rcx
movl 0x20(%r15), %edx
cmpq $0x1, %rcx
adcq $0x0, %rcx
xorl %edi, %edi
cmpl (%rax,%rdi,4), %edx
je 0x4e97
incq %rdi
cmpq %rdi, %rcx
jne 0x4dba
movq 0xa446(%rip), %rax      # 0xf218
cmpq 0xa447(%rip), %rax      # 0xf220
je 0x4eb5
movl 0xa44b(%rip), %ecx      # 0xf230
movl %ecx, (%rax)
addq $0x4, %rax
movq %rax, 0xa426(%rip)      # 0xf218
jmp 0x4edc
leaq 0x54d4(%rip), %rdi      # 0xa2d2
movsd %xmm0, -0x30(%rbp)
callq 0x2050
movapd %xmm0, %xmm1
leaq -0x178(%rbp), %r15
movq %r15, %rdi
movq %r12, %rsi
movl $0xa, %edx
jmp 0x4e3d
leaq 0x54ab(%rip), %rdi      # 0xa2d2
callq 0x2050
movapd %xmm0, %xmm1
leaq -0x70(%rbp), %r15
movq %r15, %rdi
movq %r13, %rsi
movl %r12d, %edx
movsd -0x30(%rbp), %xmm0
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x4a5e
movq (%r15), %rdi
testq %rdi, %rdi
je 0x4e5c
callq 0x2170
testb %r14b, %r14b
je 0x4e72
leaq 0xa368(%rip), %rsi      # 0xf1d0
movq %rbx, %rdi
callq 0x4fb2
jmp 0x4e82
xorpd %xmm0, %xmm0
movupd %xmm0, (%rbx)
movq $0x0, 0x10(%rbx)
movq %rbx, %rax
addq $0x158, %rsp            # imm = 0x158
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0xa372(%rip), %rax      # 0xf210
movslq (%rax,%rdi,4), %rax
movq 0xa327(%rip), %rcx      # 0xf1d0
addsd (%rcx,%rax,8), %xmm0
movsd %xmm0, (%rcx,%rax,8)
jmp 0x4e5c
leaq 0xa354(%rip), %rdi      # 0xf210
leaq 0xa36d(%rip), %rdx      # 0xf230
movq %rax, %rsi
movsd %xmm0, -0x30(%rbp)
callq 0x4696
movsd -0x30(%rbp), %xmm0
movq 0xa31c(%rip), %rsi      # 0xf1f8
addq $0x20, %r15
cmpq 0xa319(%rip), %rsi      # 0xf200
je 0x4efb
movl (%r15), %eax
movl %eax, (%rsi)
addq $0x4, %rsi
movq %rsi, 0xa2ff(%rip)      # 0xf1f8
jmp 0x4f14
leaq 0xa2ee(%rip), %rdi      # 0xf1f0
movq %r15, %rdx
movsd %xmm0, -0x30(%rbp)
callq 0x4696
movsd -0x30(%rbp), %xmm0
incl 0xa316(%rip)            # 0xf230
movq 0xa2b7(%rip), %rsi      # 0xf1d8
cmpq 0xa2b8(%rip), %rsi      # 0xf1e0
je 0x4f3e
movsd %xmm0, (%rsi)
addq $0x8, %rsi
movq %rsi, 0xa29f(%rip)      # 0xf1d8
jmp 0x4e5c
leaq 0xa28b(%rip), %rdi      # 0xf1d0
leaq -0x40(%rbp), %rdx
callq 0x5032
jmp 0x4e5c
movsd %xmm1, -0x38(%rbp)
callq 0x2372
movsd -0x38(%rbp), %xmm1
movsd -0x30(%rbp), %xmm0
jmp 0x4a94
movsd %xmm1, -0x38(%rbp)
callq 0x23c1
movsd -0x38(%rbp), %xmm1
movsd -0x30(%rbp), %xmm0
jmp 0x4aa2
movsd %xmm1, -0x38(%rbp)
callq 0x2410
movsd -0x38(%rbp), %xmm1
movsd -0x30(%rbp), %xmm0
jmp 0x4ab0