public MagicSquareSolution solve(long allowedTimeNanoSec)
    {
        int[][] square = HelperFunctions.makeInitialSquare(rand, options.n, options.constrained, options.cRow, options.cCol);
        int fitness    = HelperFunctions.evalFitness(square, options.n, options.m);
        int iteration  = 0;
        
        double temperature = INTIAL_TEMPERATURE;
        
        long startTime = System.nanoTime();
        
        do
        {
            if(fitness == 0)
            {
                break;
            }
            
            temperature *= COOLING_FRACTION;
            int tempFitness = fitness;
            
            for(int j=0; j<STEPS_PER_ITERATION; j++)
            {
                int[][] mutatedSquare = mutate(square, options.n);
                int newFitness        = HelperFunctions.evalFitness(mutatedSquare, options.n, options.m);
                
                if(newFitness < fitness || 
                   Math.exp((fitness-newFitness)/(BOLTZMANNS_CONSTANT*temperature)) > rand.nextDouble())
                {
                    square  = mutatedSquare;
                    fitness = newFitness;
                }
                
                if(fitness == 0)
                {
                    break;
                }
            }
            
            if(tempFitness != fitness)
            {
                temperature /= COOLING_FRACTION;
            }
            
            System.out.println("Iteration #" + iteration + ": best fitness: " + fitness);
            
            iteration++;
        }while(System.nanoTime() - startTime < allowedTimeNanoSec);
        
        return new MagicSquareSolution(square, fitness, iteration); 
    }