// File: EduBic[P]TSP-MetaheuristicSolvers/TabuSearchSolver.cpp
// Function: TabuSearchSolver::solve(TSP const&, TSPSolution const&, TSPSolution&)
bool TabuSearchSolver::solve(const TSP& tsp, const TSPSolution& initSol, TSPSolution& bestSol) {

    // clear previous use
    //mTabuList.clear();
    mTabuSet.clear();
    mAdvTabuList.clear();

    try {
        bool stop = false;
        int  iter = 0;

        //tabuList.reserve(tsp.n);
        //initTabuList(tsp.n);

        TSPSolution currSol(initSol);
        double bestValue, currValue;

        bestValue = currValue = currSol.evaluateObjectiveFunction(tsp);
        TSPMove move;

        clock_t currTime = clock();

        while (!stop) {
            iter++;

            // for small instance of problem print the current solution
            if (tsp.n < 20) {
                currSol.print(std::cout);
                std::cout << " (" << iter << ") value " << currValue << "\t(" << bestValue << ")";
            }

            //mAspiration = bestValue - currValue;

            /*double costVar;
            if (BestImprovement) {
                costVar = findBestNeighbor(tsp, currSol, move);
            } else {
                costVar = findFirstBestNeighbor(tsp, currSol, move);
            }*/

            double bestNeighValue = currValue + findBestNeighbor(tsp, currSol, move); // costVar;

            if (bestNeighValue >= tsp.infinite) {
                cout << "\tmove: NO legal neighbour" << endl;
                stop = true;
            }
            else {
                // insertTabu(move);
                if (mTabuLength != 0) {

                    // use only List
                    /*if (mTabuList.size() < mTabuLength) {
                        mTabuList.push_back(move);
                    } else {
                        mTabuList.pop_front();  // remove old elements
                        mTabuList.push_back(move);
                    }*/

                    // use only List + Set
                    if (mTabuSet.size() < mTabuLength) {
                        // get key from TSPMove
                        char buffer[50];
                        sprintf(buffer, "%u%u", move.from, move.to);
                        string moveKey = string(buffer);

                        mAdvTabuList.push_back(moveKey);
                        mTabuSet.insert(moveKey);

                    } else {
                        // get key from TSPMove
                        char buffer[50];
                        sprintf(buffer, "%u%u", move.from, move.to);
                        string moveKey = string(buffer);

                        //cout << "\tNew tabu move: " << moveKey << "\t" << mTabuSet.size() << endl;

                        // remove old key
                        string oldTabuKey = mAdvTabuList.front();

                        mAdvTabuList.pop_front();
                        mTabuSet.erase(oldTabuKey);

                        // add new key
                        mAdvTabuList.push_back(moveKey);
                        mTabuSet.insert(moveKey);
                    }

                }

                currSol = swap(currSol,move);
                currValue = bestNeighValue;

                if (currValue < bestValue - 0.01) { // TS: update incumbent (if better -with tolerance- solution found)
                    bestValue = currValue;
                    bestSol = currSol;

                    cout << " (" << iter << ") value " << currValue
                         << "\tmove: " << move.from << " , " << move.to
                         << "\tbetter solution" << std::endl;
                }

                // stopping criteria
                /*if (iter > mMaxIteration) {
                    stop = true;
                } else*/ if ((double)(clock() - currTime) / CLOCKS_PER_SEC > mMaxTime) {
                    stop = true;
                }

                //std::cout << "\tmove: " << move.from << " , " << move.to;
                //std::cout << std::endl;
            }
        }

        bestSol.iterations = iter;

        return true;
    }
    catch(std::exception& e) {
        std::cout << ">>>EXCEPTION: " << e.what() << std::endl;
        return false;
    }
}

// Assembly:
subq $0x2c8, %rsp            # imm = 0x2C8
movq %rdi, 0x2b8(%rsp)
movq %rsi, 0x2b0(%rsp)
movq %rdx, 0x2a8(%rsp)
movq %rcx, 0x2a0(%rsp)
movq 0x2b8(%rsp), %rdi
movq %rdi, 0xf8(%rsp)
addq $0x48, %rdi
callq 0xb230
movq 0xf8(%rsp), %rdi
addq $0x30, %rdi
callq 0xb240
movb $0x0, 0x29f(%rsp)
movl $0x0, 0x298(%rsp)
movq 0x2a8(%rsp), %rsi
leaq 0x248(%rsp), %rdi
callq 0x9e80
jmp 0xa29b
movq 0x2b0(%rsp), %rsi
leaq 0x248(%rsp), %rdi
callq 0x5bf0
movsd %xmm0, 0xf0(%rsp)
jmp 0xa2bb
movsd 0xf0(%rsp), %xmm0
movsd %xmm0, 0x228(%rsp)
movsd %xmm0, 0x230(%rsp)
callq 0x31c0
movq %rax, 0x218(%rsp)
movb 0x29f(%rsp), %al
xorb $-0x1, %al
testb $0x1, %al
jne 0xa2f5
jmp 0xa9b1
movl 0x298(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x298(%rsp)
movq 0x2b0(%rsp), %rax
cmpl $0x14, (%rax)
jge 0xa42e
movq 0x9cb2(%rip), %rsi      # 0x13fd0
leaq 0x248(%rsp), %rdi
callq 0x8720
jmp 0xa32d
movq 0x9c9c(%rip), %rdi      # 0x13fd0
leaq 0x3f3c(%rip), %rsi      # 0xe277
callq 0x3280
movq %rax, 0xe8(%rsp)
jmp 0xa34a
movq 0xe8(%rsp), %rdi
movl 0x298(%rsp), %esi
callq 0x3410
movq %rax, 0xe0(%rsp)
jmp 0xa368
movq 0xe0(%rsp), %rdi
leaq 0x3f03(%rip), %rsi      # 0xe27a
callq 0x3280
movq %rax, 0xd8(%rsp)
jmp 0xa386
movq 0xd8(%rsp), %rdi
movsd 0x228(%rsp), %xmm0
callq 0x3460
movq %rax, 0xd0(%rsp)
jmp 0xa3a6
movq 0xd0(%rsp), %rdi
leaq 0x3f72(%rip), %rsi      # 0xe327
callq 0x3280
movq %rax, 0xc8(%rsp)
jmp 0xa3c4
movq 0xc8(%rsp), %rdi
movsd 0x230(%rsp), %xmm0
callq 0x3460
movq %rax, 0xc0(%rsp)
jmp 0xa3e4
movq 0xc0(%rsp), %rdi
leaq 0x3e90(%rip), %rsi      # 0xe283
callq 0x3280
jmp 0xa3fa
jmp 0xa42e
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x240(%rsp)
movl %eax, 0x23c(%rsp)
jmp 0xa9ea
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x240(%rsp)
movl %eax, 0x23c(%rsp)
jmp 0xa9dd
movq 0xf8(%rsp), %rdi
movsd 0x228(%rsp), %xmm0
movsd %xmm0, 0xb0(%rsp)
movq 0x2b0(%rsp), %rsi
leaq 0x248(%rsp), %rdx
leaq 0x220(%rsp), %rcx
callq 0xaac0
movsd %xmm0, 0xb8(%rsp)
jmp 0xa470
movsd 0xb8(%rsp), %xmm1
movsd 0xb0(%rsp), %xmm0
addsd %xmm1, %xmm0
movsd %xmm0, 0x210(%rsp)
movsd 0x210(%rsp), %xmm0
movq 0x2b0(%rsp), %rax
ucomisd 0x20(%rax), %xmm0
jb 0xa4e7
movq 0x9b22(%rip), %rdi      # 0x13fd0
leaq 0x3e75(%rip), %rsi      # 0xe32a
callq 0x3280
movq %rax, 0xa8(%rsp)
jmp 0xa4c4
movq 0xa8(%rsp), %rdi
movq 0x9add(%rip), %rsi      # 0x13fb0
callq 0x32b0
jmp 0xa4da
movb $0x1, 0x29f(%rsp)
jmp 0xa9ac
movq 0xf8(%rsp), %rax
cmpl $0x0, 0x8(%rax)
je 0xa7e6
movq 0xf8(%rsp), %rdi
addq $0x48, %rdi
callq 0xb270
movq 0xf8(%rsp), %rcx
movl 0x8(%rcx), %ecx
cmpq %rcx, %rax
jae 0xa64c
movl 0x220(%rsp), %edx
movl 0x224(%rsp), %ecx
leaq 0x3e11(%rip), %rsi      # 0xe344
xorl %eax, %eax
leaq 0x1d0(%rsp), %rdi
movq %rdi, 0x98(%rsp)
callq 0x3080
leaq 0x1af(%rsp), %rdi
movq %rdi, 0xa0(%rsp)
callq 0x3430
movq 0x98(%rsp), %rsi
movq 0xa0(%rsp), %rdx
leaq 0x1b0(%rsp), %rdi
callq 0x3320
jmp 0xa57e
leaq 0x1af(%rsp), %rdi
callq 0x32c0
movq 0xf8(%rsp), %rdi
addq $0x30, %rdi
leaq 0x1b0(%rsp), %rsi
callq 0xb280
jmp 0xa5a6
movq 0xf8(%rsp), %rdi
addq $0x48, %rdi
leaq 0x1b0(%rsp), %rsi
callq 0xb2c0
movb %dl, 0x8f(%rsp)
movq %rax, 0x90(%rsp)
jmp 0xa5d0
movb 0x8f(%rsp), %al
movq 0x90(%rsp), %rcx
movq %rcx, 0x198(%rsp)
movb %al, 0x1a0(%rsp)
leaq 0x1b0(%rsp), %rdi
callq 0x31b0
jmp 0xa7e4
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x240(%rsp)
movl %eax, 0x23c(%rsp)
leaq 0x1af(%rsp), %rdi
callq 0x32c0
jmp 0xa9dd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x240(%rsp)
movl %eax, 0x23c(%rsp)
leaq 0x1b0(%rsp), %rdi
callq 0x31b0
jmp 0xa9dd
movl 0x220(%rsp), %edx
movl 0x224(%rsp), %ecx
leaq 0x3ce3(%rip), %rsi      # 0xe344
xorl %eax, %eax
leaq 0x160(%rsp), %rdi
movq %rdi, 0x78(%rsp)
callq 0x3080
leaq 0x13f(%rsp), %rdi
movq %rdi, 0x80(%rsp)
callq 0x3430
movq 0x78(%rsp), %rsi
movq 0x80(%rsp), %rdx
leaq 0x140(%rsp), %rdi
callq 0x3320
jmp 0xa6a6
leaq 0x13f(%rsp), %rdi
callq 0x32c0
movq 0xf8(%rsp), %rdi
addq $0x30, %rdi
callq 0xb320
movq %rax, %rsi
leaq 0x118(%rsp), %rdi
callq 0x3120
jmp 0xa6d6
movq 0xf8(%rsp), %rdi
addq $0x30, %rdi
callq 0xb350
movq 0xf8(%rsp), %rdi
addq $0x48, %rdi
leaq 0x118(%rsp), %rsi
callq 0xb380
jmp 0xa702
movq 0xf8(%rsp), %rdi
addq $0x30, %rdi
leaq 0x140(%rsp), %rsi
callq 0xb280
jmp 0xa71d
movq 0xf8(%rsp), %rdi
addq $0x48, %rdi
leaq 0x140(%rsp), %rsi
callq 0xb2c0
movb %dl, 0x6f(%rsp)
movq %rax, 0x70(%rsp)
jmp 0xa741
movb 0x6f(%rsp), %al
movq 0x70(%rsp), %rcx
movq %rcx, 0x108(%rsp)
movb %al, 0x110(%rsp)
leaq 0x118(%rsp), %rdi
callq 0x31b0
leaq 0x140(%rsp), %rdi
callq 0x31b0
jmp 0xa7e4
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x240(%rsp)
movl %eax, 0x23c(%rsp)
leaq 0x13f(%rsp), %rdi
callq 0x32c0
jmp 0xa9dd
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x240(%rsp)
movl %eax, 0x23c(%rsp)
jmp 0xa7d2
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x240(%rsp)
movl %eax, 0x23c(%rsp)
leaq 0x118(%rsp), %rdi
callq 0x31b0
leaq 0x140(%rsp), %rdi
callq 0x31b0
jmp 0xa9dd
jmp 0xa7e6
movq 0xf8(%rsp), %rdi
leaq 0x248(%rsp), %rsi
leaq 0x220(%rsp), %rdx
callq 0xad10
movq %rax, 0x60(%rsp)
jmp 0xa80a
movq 0x60(%rsp), %rsi
leaq 0x248(%rsp), %rdi
callq 0x9fb0
jmp 0xa81e
movsd 0x210(%rsp), %xmm0
movsd %xmm0, 0x228(%rsp)
movsd 0x228(%rsp), %xmm1
movsd 0x230(%rsp), %xmm0
movsd 0x3a6e(%rip), %xmm2     # 0xe2b8
subsd %xmm2, %xmm0
ucomisd %xmm1, %xmm0
jbe 0xa972
movsd 0x228(%rsp), %xmm0
movsd %xmm0, 0x230(%rsp)
movq 0x2a0(%rsp), %rdi
leaq 0x248(%rsp), %rsi
callq 0x9fb0
jmp 0xa881
movq 0x9748(%rip), %rdi      # 0x13fd0
leaq 0x39e8(%rip), %rsi      # 0xe277
callq 0x3280
movq %rax, 0x58(%rsp)
jmp 0xa89b
movq 0x58(%rsp), %rdi
movl 0x298(%rsp), %esi
callq 0x3410
movq %rax, 0x50(%rsp)
jmp 0xa8b3
movq 0x50(%rsp), %rdi
leaq 0x39bb(%rip), %rsi      # 0xe27a
callq 0x3280
movq %rax, 0x48(%rsp)
jmp 0xa8cb
movq 0x48(%rsp), %rdi
movsd 0x228(%rsp), %xmm0
callq 0x3460
movq %rax, 0x40(%rsp)
jmp 0xa8e5
movq 0x40(%rsp), %rdi
leaq 0x3a58(%rip), %rsi      # 0xe349
callq 0x3280
movq %rax, 0x38(%rsp)
jmp 0xa8fd
movq 0x38(%rsp), %rdi
movl 0x220(%rsp), %esi
callq 0x3410
movq %rax, 0x30(%rsp)
jmp 0xa915
movq 0x30(%rsp), %rdi
leaq 0x396d(%rip), %rsi      # 0xe28e
callq 0x3280
movq %rax, 0x28(%rsp)
jmp 0xa92d
movq 0x28(%rsp), %rdi
movl 0x224(%rsp), %esi
callq 0x3410
movq %rax, 0x20(%rsp)
jmp 0xa945
movq 0x20(%rsp), %rdi
leaq 0x3a00(%rip), %rsi      # 0xe351
callq 0x3280
movq %rax, 0x18(%rsp)
jmp 0xa95d
movq 0x18(%rsp), %rdi
movq 0x9647(%rip), %rsi      # 0x13fb0
callq 0x32b0
jmp 0xa970
jmp 0xa972
callq 0x31c0
movq %rax, %rcx
movq 0xf8(%rsp), %rax
subq 0x218(%rsp), %rcx
cvtsi2sd %rcx, %xmm0
movsd 0x3681(%rip), %xmm1     # 0xe018
divsd %xmm1, %xmm0
ucomisd 0x10(%rax), %xmm0
jbe 0xa9aa
movb $0x1, 0x29f(%rsp)
jmp 0xa9ac
jmp 0xa2e3
movl 0x298(%rsp), %ecx
movq 0x2a0(%rsp), %rax
movl %ecx, 0x48(%rax)
movb $0x1, 0x2c7(%rsp)
leaq 0x248(%rsp), %rdi
callq 0xa060
jmp 0xaa91
leaq 0x248(%rsp), %rdi
callq 0xa060
movl 0x23c(%rsp), %eax
movl $0x1, %ecx
cmpl %ecx, %eax
jne 0xaaa2
movq 0x240(%rsp), %rdi
callq 0x3110
movq %rax, 0x100(%rsp)
movq 0x95b6(%rip), %rdi      # 0x13fd0
leaq 0x3871(%rip), %rsi      # 0xe292
callq 0x3280
movq %rax, 0x10(%rsp)
jmp 0xaa2d
movq 0x100(%rsp), %rdi
movq (%rdi), %rax
movq 0x10(%rax), %rax
callq *%rax
movq 0x10(%rsp), %rdi
movq %rax, %rsi
callq 0x3280
movq %rax, 0x8(%rsp)
jmp 0xaa52
movq 0x8(%rsp), %rdi
movq 0x9552(%rip), %rsi      # 0x13fb0
callq 0x32b0
jmp 0xaa65
movb $0x0, 0x2c7(%rsp)
callq 0x33d0
jmp 0xaa91
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x240(%rsp)
movl %eax, 0x23c(%rsp)
callq 0x33d0
jmp 0xaa8f
jmp 0xaaa2
movb 0x2c7(%rsp), %al
andb $0x1, %al
addq $0x2c8, %rsp            # imm = 0x2C8
retq
movq 0x240(%rsp), %rdi
callq 0x3420
movq %rax, %rdi
callq 0x6770
nopw (%rax,%rax)