// File: 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
movq %rsp, %rbp
subq $0x1f0, %rsp            # imm = 0x1F0
movq %rdi, -0x10(%rbp)
movl %esi, -0x14(%rbp)
movq -0x10(%rbp), %rax
movq %rax, -0x188(%rbp)
movq $0x0, -0x20(%rbp)
movb $0x0, -0x21(%rbp)
movl $0xffffffff, -0x28(%rbp) # imm = 0xFFFFFFFF
movl 0xb5ae(%rip), %eax      # 0x17364
incl %eax
cltq
movq %rax, -0x180(%rbp)
leaq -0x41(%rbp), %rdi
movq %rdi, -0x178(%rbp)
callq 0xa2d0
movq -0x180(%rbp), %rsi
movq -0x178(%rbp), %rdx
leaq -0x40(%rbp), %rdi
callq 0xcaa0
jmp 0xbdea
leaq -0x41(%rbp), %rdi
callq 0xa510
movl 0xb56b(%rip), %eax      # 0x17364
incl %eax
cltq
movq %rax, -0x198(%rbp)
leaq -0x71(%rbp), %rdi
movq %rdi, -0x190(%rbp)
callq 0xa2d0
movq -0x198(%rbp), %rsi
movq -0x190(%rbp), %rdx
leaq -0x70(%rbp), %rdi
callq 0xcaa0
jmp 0xbe2d
leaq -0x71(%rbp), %rdi
callq 0xa510
movq $0x0, -0x80(%rbp)
movq -0x80(%rbp), %rax
movq %rax, -0x1a0(%rbp)
leaq -0x40(%rbp), %rdi
callq 0xa190
movq %rax, %rcx
movq -0x1a0(%rbp), %rax
cmpq %rcx, %rax
jae 0xbecb
movq -0x80(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xa1b0
movq $0x0, (%rax)
movq -0x80(%rbp), %rsi
leaq -0x70(%rbp), %rdi
callq 0xa1b0
movq $0x0, (%rax)
movq -0x80(%rbp), %rax
addq $0x1, %rax
movq %rax, -0x80(%rbp)
jmp 0xbe3e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
leaq -0x41(%rbp), %rdi
callq 0xa510
jmp 0xc4e1
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
leaq -0x71(%rbp), %rdi
callq 0xa510
jmp 0xc4d8
jmp 0xbecd
jmp 0xbecf
movq -0x188(%rbp), %rdi
movl -0x14(%rbp), %esi
leaq -0x40(%rbp), %rdx
leaq -0x70(%rbp), %rcx
callq 0xaf90
movl %eax, -0x1a4(%rbp)
jmp 0xbeee
movl -0x1a4(%rbp), %eax
movl %eax, -0x84(%rbp)
cmpl $-0x1, -0x84(%rbp)
je 0xbf2d
movslq -0x84(%rbp), %rsi
leaq -0x70(%rbp), %rdi
callq 0xa1b0
movq (%rax), %rax
movq %rax, -0x20(%rbp)
jmp 0xbf2d
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
jmp 0xc4cf
movb -0x21(%rbp), %al
andb $0x1, %al
movzbl %al, %eax
movl %eax, -0x1ac(%rbp)
xorl %eax, %eax
cmpl $-0x1, -0x84(%rbp)
movb %al, -0x1a5(%rbp)
je 0xbfa2
movq -0x20(%rbp), %rdi
addq $0x69, %rdi
callq 0xcb10
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, -0x1ad(%rbp)
jne 0xbf6a
jmp 0xbf96
movq -0x20(%rbp), %rax
movl 0x6c(%rax), %ecx
xorl %eax, %eax
cmpl -0x84(%rbp), %ecx
movb %al, -0x1ad(%rbp)
jne 0xbf96
movq -0x20(%rbp), %rdi
addq $0x68, %rdi
callq 0xcb10
xorb $-0x1, %al
movb %al, -0x1ad(%rbp)
movb -0x1ad(%rbp), %al
movb %al, -0x1a5(%rbp)
movl -0x1ac(%rbp), %eax
movb -0x1a5(%rbp), %cl
andb $0x1, %cl
movzbl %cl, %ecx
orl %ecx, %eax
cmpl $0x0, %eax
je 0xc49a
testb $0x1, -0x21(%rbp)
jne 0xc021
movq -0x20(%rbp), %rax
movl 0x6c(%rax), %eax
movl %eax, -0x28(%rbp)
movq -0x20(%rbp), %rdi
callq 0x9f50
jmp 0xbfda
movq -0x20(%rbp), %rdi
addq $0x68, %rdi
callq 0xcb10
testb $0x1, %al
jne 0xbfed
jmp 0xc00b
movq -0x20(%rbp), %rdi
callq 0x9f70
jmp 0xbff8
movb $0x0, -0x1(%rbp)
movl $0x1, -0x88(%rbp)
jmp 0xc4af
movq -0x20(%rbp), %rdi
addq $0x68, %rdi
movl $0x1, %esi
callq 0xce00
movb $0x1, -0x21(%rbp)
leaq -0xb8(%rbp), %rdi
callq 0xcba0
movb $0x1, -0xc1(%rbp)
movl $0x0, -0xc8(%rbp)
xorl %eax, %eax
testb $0x1, -0xc1(%rbp)
movb %al, -0x1ae(%rbp)
je 0xc061
movl -0xc8(%rbp), %eax
cmpl -0x28(%rbp), %eax
setle %al
movb %al, -0x1ae(%rbp)
movb -0x1ae(%rbp), %al
testb $0x1, %al
jne 0xc070
jmp 0xc27e
movslq -0xc8(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xa1b0
movq (%rax), %rax
movq %rax, -0xc0(%rbp)
leaq -0xb8(%rbp), %rdi
leaq -0xc0(%rbp), %rsi
callq 0xcbc0
movq %rax, -0x1b8(%rbp)
jmp 0xc0a6
movq -0x1b8(%rbp), %rax
cmpq $0x0, %rax
jne 0xc216
movq -0xc0(%rbp), %rdi
callq 0x9f50
jmp 0xc0c5
movl $0x1, -0xdc(%rbp)
leaq -0xc0(%rbp), %rdi
leaq -0xdc(%rbp), %rsi
callq 0xcc60
movl %edx, -0x1c4(%rbp)
movq %rax, -0x1c0(%rbp)
jmp 0xc0f1
movl -0x1c4(%rbp), %eax
movq -0x1c0(%rbp), %rcx
movq %rcx, -0xf0(%rbp)
movl %eax, -0xe8(%rbp)
movl -0xe8(%rbp), %eax
movl %eax, -0xd0(%rbp)
movq -0xf0(%rbp), %rax
movq %rax, -0xd8(%rbp)
leaq -0xb8(%rbp), %rdi
leaq -0xd8(%rbp), %rsi
callq 0xcc20
movb %dl, -0x1d1(%rbp)
movq %rax, -0x1d0(%rbp)
jmp 0xc147
movb -0x1d1(%rbp), %al
movq -0x1d0(%rbp), %rcx
movq %rcx, -0x100(%rbp)
movb %al, -0xf8(%rbp)
jmp 0xc216
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
movl -0x54(%rbp), %eax
movl $0x1, %ecx
cmpl %ecx, %eax
jne 0xc48c
movq -0x50(%rbp), %rdi
callq 0x30c0
movq %rax, -0x150(%rbp)
leaq -0xb8(%rbp), %rax
movq %rax, -0x158(%rbp)
movq -0x158(%rbp), %rdi
callq 0xcd20
movq %rax, -0x160(%rbp)
movq -0x158(%rbp), %rdi
callq 0xcd50
movq %rax, -0x168(%rbp)
leaq -0x160(%rbp), %rdi
leaq -0x168(%rbp), %rsi
callq 0xcd80
testb $0x1, %al
jne 0xc1e2
jmp 0xc438
leaq -0x160(%rbp), %rdi
callq 0xcdb0
movq %rax, -0x170(%rbp)
movq -0x170(%rbp), %rax
movq (%rax), %rdi
callq 0x9f70
jmp 0xc206
jmp 0xc208
leaq -0x160(%rbp), %rdi
callq 0xcdd0
jmp 0xc1c6
movq -0xc0(%rbp), %rdi
addq $0x68, %rdi
callq 0xcb10
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, -0x1d2(%rbp)
jne 0xc25c
movq -0xc0(%rbp), %rdi
addq $0x28, %rdi
movslq -0xc8(%rbp), %rsi
callq 0xa1b0
movq (%rax), %rax
cmpq -0x20(%rbp), %rax
sete %al
movb %al, -0x1d2(%rbp)
movb -0x1d2(%rbp), %al
andb $0x1, %al
movb %al, -0xc1(%rbp)
movl -0xc8(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xc8(%rbp)
jmp 0xc03e
testb $0x1, -0xc1(%rbp)
jne 0xc31b
leaq -0xb8(%rbp), %rax
movq %rax, -0x108(%rbp)
movq -0x108(%rbp), %rdi
callq 0xcd20
movq %rax, -0x110(%rbp)
movq -0x108(%rbp), %rdi
callq 0xcd50
movq %rax, -0x118(%rbp)
leaq -0x110(%rbp), %rdi
leaq -0x118(%rbp), %rsi
callq 0xcd80
testb $0x1, %al
jne 0xc2d8
jmp 0xc30c
leaq -0x110(%rbp), %rdi
callq 0xcdb0
movq %rax, -0x120(%rbp)
movq -0x120(%rbp), %rax
movq (%rax), %rdi
callq 0x9f70
jmp 0xc2fc
jmp 0xc2fe
leaq -0x110(%rbp), %rdi
callq 0xcdd0
jmp 0xc2bf
movl $0x5, -0x88(%rbp)
jmp 0xc44b
movl -0x28(%rbp), %eax
movl %eax, -0x124(%rbp)
cmpl $0x0, -0x124(%rbp)
jl 0xc389
movq -0x20(%rbp), %rdi
addq $0x28, %rdi
movslq -0x124(%rbp), %rsi
callq 0xa1b0
movq (%rax), %rax
movq %rax, -0x1e0(%rbp)
movslq -0x124(%rbp), %rsi
leaq -0x40(%rbp), %rdi
callq 0xa1b0
movq (%rax), %rdi
addq $0x28, %rdi
movslq -0x124(%rbp), %rsi
callq 0xa1b0
movq -0x1e0(%rbp), %rcx
movq %rcx, (%rax)
movl -0x124(%rbp), %eax
addl $-0x1, %eax
movl %eax, -0x124(%rbp)
jmp 0xc324
movq -0x20(%rbp), %rdi
callq 0x9f70
jmp 0xc394
leaq -0xb8(%rbp), %rax
movq %rax, -0x130(%rbp)
movq -0x130(%rbp), %rdi
callq 0xcd20
movq %rax, -0x138(%rbp)
movq -0x130(%rbp), %rdi
callq 0xcd50
movq %rax, -0x140(%rbp)
leaq -0x138(%rbp), %rdi
leaq -0x140(%rbp), %rsi
callq 0xcd80
testb $0x1, %al
jne 0xc3e1
jmp 0xc415
leaq -0x138(%rbp), %rdi
callq 0xcdb0
movq %rax, -0x148(%rbp)
movq -0x148(%rbp), %rax
movq (%rax), %rdi
callq 0x9f70
jmp 0xc405
jmp 0xc407
leaq -0x138(%rbp), %rdi
callq 0xcdd0
jmp 0xc3c8
movb $0x1, -0x1(%rbp)
movl $0x1, -0x88(%rbp)
jmp 0xc44b
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
callq 0x3380
jmp 0xc48a
callq 0x3380
jmp 0xc43f
jmp 0xc441
movl $0x0, -0x88(%rbp)
leaq -0xb8(%rbp), %rdi
callq 0xce30
movl -0x88(%rbp), %eax
movl %eax, -0x1e4(%rbp)
testl %eax, %eax
je 0xc47a
jmp 0xc469
movl -0x1e4(%rbp), %eax
subl $0x5, %eax
je 0xbecd
jmp 0xc4af
jmp 0xc4aa
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x50(%rbp)
movl %eax, -0x54(%rbp)
jmp 0xc48c
jmp 0xc48c
leaq -0xb8(%rbp), %rdi
callq 0xce30
jmp 0xc4cf
movb $0x0, -0x1(%rbp)
movl $0x1, -0x88(%rbp)
jmp 0xc4af
jmp 0xbecd
leaq -0x70(%rbp), %rdi
callq 0xa1d0
leaq -0x40(%rbp), %rdi
callq 0xa1d0
movb -0x1(%rbp), %al
andb $0x1, %al
addq $0x1f0, %rsp            # imm = 0x1F0
popq %rbp
retq
leaq -0x70(%rbp), %rdi
callq 0xa1d0
leaq -0x40(%rbp), %rdi
callq 0xa1d0
movq -0x50(%rbp), %rdi
callq 0x33b0
movq %rax, %rdi
callq 0x61f0
nopw %cs:(%rax,%rax)