// File: /workspace/github_cpp_makefile_star2_O2/shreyas-gopalakrishna[P]Concurrent-Skip-list/skip_list.cpp
// Function: SkipList::remove(int)
bool SkipList::remove(int key){
    // Initialization
    Node* victim = NULL;
    bool is_marked = false;
    int top_level = -1;

    // Initialization of references of the predecessors and successors
    vector<Node*> preds(max_level + 1); 
    vector<Node*> succs(max_level + 1);

    for (size_t i = 0; i < preds.size(); i++){
        preds[i] = NULL;
        succs[i] = NULL;
    }

    // Keep trying to delete the element from the list. In case predecessors and successors are changed,
    // this loop helps to try the delete again
    while(true){
        
        // Find the predecessors and successors of where the key to be deleted
        int found = find(key, preds, succs);

        // If found, select the node to delete. else return
        if(found != -1){
            victim = succs[found];
        }

        // If node not found and the node to be deleted is fully linked and not marked return
        if(is_marked | 
                (found != -1 &&
                (victim->fully_linked && victim->top_level == found && !(victim->marked))
                )
            ){
                // If not marked, the we lock the node and mark the node to delete
                if(!is_marked){
                    top_level = victim->top_level;
                    victim->lock();
                    if(victim->marked){
                        victim->unlock();
                        return false;
                    }
                    victim->marked = true;
                    is_marked = true;
                }

                // Store all the Nodes which lock we acquire in a map
                // Map used so that we don't try to acquire lock to a Node we have already acquired
                // This may happen when we have the same predecessor at different levels
                map<Node*, int> locked_nodes;

                // Traverse the skip list and try to acquire the lock of predecessor at every level
                try{
                    Node* pred;
                    //Node* succ;

                    // Used to check if the predecessors are not marked for delete and if the predecessor next is the node we are trying to delete or if it is changed.
                    bool valid = true;

                    for(int level = 0; valid && (level <= top_level); level++){
                        pred = preds[level];
                        
                        // If not already acquired lock, then acquire the lock 
                        if(!(locked_nodes.count( pred ))){
                            pred->lock();
                            locked_nodes.insert(make_pair(pred, 1));
                        }
                        
                        // If predecessor marked or if the predecessor's next has changed, then abort and try again
                        valid = !(pred->marked) && pred->next[level] == victim;
                    }

                    // Conditons are not met, release locks, abort and try again.
                    if(!valid){
                        for (auto const& x : locked_nodes){
                            x.first->unlock();
                        }
                        continue;
                    }

                    // All conditions satisfied, delete the Node and link them to the successors appropriately
                    for(int level = top_level; level >= 0; level--){
                        preds[level]->next[level] = victim->next[level];
                    }

                    victim->unlock();

                    // delete victim;

                    // Delete is completed, release the locks held.
                    for (auto const& x : locked_nodes){
                        x.first->unlock();
                    }

                    return true;
                }catch(const std::exception& e){
                    // If any exception occurs during the above delete, release locks of the held nodes and try again.
                    for (auto const& x : locked_nodes){
                        x.first->unlock();
                    }
                }

            }else{
                return false;
            }
    }
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x98, %rsp
movl %esi, %ebx
movq %rdi, 0x80(%rsp)
movslq 0x528e(%rip), %rsi      # 0xa2f4
incq %rsi
leaq 0x18(%rsp), %rdi
leaq 0x50(%rsp), %rdx
callq 0x567e
movslq 0x5275(%rip), %rsi      # 0xa2f4
incq %rsi
leaq 0x38(%rsp), %rdi
leaq 0x50(%rsp), %rdx
callq 0x567e
xorl %eax, %eax
movq 0x18(%rsp), %rcx
movq 0x20(%rsp), %rdx
subq %rcx, %rdx
sarq $0x3, %rdx
cmpq %rdx, %rax
jae 0x50bd
andq $0x0, (%rcx,%rax,8)
movq 0x38(%rsp), %rcx
andq $0x0, (%rcx,%rax,8)
incq %rax
jmp 0x5093
pushq $-0x1
popq %rbp
leaq 0x58(%rsp), %r15
xorl %r14d, %r14d
leaq 0x18(%rsp), %rdx
leaq 0x38(%rsp), %rcx
leaq 0x50(%rsp), %r13
xorl %r12d, %r12d
movl %ebx, 0x14(%rsp)
movq 0x80(%rsp), %rdi
movl %ebx, %esi
callq 0x4a3a
xorl %ecx, %ecx
cmpl $-0x1, %eax
je 0x511d
movl %eax, %edx
movq 0x38(%rsp), %rsi
movq (%rsi,%rdx,8), %r12
movb 0x69(%r12), %dl
testb $0x1, %dl
je 0x511d
cmpl %eax, 0x6c(%r12)
jne 0x511d
movb 0x68(%r12), %al
movzbl %al, %ecx
notl %ecx
andl $0x1, %ecx
movl %r14d, %eax
andb $0x1, %al
movzbl %al, %ebx
orl %ecx, %ebx
je 0x5323
testb $0x1, %r14b
jne 0x5157
movl 0x6c(%r12), %ebp
movq %r12, %rdi
callq 0x46ee
movb 0x68(%r12), %al
testb $0x1, %al
jne 0x531b
movb $0x1, %r14b
movb $0x1, %al
xchgb %al, 0x68(%r12)
movq %r14, 0x30(%rsp)
andl $0x0, 0x58(%rsp)
andq $0x0, 0x60(%rsp)
movq %r15, 0x68(%rsp)
movq %r15, 0x70(%rsp)
andq $0x0, 0x78(%rsp)
movl %ebp, 0x4(%rsp)
movslq %ebp, %r14
movb $0x1, %al
xorl %ebp, %ebp
testb $0x1, %al
je 0x5202
cmpq %r14, %rbp
jg 0x5202
movq 0x18(%rsp), %rax
movq (%rax,%rbp,8), %rax
movq %rax, 0x8(%rsp)
movq %r13, %rdi
leaq 0x8(%rsp), %rsi
callq 0x56d0
testq %rax, %rax
jne 0x51dd
movq 0x8(%rsp), %rdi
callq 0x46ee
movq 0x8(%rsp), %rax
movq %rax, 0x88(%rsp)
movl $0x1, 0x90(%rsp)
movq %r13, %rdi
leaq 0x88(%rsp), %rsi
callq 0x57ea
movq 0x8(%rsp), %rax
movb 0x68(%rax), %al
testb $0x1, %al
je 0x51ed
xorl %eax, %eax
jmp 0x51fd
movq 0x8(%rsp), %rax
movq 0x28(%rax), %rax
cmpq %r12, (%rax,%rbp,8)
sete %al
incq %rbp
jmp 0x5182
movl 0x4(%rsp), %ebp
movl %ebp, %ecx
testb $0x1, %al
movq 0x30(%rsp), %r14
je 0x5235
testl %ecx, %ecx
js 0x5259
movl %ecx, %eax
movq 0x28(%r12), %rdx
movq (%rdx,%rax,8), %rdx
movq 0x18(%rsp), %rsi
movq (%rsi,%rax,8), %rsi
movq 0x28(%rsi), %rsi
movq %rdx, (%rsi,%rax,8)
decl %ecx
jmp 0x5211
movq 0x68(%rsp), %rbx
cmpq %r15, %rbx
je 0x52ce
movq 0x20(%rbx), %rdi
callq 0x46f8
movq %rbx, %rdi
callq 0x2150
movq %rax, %rbx
jmp 0x523a
movq %r12, %rdi
callq 0x46f8
movq 0x68(%rsp), %r14
cmpq %r15, %r14
je 0x52e9
movq 0x20(%r14), %rdi
callq 0x46f8
movq %r14, %rdi
callq 0x2150
movq %rax, %r14
jmp 0x5266
jmp 0x5287
jmp 0x528c
jmp 0x5287
movq %rax, %rbx
jmp 0x5293
movq %rax, %rbx
movl 0x4(%rsp), %ebp
cmpl $0x1, %edx
movq 0x30(%rsp), %r14
jne 0x5334
movq %rbx, %rdi
callq 0x20a0
movq 0x68(%rsp), %rbx
cmpq %r15, %rbx
je 0x52c9
movq 0x20(%rbx), %rdi
callq 0x46f8
movq %rbx, %rdi
callq 0x2150
movq %rax, %rbx
jmp 0x52ae
callq 0x22e0
movq %r13, %rdi
callq 0x56ea
movl 0x14(%rsp), %ebx
leaq 0x18(%rsp), %rdx
leaq 0x38(%rsp), %rcx
jmp 0x50de
leaq 0x50(%rsp), %rdi
callq 0x56ea
leaq 0x38(%rsp), %rdi
callq 0x4778
leaq 0x18(%rsp), %rdi
callq 0x4778
movl %ebx, %eax
addq $0x98, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq %r12, %rdi
callq 0x46f8
xorl %ebx, %ebx
jmp 0x52f3
movq %rax, %rbx
jmp 0x5334
movq %rax, %rbx
callq 0x22e0
leaq 0x50(%rsp), %rdi
callq 0x56ea
jmp 0x5357
movq %rax, %rdi
callq 0x3bfe
jmp 0x5354
movq %rax, %rbx
jmp 0x5361
movq %rax, %rbx
jmp 0x536b
movq %rax, %rbx
leaq 0x38(%rsp), %rdi
callq 0x4778
leaq 0x18(%rsp), %rdi
callq 0x4778
movq %rbx, %rdi
callq 0x2310
nop