// File: lioncedric[P]cimpressGridCoverage/src/main.cc
// Function: algoTabuMPI(int*, int, int, int, int)
void algoTabuMPI(int * puzzle_step, const int height, const int width, const int nbNeigbours, const int tps) {
    const int numProc = MPI::COMM_WORLD.Get_rank();
    const int nbProc = MPI::COMM_WORLD.Get_size();
    //initialize the seed for all the process
    int seed;

    if (numProc == 0) {
        srand(time(NULL));
        seed = rand();

        for (int it = 1; it < nbProc; it++) {
            seed = rand();
            MPI::COMM_WORLD.Send(&seed, 1, MPI::INT, it, 10);
        }

    } else {
        MPI::COMM_WORLD.Recv(&seed, 1, MPI::INT, 0, 10);
        srand(seed);
    }

    //timer
    double start = MPI::Wtime();
    double end;

    //the number of increments
    int count = 0;

    //first solution is calculated on the full area starting form the top left corner
    int area[4] = {1, 1, width - 2, height - 2};
    int f_step = solver(puzzle_step, width, 1, area);
    if (numProc == 0) {
        cout << "init : " << f_step << endl;
    }
    //fmin is now the first solution
    int fmin = f_step;
    int fminpuzzle[height * width];
    memcpy(fminpuzzle, puzzle_step, height * width * sizeof *puzzle_step);

    //iterations variables

    int f;
    int test;
    int params[4];
    int fparams[4];
    int squares_removed[height * width * 3]; //for better performance array size is initialized to maximum (instead of a list)

    //taboo list
    list< int > listeS1;

    //start
    do {
        //neighbour solution min 
        f = 9999999;

        for (int nb = 0; nb < nbNeigbours / nbProc; ++nb) {
            //Calculation of the next neighbour
            params[0] = 1 + rand() % (width - 2); //x0
            params[1] = 1 + rand() % (height - 2); //y0 
            params[2] = 1 + rand() % 20; //size
            params[3] = rand() % 4; //solver orientation

            //define the area to recompute
            area[0] = params[0];
            area[1] = params[1];
            area[2] = min(params[0] + params[2], width - 2);
            area[3] = min(params[1] + params[2], height - 2);

            //clear area with 1 and recalulate the area accorting to the squares removed
            int nb_squares_removed = clear_squares(puzzle_step, width, area, squares_removed);
            //compute area previously removed
            int nb_squares_used = solver(puzzle_step, width, params[3], area);
            //get result
            test = f_step - nb_squares_removed + nb_squares_used;

            //save if best neighbour
            if (find(listeS1.begin(), listeS1.end(), test) == listeS1.end()) {
                if (test < f) {
                    fparams[0] = params[0];
                    fparams[1] = params[1];

                    fparams[2] = params[2];
                    fparams[3] = params[3];
                    f = test;
                }
            }
            //back to original to process new neighbour
            use_squares(puzzle_step, width, squares_removed);
        }
        //get best neighbour from all proc
        if (numProc == 0) {
            for (int it = 1; it < nbProc; it++) {
                MPI::COMM_WORLD.Recv(&test, 1, MPI::INT, it, 1);
                MPI::COMM_WORLD.Recv(params, 4, MPI::INT, it, 4);

                if (test < f) {
                    fparams[0] = params[0];
                    fparams[1] = params[1];

                    fparams[2] = params[2];
                    fparams[3] = params[3];
                    f = test;
                }
            }

        } else {
            MPI::COMM_WORLD.Send(&f, 1, MPI::INT, 0, 1);
            MPI::COMM_WORLD.Send(fparams, 4, MPI::INT, 0, 4);
        }
        MPI::COMM_WORLD.Bcast(fparams, 4, MPI::INT, 0);
        MPI::COMM_WORLD.Bcast(&f, 1, MPI::INT, 0);

        //if a neighbour was saved use it for the next iterations
        if (f < 9999999) {
            area[0] = fparams[0];
            area[1] = fparams[1];
            area[2] = min(fparams[0] + fparams[2], width - 2);
            area[3] = min(fparams[1] + fparams[2], height - 2);
            clear_squares(puzzle_step, width, area, squares_removed);
            solver(puzzle_step, width, fparams[3], area);
            f_step = f;
        }
        //update tabu list
        if (listeS1.size() > 3) {
            listeS1.pop_back();
        }
        listeS1.push_front(f);

        if (numProc == 0 && f < fmin) {
            fmin = f_step;
            memcpy(fminpuzzle, puzzle_step, height * width * sizeof *puzzle_step);
            cout << fmin << endl;
        }
        count++;

        //there is no way to know is the best anwser is found so we stop after a certain duration
        end = MPI::Wtime() - start;
        MPI::COMM_WORLD.Bcast(&end, 1, MPI::DOUBLE, 0);


    } while (end < tps);
    if (numProc == 0) {
        memcpy(puzzle_step, fminpuzzle, height * width * sizeof *fminpuzzle);
        cout << "Computation Performances : " << count * nbNeigbours << endl;
    }

}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x190, %rsp            # imm = 0x190
movq %rdi, -0x8(%rbp)
movl %esi, -0xc(%rbp)
movl %edx, -0x10(%rbp)
movl %ecx, -0x14(%rbp)
movl %r8d, -0x18(%rbp)
movq 0xe045(%rip), %rdi      # 0x17fb8
callq 0xb3e0
movl %eax, -0x1c(%rbp)
movq 0xe036(%rip), %rdi      # 0x17fb8
callq 0xb410
movl %eax, -0x20(%rbp)
cmpl $0x0, -0x1c(%rbp)
jne 0x9ff2
xorl %eax, %eax
movl %eax, %edi
callq 0x9620
movl %eax, %edi
callq 0x9690
callq 0x9190
movl %eax, -0x24(%rbp)
movl $0x1, -0x28(%rbp)
movl -0x28(%rbp), %eax
cmpl -0x20(%rbp), %eax
jge 0x9ff0
callq 0x9190
movl %eax, -0x24(%rbp)
movl -0x28(%rbp), %r8d
movq 0xdfee(%rip), %rdi      # 0x17fb8
leaq -0x24(%rbp), %rsi
movl $0x1, %edx
movq 0xdfe6(%rip), %rcx      # 0x17fc0
movl $0xa, %r9d
callq 0xade0
movl -0x28(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x28(%rbp)
jmp 0x9faf
jmp 0xa01f
movq 0xdfbf(%rip), %rdi      # 0x17fb8
leaq -0x24(%rbp), %rsi
movl $0x1, %edx
movq 0xdfb7(%rip), %rcx      # 0x17fc0
xorl %r8d, %r8d
movl $0xa, %r9d
callq 0xb440
movl -0x24(%rbp), %edi
callq 0x9690
callq 0xb4c0
movsd %xmm0, -0x30(%rbp)
movl $0x0, -0x3c(%rbp)
movl $0x1, -0x50(%rbp)
movl $0x1, -0x4c(%rbp)
movl -0x10(%rbp), %eax
subl $0x2, %eax
movl %eax, -0x48(%rbp)
movl -0xc(%rbp), %eax
subl $0x2, %eax
movl %eax, -0x44(%rbp)
movq -0x8(%rbp), %rdi
movl -0x10(%rbp), %esi
leaq -0x50(%rbp), %rcx
movl $0x1, %edx
callq 0x113b0
movl %eax, -0x54(%rbp)
cmpl $0x0, -0x1c(%rbp)
jne 0xa09b
movq 0xdf5b(%rip), %rdi      # 0x17fd0
leaq 0x8f88(%rip), %rsi      # 0x13004
callq 0x96b0
movq %rax, %rdi
movl -0x54(%rbp), %esi
callq 0x9c10
movq %rax, %rdi
movq 0xdf12(%rip), %rsi      # 0x17fa8
callq 0x9700
movl -0x54(%rbp), %eax
movl %eax, -0x58(%rbp)
movl -0xc(%rbp), %eax
movl -0x10(%rbp), %ecx
imull %ecx, %eax
movq %rsp, %rcx
movq %rcx, -0x60(%rbp)
leaq 0xf(,%rax,4), %rcx
andq $-0x10, %rcx
movq %rsp, %rdi
subq %rcx, %rdi
movq %rdi, -0x128(%rbp)
movq %rdi, %rsp
movq %rax, -0x68(%rbp)
movq -0x8(%rbp), %rsi
movl -0xc(%rbp), %eax
movl -0x10(%rbp), %ecx
imull %ecx, %eax
movslq %eax, %rdx
shlq $0x2, %rdx
callq 0x9580
movl -0xc(%rbp), %ecx
movl -0x10(%rbp), %eax
imull %eax, %ecx
movl %ecx, %eax
leal (%rax,%rax,2), %eax
leaq 0xf(,%rax,4), %rdx
andq $-0x10, %rdx
movq %rsp, %rcx
subq %rdx, %rcx
movq %rcx, -0x120(%rbp)
movq %rcx, %rsp
movq %rax, -0x98(%rbp)
leaq -0xb0(%rbp), %rdi
callq 0xb4d0
movl $0x98967f, -0x6c(%rbp)  # imm = 0x98967F
movl $0x0, -0xb4(%rbp)
movl -0xb4(%rbp), %eax
movl %eax, -0x12c(%rbp)
movl -0x14(%rbp), %eax
cltd
idivl -0x20(%rbp)
movl %eax, %ecx
movl -0x12c(%rbp), %eax
cmpl %ecx, %eax
jge 0xa3d0
callq 0x9190
movl -0x10(%rbp), %ecx
addl $-0x2, %ecx
cltd
idivl %ecx
incl %edx
movl %edx, -0x80(%rbp)
callq 0x9190
movl -0xc(%rbp), %ecx
addl $-0x2, %ecx
cltd
idivl %ecx
incl %edx
movl %edx, -0x7c(%rbp)
callq 0x9190
movl %eax, %ecx
movslq %ecx, %rax
imulq $0x66666667, %rax, %rdx # imm = 0x66666667
movq %rdx, %rax
shrq $0x3f, %rax
sarq $0x23, %rdx
addl %eax, %edx
shll $0x2, %edx
movl %edx, %eax
leal (%rax,%rax,4), %edx
movl %ecx, %eax
negl %edx
movl %edx, %ecx
leal 0x1(%rax,%rcx), %eax
movl %eax, -0x78(%rbp)
callq 0x9190
movl %eax, %edx
addl $0x3, %edx
testl %eax, %eax
movl %eax, %ecx
cmovsl %edx, %ecx
andl $-0x4, %ecx
subl %ecx, %eax
movl %eax, -0x74(%rbp)
movl -0x80(%rbp), %eax
movl %eax, -0x50(%rbp)
movl -0x7c(%rbp), %eax
movl %eax, -0x4c(%rbp)
movl -0x80(%rbp), %eax
movl -0x78(%rbp), %ecx
addl %ecx, %eax
movl %eax, -0xb8(%rbp)
movl -0x10(%rbp), %eax
addl $-0x2, %eax
movl %eax, -0xbc(%rbp)
leaq -0xb8(%rbp), %rdi
leaq -0xbc(%rbp), %rsi
callq 0xb4e0
movq %rax, -0x138(%rbp)
jmp 0xa20f
movq -0x138(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x48(%rbp)
movl -0x7c(%rbp), %eax
movl -0x78(%rbp), %ecx
addl %ecx, %eax
movl %eax, -0xd0(%rbp)
movl -0xc(%rbp), %eax
addl $-0x2, %eax
movl %eax, -0xd4(%rbp)
leaq -0xd0(%rbp), %rdi
leaq -0xd4(%rbp), %rsi
callq 0xb4e0
movq %rax, -0x140(%rbp)
jmp 0xa251
movq -0x120(%rbp), %rcx
movq -0x140(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x44(%rbp)
movq -0x8(%rbp), %rdi
movl -0x10(%rbp), %esi
leaq -0x50(%rbp), %rdx
callq 0x10f00
movl %eax, -0x144(%rbp)
jmp 0xa27c
movl -0x144(%rbp), %eax
movl %eax, -0xd8(%rbp)
movq -0x8(%rbp), %rdi
movl -0x10(%rbp), %esi
movl -0x74(%rbp), %edx
leaq -0x50(%rbp), %rcx
callq 0x113b0
movl %eax, -0x148(%rbp)
jmp 0xa2a3
movl -0x148(%rbp), %eax
movl %eax, -0xdc(%rbp)
movl -0x54(%rbp), %eax
movl -0xd8(%rbp), %ecx
subl %ecx, %eax
movl -0xdc(%rbp), %ecx
addl %ecx, %eax
movl %eax, -0x70(%rbp)
leaq -0xb0(%rbp), %rdi
movq %rdi, -0x158(%rbp)
callq 0xb5a0
movq -0x158(%rbp), %rdi
movq %rax, -0xf0(%rbp)
callq 0xb5d0
movq %rax, -0xf8(%rbp)
movq -0xf0(%rbp), %rdi
movq -0xf8(%rbp), %rsi
leaq -0x70(%rbp), %rdx
callq 0xb540
movq %rax, -0x150(%rbp)
jmp 0xa312
movq -0x150(%rbp), %rax
movq %rax, -0xe8(%rbp)
leaq -0xb0(%rbp), %rdi
callq 0xb5d0
movq %rax, -0x100(%rbp)
leaq -0xe8(%rbp), %rdi
leaq -0x100(%rbp), %rsi
callq 0xb520
testb $0x1, %al
jne 0xa34c
jmp 0xa3a5
movl -0x70(%rbp), %eax
cmpl -0x6c(%rbp), %eax
jge 0xa3a3
movl -0x80(%rbp), %eax
movl %eax, -0x90(%rbp)
movl -0x7c(%rbp), %eax
movl %eax, -0x8c(%rbp)
movl -0x78(%rbp), %eax
movl %eax, -0x88(%rbp)
movl -0x74(%rbp), %eax
movl %eax, -0x84(%rbp)
movl -0x70(%rbp), %eax
movl %eax, -0x6c(%rbp)
jmp 0xa3a3
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0xc8(%rbp)
movl %eax, -0xcc(%rbp)
leaq -0xb0(%rbp), %rdi
callq 0xb720
jmp 0xa78f
jmp 0xa3a5
movq -0x120(%rbp), %rdx
movq -0x8(%rbp), %rdi
movl -0x10(%rbp), %esi
callq 0x11270
jmp 0xa3ba
jmp 0xa3bc
movl -0xb4(%rbp), %eax
addl $0x1, %eax
movl %eax, -0xb4(%rbp)
jmp 0xa138
cmpl $0x0, -0x1c(%rbp)
jne 0xa48f
movl $0x1, -0x104(%rbp)
movl -0x104(%rbp), %eax
cmpl -0x20(%rbp), %eax
jge 0xa48d
movl -0x104(%rbp), %r8d
movq 0xdbb7(%rip), %rdi      # 0x17fb8
movq 0xdbb8(%rip), %rcx      # 0x17fc0
leaq -0x70(%rbp), %rsi
movl $0x1, %r9d
movl %r9d, %edx
callq 0xb440
jmp 0xa41c
movl -0x104(%rbp), %r8d
movq 0xdb8e(%rip), %rdi      # 0x17fb8
movq 0xdb8f(%rip), %rcx      # 0x17fc0
leaq -0x80(%rbp), %rsi
movl $0x4, %r9d
movl %r9d, %edx
callq 0xb440
jmp 0xa445
movl -0x70(%rbp), %eax
cmpl -0x6c(%rbp), %eax
jge 0xa477
movl -0x80(%rbp), %eax
movl %eax, -0x90(%rbp)
movl -0x7c(%rbp), %eax
movl %eax, -0x8c(%rbp)
movl -0x78(%rbp), %eax
movl %eax, -0x88(%rbp)
movl -0x74(%rbp), %eax
movl %eax, -0x84(%rbp)
movl -0x70(%rbp), %eax
movl %eax, -0x6c(%rbp)
jmp 0xa479
movl -0x104(%rbp), %eax
addl $0x1, %eax
movl %eax, -0x104(%rbp)
jmp 0xa3e4
jmp 0xa4de
movq 0xdb22(%rip), %rdi      # 0x17fb8
movq 0xdb23(%rip), %rcx      # 0x17fc0
leaq -0x6c(%rbp), %rsi
xorl %r8d, %r8d
movl $0x1, %r9d
movl %r9d, %edx
callq 0xade0
jmp 0xa4b4
movq 0xdafd(%rip), %rdi      # 0x17fb8
movq 0xdafe(%rip), %rcx      # 0x17fc0
leaq -0x90(%rbp), %rsi
xorl %r8d, %r8d
movl $0x4, %r9d
movl %r9d, %edx
callq 0xade0
jmp 0xa4dc
jmp 0xa4de
movq 0xdad3(%rip), %rdi      # 0x17fb8
movq 0xdad4(%rip), %rcx      # 0x17fc0
leaq -0x90(%rbp), %rsi
movl $0x4, %edx
xorl %r8d, %r8d
callq 0xb600
jmp 0xa502
movq 0xdaaf(%rip), %rdi      # 0x17fb8
movq 0xdab0(%rip), %rcx      # 0x17fc0
leaq -0x6c(%rbp), %rsi
movl $0x1, %edx
xorl %r8d, %r8d
callq 0xb600
jmp 0xa523
cmpl $0x98967f, -0x6c(%rbp)  # imm = 0x98967F
jge 0xa609
movl -0x90(%rbp), %eax
movl %eax, -0x50(%rbp)
movl -0x8c(%rbp), %eax
movl %eax, -0x4c(%rbp)
movl -0x90(%rbp), %eax
movl -0x88(%rbp), %ecx
addl %ecx, %eax
movl %eax, -0x108(%rbp)
movl -0x10(%rbp), %eax
addl $-0x2, %eax
movl %eax, -0x10c(%rbp)
leaq -0x108(%rbp), %rdi
leaq -0x10c(%rbp), %rsi
callq 0xb4e0
movq %rax, -0x160(%rbp)
jmp 0xa57e
movq -0x160(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x48(%rbp)
movl -0x8c(%rbp), %eax
movl -0x88(%rbp), %ecx
addl %ecx, %eax
movl %eax, -0x110(%rbp)
movl -0xc(%rbp), %eax
addl $-0x2, %eax
movl %eax, -0x114(%rbp)
leaq -0x110(%rbp), %rdi
leaq -0x114(%rbp), %rsi
callq 0xb4e0
movq %rax, -0x168(%rbp)
jmp 0xa5c6
movq -0x120(%rbp), %rcx
movq -0x168(%rbp), %rax
movl (%rax), %eax
movl %eax, -0x44(%rbp)
movq -0x8(%rbp), %rdi
movl -0x10(%rbp), %esi
leaq -0x50(%rbp), %rdx
callq 0x10f00
jmp 0xa5eb
movq -0x8(%rbp), %rdi
movl -0x10(%rbp), %esi
movl -0x84(%rbp), %edx
leaq -0x50(%rbp), %rcx
callq 0x113b0
jmp 0xa603
movl -0x6c(%rbp), %eax
movl %eax, -0x54(%rbp)
leaq -0xb0(%rbp), %rdi
callq 0xb670
cmpq $0x3, %rax
jbe 0xa627
leaq -0xb0(%rbp), %rdi
callq 0xb6a0
leaq -0xb0(%rbp), %rdi
leaq -0x6c(%rbp), %rsi
callq 0xb6e0
jmp 0xa639
cmpl $0x0, -0x1c(%rbp)
jne 0xa69c
movl -0x6c(%rbp), %eax
cmpl -0x58(%rbp), %eax
jge 0xa69c
movq -0x128(%rbp), %rdi
movl -0x54(%rbp), %eax
movl %eax, -0x58(%rbp)
movq -0x8(%rbp), %rsi
movl -0xc(%rbp), %eax
movl -0x10(%rbp), %ecx
imull %ecx, %eax
movslq %eax, %rdx
shlq $0x2, %rdx
callq 0x9580
movl -0x58(%rbp), %esi
movq 0xd959(%rip), %rdi      # 0x17fd0
callq 0x9c10
movq %rax, -0x170(%rbp)
jmp 0xa685
movq -0x170(%rbp), %rdi
movq 0xd915(%rip), %rsi      # 0x17fa8
callq 0x9700
jmp 0xa69a
jmp 0xa69c
movl -0x3c(%rbp), %eax
incl %eax
movl %eax, -0x3c(%rbp)
callq 0xb4c0
movsd %xmm0, -0x178(%rbp)
jmp 0xa6b3
movsd -0x178(%rbp), %xmm0
movsd -0x30(%rbp), %xmm1
subsd %xmm1, %xmm0
movsd %xmm0, -0x38(%rbp)
movq 0xd8e8(%rip), %rdi      # 0x17fb8
movq 0xd901(%rip), %rcx      # 0x17fd8
leaq -0x38(%rbp), %rsi
movl $0x1, %edx
xorl %r8d, %r8d
callq 0xb600
jmp 0xa6ea
jmp 0xa6ec
movsd -0x38(%rbp), %xmm1
cvtsi2sdl -0x18(%rbp), %xmm0
ucomisd %xmm1, %xmm0
ja 0xa127
cmpl $0x0, -0x1c(%rbp)
jne 0xa777
movq -0x128(%rbp), %rsi
movq -0x8(%rbp), %rdi
movl -0xc(%rbp), %eax
movl -0x10(%rbp), %ecx
imull %ecx, %eax
movslq %eax, %rdx
shlq $0x2, %rdx
callq 0x9580
movq 0xd8a3(%rip), %rdi      # 0x17fd0
leaq 0x88d8(%rip), %rsi      # 0x1300c
callq 0x96b0
movq %rax, -0x180(%rbp)
jmp 0xa742
movq -0x180(%rbp), %rdi
movl -0x3c(%rbp), %esi
movl -0x14(%rbp), %eax
imull %eax, %esi
callq 0x9c10
movq %rax, -0x188(%rbp)
jmp 0xa760
movq -0x188(%rbp), %rdi
movq 0xd83a(%rip), %rsi      # 0x17fa8
callq 0x9700
jmp 0xa775
jmp 0xa777
leaq -0xb0(%rbp), %rdi
callq 0xb720
movq -0x60(%rbp), %rax
movq %rax, %rsp
movq %rbp, %rsp
popq %rbp
retq
movq -0xc8(%rbp), %rdi
callq 0x9c40
nopl (%rax,%rax)