// File: 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
subq $0x1f0, %rsp            # imm = 0x1F0
movq %rdi, -0x198(%rbp)
movb %r8b, %al
movq %rdi, %r8
movq %r8, -0x190(%rbp)
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movl %edx, -0x14(%rbp)
movsd %xmm0, -0x20(%rbp)
andb $0x1, %cl
movb %cl, -0x21(%rbp)
movsd %xmm1, -0x30(%rbp)
andb $0x1, %al
movb %al, -0x31(%rbp)
movq -0x10(%rbp), %rax
movq %rax, -0x188(%rbp)
cmpb $0x0, 0xef07(%rip)      # 0x15218
jne 0x6369
leaq 0xeefe(%rip), %rdi      # 0x15218
callq 0x22a0
cmpl $0x0, %eax
je 0x6369
leaq 0xeed5(%rip), %rdi      # 0x15200
xorl %esi, %esi
movl $0x18, %edx
callq 0x20f0
leaq 0xeec2(%rip), %rdi      # 0x15200
callq 0x6bb0
leaq 0x886(%rip), %rdi       # 0x6bd0
leaq 0xeeaf(%rip), %rsi      # 0x15200
leaq 0xee38(%rip), %rdx      # 0x15190
callq 0x2180
leaq 0xeeb4(%rip), %rdi      # 0x15218
callq 0x2120
cmpb $0x0, 0xeec8(%rip)      # 0x15238
jne 0x63c8
leaq 0xeebf(%rip), %rdi      # 0x15238
callq 0x22a0
cmpl $0x0, %eax
je 0x63c8
leaq 0xee96(%rip), %rdi      # 0x15220
xorl %esi, %esi
movl $0x18, %edx
callq 0x20f0
leaq 0xee83(%rip), %rdi      # 0x15220
callq 0x52f0
leaq -0x1099(%rip), %rdi     # 0x5310
leaq 0xee70(%rip), %rsi      # 0x15220
leaq 0xedd9(%rip), %rdx      # 0x15190
callq 0x2180
leaq 0xee75(%rip), %rdi      # 0x15238
callq 0x2120
cmpb $0x0, 0xee89(%rip)      # 0x15258
jne 0x6427
leaq 0xee80(%rip), %rdi      # 0x15258
callq 0x22a0
cmpl $0x0, %eax
je 0x6427
leaq 0xee57(%rip), %rdi      # 0x15240
xorl %esi, %esi
movl $0x18, %edx
callq 0x20f0
leaq 0xee44(%rip), %rdi      # 0x15240
callq 0x52f0
leaq -0x10f8(%rip), %rdi     # 0x5310
leaq 0xee31(%rip), %rsi      # 0x15240
leaq 0xed7a(%rip), %rdx      # 0x15190
callq 0x2180
leaq 0xee36(%rip), %rdi      # 0x15258
callq 0x2120
movsd -0x20(%rbp), %xmm0
movsd %xmm0, -0x40(%rbp)
testb $0x1, -0x21(%rbp)
je 0x6478
movq -0x188(%rbp), %rdi
movl $0x1, %esi
callq 0x4ce0
leaq 0xedb1(%rip), %rdi      # 0x15200
callq 0x6c30
leaq 0xedc5(%rip), %rdi      # 0x15220
callq 0x5370
leaq 0xedd9(%rip), %rdi      # 0x15240
callq 0x5370
movl $0x0, 0xedea(%rip)      # 0x15260
jmp 0x64a2
movq -0x188(%rbp), %rdi
movsd (%rdi), %xmm0
movl -0x14(%rbp), %esi
movsd -0x30(%rbp), %xmm1
movb -0x31(%rbp), %al
andb $0x1, %al
movzbl %al, %edx
callq 0x5fd0
mulsd -0x40(%rbp), %xmm0
movsd %xmm0, -0x40(%rbp)
movq -0x188(%rbp), %rax
cmpl $0xa, 0x8(%rax)
jne 0x65ae
movq -0x188(%rbp), %rax
movq 0x28(%rax), %rcx
movsd (%rcx), %xmm0
movq 0x10(%rax), %rax
ucomisd (%rax), %xmm0
jb 0x6555
movq -0x188(%rbp), %rax
movq 0x28(%rax), %rcx
movsd (%rcx), %xmm1
movq 0x18(%rax), %rax
movsd (%rax), %xmm0
ucomisd %xmm1, %xmm0
jb 0x6555
movq -0x188(%rbp), %rax
movq 0x28(%rax), %rcx
movq %rcx, -0x1b0(%rbp)
movl 0x8(%rax), %eax
movl %eax, -0x1a4(%rbp)
movsd -0x40(%rbp), %xmm0
movsd %xmm0, -0x1a0(%rbp)
leaq 0x8d1a(%rip), %rdi      # 0xf236
callq 0x2050
movq -0x1b0(%rbp), %rsi
movl -0x1a4(%rbp), %edx
movaps %xmm0, %xmm1
movsd -0x1a0(%rbp), %xmm0
leaq -0x58(%rbp), %rdi
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x62c0
leaq -0x58(%rbp), %rdi
callq 0x6bd0
jmp 0x65a9
movq -0x188(%rbp), %rax
movq 0x28(%rax), %rcx
movq %rcx, -0x1c0(%rbp)
movl 0x8(%rax), %eax
movl %eax, -0x1b4(%rbp)
leaq 0x8cbf(%rip), %rdi      # 0xf236
callq 0x2050
movq -0x1c0(%rbp), %rsi
movl -0x1b4(%rbp), %edx
movaps %xmm0, %xmm1
leaq -0x70(%rbp), %rdi
xorps %xmm0, %xmm0
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x62c0
leaq -0x70(%rbp), %rdi
callq 0x6bd0
jmp 0x6a18
movq -0x188(%rbp), %rax
cmpl $0x8, 0x8(%rax)
jne 0x66c3
movq -0x188(%rbp), %rax
movq 0x10(%rax), %rcx
movsd (%rcx), %xmm0
movq 0x18(%rax), %rax
ucomisd (%rax), %xmm0
jb 0x664c
movq -0x188(%rbp), %rax
movq 0x10(%rax), %rsi
movl 0x8(%rax), %edx
movq 0x18(%rax), %rax
movsd (%rax), %xmm1
leaq -0x88(%rbp), %rdi
xorps %xmm0, %xmm0
xorl %r8d, %r8d
movl %r8d, %ecx
callq 0x62c0
leaq -0x88(%rbp), %rdi
callq 0x6bd0
movq -0x188(%rbp), %rax
movq 0x18(%rax), %rsi
movl 0x8(%rax), %edx
movsd -0x40(%rbp), %xmm0
movq 0x10(%rax), %rax
movsd (%rax), %xmm1
leaq -0xa0(%rbp), %rdi
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x62c0
leaq -0xa0(%rbp), %rdi
callq 0x6bd0
jmp 0x66be
movq -0x188(%rbp), %rax
movq 0x10(%rax), %rsi
movl 0x8(%rax), %edx
movsd -0x40(%rbp), %xmm0
movq 0x18(%rax), %rax
movsd (%rax), %xmm1
leaq -0xb8(%rbp), %rdi
xorl %r8d, %r8d
movl %r8d, %ecx
callq 0x62c0
leaq -0xb8(%rbp), %rdi
callq 0x6bd0
movq -0x188(%rbp), %rax
movq 0x18(%rax), %rsi
movl 0x8(%rax), %edx
movq 0x10(%rax), %rax
movsd (%rax), %xmm1
leaq -0xd0(%rbp), %rdi
xorps %xmm0, %xmm0
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x62c0
leaq -0xd0(%rbp), %rdi
callq 0x6bd0
jmp 0x6a16
movq -0x188(%rbp), %rax
cmpl $0x9, 0x8(%rax)
jne 0x67d8
movq -0x188(%rbp), %rax
movq 0x10(%rax), %rcx
movsd (%rcx), %xmm0
movq 0x18(%rax), %rax
ucomisd (%rax), %xmm0
jb 0x6761
movq -0x188(%rbp), %rax
movq 0x10(%rax), %rsi
movl 0x8(%rax), %edx
movsd -0x40(%rbp), %xmm0
movq 0x18(%rax), %rax
movsd (%rax), %xmm1
leaq -0xe8(%rbp), %rdi
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x62c0
leaq -0xe8(%rbp), %rdi
callq 0x6bd0
movq -0x188(%rbp), %rax
movq 0x18(%rax), %rsi
movl 0x8(%rax), %edx
movq 0x10(%rax), %rax
movsd (%rax), %xmm1
leaq -0x100(%rbp), %rdi
xorps %xmm0, %xmm0
xorl %r8d, %r8d
movl %r8d, %ecx
callq 0x62c0
leaq -0x100(%rbp), %rdi
callq 0x6bd0
jmp 0x67d3
movq -0x188(%rbp), %rax
movq 0x10(%rax), %rsi
movl 0x8(%rax), %edx
movq 0x18(%rax), %rax
movsd (%rax), %xmm1
leaq -0x118(%rbp), %rdi
xorps %xmm0, %xmm0
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x62c0
leaq -0x118(%rbp), %rdi
callq 0x6bd0
movq -0x188(%rbp), %rax
movq 0x18(%rax), %rsi
movl 0x8(%rax), %edx
movsd -0x40(%rbp), %xmm0
movq 0x10(%rax), %rax
movsd (%rax), %xmm1
leaq -0x130(%rbp), %rdi
xorl %r8d, %r8d
movl %r8d, %ecx
callq 0x62c0
leaq -0x130(%rbp), %rdi
callq 0x6bd0
jmp 0x6a14
movq -0x188(%rbp), %rax
cmpq $0x0, 0x10(%rax)
je 0x68df
movq -0x188(%rbp), %rax
cmpq $0x0, 0x18(%rax)
je 0x686e
movq -0x188(%rbp), %rax
movq 0x10(%rax), %rsi
movl 0x8(%rax), %edx
movsd -0x40(%rbp), %xmm0
movq 0x18(%rax), %rax
movsd (%rax), %xmm1
leaq -0x148(%rbp), %rdi
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x62c0
leaq -0x148(%rbp), %rdi
callq 0x6bd0
movq -0x188(%rbp), %rax
movq 0x18(%rax), %rsi
movl 0x8(%rax), %edx
movsd -0x40(%rbp), %xmm0
movq 0x10(%rax), %rax
movsd (%rax), %xmm1
leaq -0x160(%rbp), %rdi
xorl %r8d, %r8d
movl %r8d, %ecx
callq 0x62c0
leaq -0x160(%rbp), %rdi
callq 0x6bd0
jmp 0x68da
movq -0x188(%rbp), %rax
movq 0x10(%rax), %rcx
movq %rcx, -0x1d8(%rbp)
movl 0x8(%rax), %eax
movl %eax, -0x1cc(%rbp)
movsd -0x40(%rbp), %xmm0
movsd %xmm0, -0x1c8(%rbp)
leaq 0x8999(%rip), %rdi      # 0xf236
callq 0x2050
movq -0x1d8(%rbp), %rsi
movl -0x1cc(%rbp), %edx
movaps %xmm0, %xmm1
movsd -0x1c8(%rbp), %xmm0
leaq -0x178(%rbp), %rdi
xorl %ecx, %ecx
movl $0x1, %r8d
callq 0x62c0
leaq -0x178(%rbp), %rdi
callq 0x6bd0
jmp 0x6a12
movq -0x188(%rbp), %rax
cmpl $0x0, 0x8(%rax)
jne 0x6a10
movb $0x0, -0x179(%rbp)
movl $0x0, -0x180(%rbp)
movslq -0x180(%rbp), %rax
movq %rax, -0x1e0(%rbp)
leaq 0xe90a(%rip), %rdi      # 0x15220
callq 0x5bb0
movq %rax, %rcx
movq -0x1e0(%rbp), %rax
cmpq %rcx, %rax
jae 0x69bc
movq -0x188(%rbp), %rax
movl 0x20(%rax), %eax
movl %eax, -0x1e4(%rbp)
movslq -0x180(%rbp), %rsi
leaq 0xe8d4(%rip), %rdi      # 0x15220
callq 0x6c50
movq %rax, %rcx
movl -0x1e4(%rbp), %eax
cmpl (%rcx), %eax
jne 0x69a6
movb $0x1, -0x179(%rbp)
movsd -0x40(%rbp), %xmm0
movsd %xmm0, -0x1f0(%rbp)
movslq -0x180(%rbp), %rsi
leaq 0xe8c0(%rip), %rdi      # 0x15240
callq 0x6c50
movslq (%rax), %rsi
leaq 0xe871(%rip), %rdi      # 0x15200
callq 0x6c70
movsd -0x1f0(%rbp), %xmm0
addsd (%rax), %xmm0
movsd %xmm0, (%rax)
jmp 0x69bc
jmp 0x69a8
movl -0x180(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x180(%rbp)
jmp 0x6901
testb $0x1, -0x179(%rbp)
jne 0x6a0e
leaq 0xe874(%rip), %rdi      # 0x15240
leaq 0xe88d(%rip), %rsi      # 0x15260
callq 0x5470
movq -0x188(%rbp), %rsi
addq $0x20, %rsi
leaq 0xe836(%rip), %rdi      # 0x15220
callq 0x5470
movl 0xe86b(%rip), %eax      # 0x15260
addl $0x1, %eax
movl %eax, 0xe862(%rip)      # 0x15260
leaq 0xe7fb(%rip), %rdi      # 0x15200
leaq -0x40(%rbp), %rsi
callq 0x6c90
jmp 0x6a10
jmp 0x6a12
jmp 0x6a14
jmp 0x6a16
jmp 0x6a18
testb $0x1, -0x21(%rbp)
je 0x6a33
movq -0x198(%rbp), %rdi
leaq 0xe7d4(%rip), %rsi      # 0x15200
callq 0x6d00
jmp 0x6a52
movq -0x198(%rbp), %rdi
xorl %esi, %esi
movl $0x18, %edx
callq 0x20f0
movq -0x198(%rbp), %rdi
callq 0x6bb0
movq -0x190(%rbp), %rax
addq $0x1f0, %rsp            # imm = 0x1F0
popq %rbp
retq
nopw %cs:(%rax,%rax)
nopl (%rax)