/*  Monte-Carlo simulation code for statistical physics
    Copyright (C) 2001-2005  Sylvain Reynal
    
    Département de Physique
    Ecole Nationale Supérieure de l'Electronique et de ses Applications (ENSEA)
    6, avenue du Ponceau,    
    F-95014 CERGY CEDEX

    et
    
    Laboratoire de Physique Théorique et Modélisation (LPTM)
    Université de Cergy-Pontoise - Site de Neuville
    F-95031 CERGY CEDEX
    
	
    Tel : 00 +33 130 736 245
    Fax : 00 +33 130 736 667
    e-mail : reynal@ensea.fr
    web page : http://www.ensea.fr/staff/reynal/
*/

package fr.ensea.montecarlo.canonical;

import java.io.*;
import java.awt.Color;

import java.text.*;
import fr.ensea.Localizer;
import fr.ensea.montecarlo.model.*;
import fr.ensea.montecarlo.data.*;
import fr.ensea.montecarlo.canonical.*;
import fr.ensea.montecarlo.misc.*;

/**
 * Monte-carlo algorithm with Metropolis updates (=single-spin updates) for the 2D Potts model
 */
public class PottsMetropolis extends AbstractMetropolis {

	private PottsLattice lattice;
	
	// --- RG specific ---
	private PottsLattice[] latticeRG; 
	private int factorRG;
	
	
	private double[] probas; // contains acceptance probabilities whenever dE > 0 (only valid if external field is null)
	// (negative dE yield a unit proba anyway)
	// example :
	//       3                   3
	//   1   2   2     =>    1   3   2        yields dE = -1
	//       3                   3
	//
	
	/**
	 * Init a new MC algorithm
	 * @param energySamples list for energy measurements
	 * @param magnetizationSamples list for magnetization measurements
	 * @param lattice lattice to be simulated
	 * @param kT default temperature
	 */
	public PottsMetropolis(PottsLattice lattice, SamplesBag bag, double kT) {
		super(bag, kT);
		this.lattice = lattice;
	}

	/**
	 * Alters the current temperature and reset counters
	 */
	public synchronized void setTemperature(double kT){
		super.setTemperature(kT);
		this.probas=new double[5];
		if (kT>0) for (int dE=1; dE<5; dE++) probas[dE] = Math.exp(-(double)dE/kT);
		// otherwise, when kT==0, probabilities are null for an update with positive dE
	}
	
	public void activateRG(){
		setFactorRG(1);
	}
	
	public void setFactorRG(int f){
		if (f<1) f=1;
		this.factorRG=f;
		latticeRG = new PottsLattice[factorRG];
		latticeRG[factorRG-1] = lattice.renormalize(latticeRG[factorRG-1]);
		for (int s=factorRG-2; s >= 0; s--){
			latticeRG[s] = latticeRG[s+1].renormalize(latticeRG[s]);
		}
	}
	
	public PottsLattice getRGLattice(){
		return latticeRG[0];
	}
	
	/**
	 * Réalise un sweep de l'ensemble du réseau, i.e. tente de mettre à jour L*L spins.
	 */
	public void sweep(){

		int indexSpin,  nouvelleValeur;
		double dE;
		final int Q = lattice.pottsQ;
		final int qmax = Q-1;
		final int L2 = lattice.size2;

		for (int pas=0; pas < L2; pas++){

			attemptedMoves++;

			// draw one spin to be updated at random amongst L^2 spins:
			indexSpin = (int)(L2 * createRandomNumber());

			// choose a new value for this spin:
			nouvelleValeur = lattice.getSpinValue(indexSpin) + 1 + (int)(createRandomNumber() * qmax);
			if (nouvelleValeur > qmax) nouvelleValeur -= Q; // make sure the new value belongs to [0,Q-1]


			dE = lattice.getEnergyChange(indexSpin, nouvelleValeur);

			if (DEBUG) {
				System.out.println(lattice.toString());
				System.out.println("indexSpin=" + indexSpin);
				System.out.println("nouvelleValeur=" + nouvelleValeur);
				System.out.println("dE=" + dE);
				//System.out.println("proba=" + (dE > 0 ? probas[dE] : 1.0));
			}

			if (lattice.getMagneticField()==0){
				// attempt move:
				if (dE <= 0.0 || createRandomNumber() <= probas[(int)dE]){
					acceptedMoves++;
					lattice.setSpinValue(indexSpin, nouvelleValeur);
					lattice.energy += dE;
					if (DEBUG) System.out.println("Acceptée !\n"+lattice.toString());
				}
			}
			else {
				if (dE <= 0.0 || createRandomNumber() <= Math.exp(-dE/kT)){
					acceptedMoves++;
					lattice.setSpinValue(indexSpin, nouvelleValeur);
					lattice.energy += dE;
					if (DEBUG) System.out.println("Acceptée !\n"+lattice.toString());
				}
				
			}
			if (DEBUG) {
				System.out.println("=======================================================");
				pause();
			}
		}
		samplesBag.addEnergySample(lattice.getEnergy()/lattice.size2);
		samplesBag.addMagnetizationSample(lattice.getMagnetization());
		if (latticeRG != null){ // means RG has been activated
			latticeRG[factorRG-1] = lattice.renormalize(latticeRG[factorRG-1]);
			for (int s=factorRG-2; s >= 0; s--){
				latticeRG[s] = latticeRG[s+1].renormalize(latticeRG[s]);
			}
			samplesBag.addEnergyRGSample(latticeRG[0].getEnergy()/latticeRG[0].size2);
		}
	}

	
}

