import java.lang.*;
import java.math.*;
import java.util.Random;

public class Beetlex {

	/*************************************************************************
	*
	* These values represent the dimensions of the model, and its general 
	* runtime behavior. We could, if we wanted more flexibility, read these 
	* values (and the next set) from the command-line (i.e. args[]), or some
	* other runtime input mechanism. As it stands, to change the assumptions 
	* and/or dimensions of the computational model, we have to change them 
	* below and recompile.
	*
	*************************************************************************/

	private static int rows = 40;
	private static int columns = 40;
	private static int runLength = 200;
	
	/*************************************************************************
	*
	* These values represent the different constants in our model; if we
	* change these, the model will behave differently.
	*
	*************************************************************************/

	private static int avgHealthyTrees = 45;
	private static int avgWeakTrees = 20;
	private static int eggsPerPair = 75;
	private static double avgFlightDistance = 2.5;
	private static double stdDevFlightDistance = 0.5;
	private static double windDirection = Math.PI; // In radians
	private static double windSpeed = 1;
	private static int minSuccessfulWeakAttack = 1;
	private static int minSuccessfulHealthyAttack = 100;
	private static int developmentalCycle = 26;
	private static int seedBeetlesPerWeek = 75;
	private static int seedPoints = 2;

	/*************************************************************************
	*
	* This is the "playing field" - the map on which we will place trees and
	* beetles.
	*
	*************************************************************************/
	
	private static Forest forest = new Forest (rows, columns, avgHealthyTrees, avgWeakTrees, developmentalCycle);

	/*************************************************************************
	*
	* This is the high-level heart of the model - the work performed at each
	* iteration. It would probably be more logical to make this a method of
	* the Forest class (since that is really where everything happens); of 
	* course, if we did that, students would know the joy of typing all this in.
	*
	*************************************************************************/

	static void iterate (int week) {
		forest.emerge (eggsPerPair, avgFlightDistance, stdDevFlightDistance, windDirection, windSpeed);
		System.out.println (forest.inventory (week));
		forest.infest (minSuccessfulWeakAttack, minSuccessfulHealthyAttack);
	}

	/*************************************************************************
	*
	* Any class which is the central class of a Java program MUST have a main 
	* method, with the following declaration:
	* 
	* public static void main (String[] args) {
	*	// Code goes here.
	* }
	*
	* Our main method contains the time loop of the process, which calls the
	* iterate method (as well as the Forst.emergeInitial method, until beetles
	* start emerging from the trees).
	*
	*************************************************************************/

	public static void main (String[] args) {
		for (int iterCount = 0; iterCount < runLength; iterCount++) {
			if (iterCount < developmentalCycle) {
				for (int seedIndex = 0; seedIndex < seedPoints; seedIndex++) {
					forest.emergeInitial (seedBeetlesPerWeek, avgFlightDistance, stdDevFlightDistance, windDirection, windSpeed);
				}
			}
			iterate (iterCount + 1);
		}
	}

}

/*****************************************************************************
*
* For clarity in presentation, this file contains not only the central program
* class, but also the private classes instantiated by the program. (When 
* multiple classes are included in one source file, only one of the classes 
* may be declared as "public".)
*
*****************************************************************************/

/*****************************************************************************
*
* Infestation is a simple class which encapsulates the pairs of beetles which
* which have burrowed a number of trees in the same cell of the forest grid.
* 
*****************************************************************************/

class Infestation {

	private int trees = 0;
	private int beetlePairs = 0;

	public Infestation () {
	}

	public Infestation (int trees, int beetlePairs) {
		this.trees = trees;
		this.beetlePairs = beetlePairs;
	}

	public int getTrees () {
		return trees;
	}

	public int getBeetlePairs () {
		return beetlePairs;
	}

}

/*****************************************************************************
*
* TreePatch is the class which encapsulates all the trees and beetles within
* one cell of the grid representing the forest. Since there may be hundreds of 
* trees, and thousands of beetles, in this single section of the forest, we 
* aren't managing those as object; instead we are keeping a tally on all of
* them, in this object.
* 
*****************************************************************************/

class TreePatch {

	private int gestation;
	private int healthyTrees;
	private int weakTrees;
	private int deadTrees;
	private int beetles;
	private Infestation healthyInfestation [];
	private Infestation weakInfestation [];

	public TreePatch (int gestation, int healthyTrees, int weakTrees) {
		Random rng = new Random ();
		this.gestation = gestation;
		this.healthyTrees = (int) (rng.nextGaussian () * Math.sqrt (1.0 * healthyTrees) + healthyTrees);
		this.weakTrees = (int) (rng.nextGaussian () * Math.sqrt (1.0 * weakTrees) + weakTrees);
		this.deadTrees = 0;
		healthyInfestation = new Infestation [gestation];
		weakInfestation = new Infestation [gestation];
		for (int gestationIndex = 0; gestationIndex < gestation; gestationIndex++) {
			healthyInfestation [gestationIndex] = new Infestation ();
			weakInfestation [gestationIndex] = new Infestation ();
		}
	}

	public void land () {
		this.beetles++;
	}

	public void land (int beetles) {
		this.beetles += beetles;
	}

	public void infest (int minSuccessfulWeakAttack, int minSuccessfulHealthyAttack) {
		int attacks;
		int healthyAttacks;
		int weakAttacks;
		Infestation newHealthyInfestation;
		Infestation newWeakInfestation;
		double avgPairs;
		int treesInfested;
		int totalTrees = this.healthyTrees + this.weakTrees;
		if (this.beetles > 0) {
			if (this.beetles > 2 * totalTrees) {
				attacks = totalTrees;
			}
			else {
				attacks = (int) (this.beetles / 2);
			}
			if (attacks > 0) {
				healthyAttacks = (int) (attacks * this.healthyTrees / totalTrees);
				weakAttacks = attacks - healthyAttacks;
				avgPairs = (0.5 * beetles) / attacks;
				if (avgPairs <= minSuccessfulWeakAttack - 1) {
					newHealthyInfestation = new Infestation (0, 0);
					newWeakInfestation = new Infestation (0, 0);
				}
				else if (avgPairs < minSuccessfulWeakAttack) {
					newHealthyInfestation = new Infestation (0, 0);
					treesInfested = (int) (weakAttacks * (avgPairs - (int) (avgPairs)));
					newWeakInfestation = new Infestation (treesInfested, treesInfested * minSuccessfulWeakAttack);
				}
				else if (avgPairs <= minSuccessfulHealthyAttack - 1) {
					newHealthyInfestation = new Infestation (0, 0);
					newWeakInfestation = new Infestation (weakAttacks, (int) (weakAttacks * avgPairs));
				}
				else if (avgPairs < minSuccessfulHealthyAttack) {
					treesInfested = (int) (healthyAttacks * (avgPairs - (int) (avgPairs)));
					newHealthyInfestation = new Infestation (treesInfested, treesInfested * minSuccessfulHealthyAttack);
					newWeakInfestation = new Infestation (weakAttacks, (int) (weakAttacks * avgPairs));
				}
				else {
					newHealthyInfestation = new Infestation (healthyAttacks, (int) (healthyAttacks * avgPairs));
					newWeakInfestation = new Infestation (weakAttacks, (int) (weakAttacks * avgPairs));
				}
			}
			else {
				newHealthyInfestation = new Infestation (0, 0);
				newWeakInfestation = new Infestation (0, 0);
			}
		}
		else {
			newHealthyInfestation = new Infestation (0, 0);
			newWeakInfestation = new Infestation (0, 0);
		}
		healthyTrees -= newHealthyInfestation.getTrees ();
		weakTrees -= newWeakInfestation.getTrees ();
		healthyInfestation [gestation - 1] = newHealthyInfestation;
		weakInfestation [gestation - 1] = newWeakInfestation;
		this.beetles = 0;
	}

	public int emerge (int eggsPerPair) {
		int beetlePairs = healthyInfestation [0].getBeetlePairs () + weakInfestation [0].getBeetlePairs ();
		deadTrees += healthyInfestation [0].getTrees () + weakInfestation [0].getTrees ();
		for (int infestIndex = 1; infestIndex < gestation; infestIndex++) {
			healthyInfestation [infestIndex - 1] = healthyInfestation [infestIndex];
			weakInfestation [infestIndex - 1] = weakInfestation [infestIndex];
		};
		return (beetlePairs * eggsPerPair);
	}

	public int getHealthyTrees () {
		return this.healthyTrees;
	}

	public int getWeakTrees () {
		return this.weakTrees;
	}

	public int getDeadTrees () {
		return this.deadTrees;
	}

	public int getBeetles () {
		return this.beetles;
	}

}

/*****************************************************************************
*
* The Forest class contains a grid of TreePatch cells; this class "knows" how 
* to iterate across the rows and columns - to "plant" the trees, to "seed" the 
* forest with the initial beetle population, to move the beetles that emerge
* in one cell to another, etc. It also knows how to take an "inventory" of the
* state of the forest (number of beetles, healthy trees, weak trees, etc.
* 
*****************************************************************************/

class Forest {

	private int rows;
	private int columns;
	private TreePatch patches [][];

	public Forest (int rows, int columns, int avgHealthyTrees, int avgWeakTrees, int gestation) {
		this.rows = rows;
		this.columns = columns;
		this.patches = new TreePatch [rows][columns];
		for (int row = 0; row < rows; row++) {
			for (int column = 0; column < columns; column++) {
				this.patches [row][column] = new TreePatch (gestation, avgHealthyTrees, avgWeakTrees);
			}
		}
	}
	
	public void emergeInitial (int beetles, double avgFlight, double stdDevFlight, double windDirection, double windSpeed) {
		int endingX;
		int endingY;
		double flightDistance;
		double flightDirection;
		Random rng = new Random ();
		double startingX = 0.5 + (int) (Math.random () * columns);
		double startingY = 0.5 + (int) (Math.random () * rows);
		for (int beetleIndex = 0; beetleIndex < beetles; beetleIndex++) {
			flightDirection = Math.random () * 2 * Math.PI;
			flightDistance = rng.nextGaussian () * stdDevFlight + avgFlight;
			endingX = (int) (Math.round (startingX + Math.cos (flightDirection) * flightDistance + Math.cos (windDirection) * windSpeed));
			endingY = (int) (Math.round (startingY + Math.sin (flightDirection) * flightDistance + Math.sin (windDirection) * windSpeed));
			while (endingX < 0) {
				endingX += columns;
			}
			while (endingX >= columns) {
				endingX -= columns;
			}
			while (endingY < 0) {
				endingY += rows;
			}
			while (endingY >= rows) {
				endingY -= rows;
			}
			patches [endingY][endingX].land ();
		}		
	}

	public void emerge (int eggsPerPair, double avgFlight, double stdDevFlight, double windDirection, double windSpeed) {
		int beetles;
		double startingX;
		double startingY;
		int endingX;
		int endingY;
		double flightDistance;
		double flightDirection;
		Random rng = new Random ();
		for (int rowIndex = 0; rowIndex < rows; rowIndex++) {
			startingY = 0.5 + rowIndex;
			for (int colIndex = 0; colIndex < rows; colIndex++) {
				startingX = 0.5 + colIndex;
				beetles = patches [rowIndex][colIndex].emerge (eggsPerPair);
				for (int beetleIndex = 0; beetleIndex < beetles; beetleIndex++) {
					flightDirection = Math.random () * 2 * Math.PI;
					flightDistance = rng.nextGaussian () * stdDevFlight + avgFlight;
					endingX = (int) (Math.round (startingX + Math.cos (flightDirection) * flightDistance + Math.cos (windDirection) * windSpeed));
					endingY = (int) (Math.round (startingY + Math.sin (flightDirection) * flightDistance + Math.sin (windDirection) * windSpeed));
					while (endingX < 0) {
						endingX += columns;
					}
					while (endingX >= columns) {
						endingX -= columns;
					}
					while (endingY < 0) {
						endingY += rows;
					}
					while (endingY >= rows) {
						endingY -= rows;
					}
					patches [endingY][endingX].land ();
				}
			}
		}
	}

	public void infest (int minSuccessfulHealthyAttack, int minSuccessfulWeakAttack) {
		for (int row = 0; row < rows; row++) {
			for (int column = 0; column < columns; column++) {
				patches [row][column].infest (minSuccessfulHealthyAttack, minSuccessfulWeakAttack);
			}
		}
	}

	public String inventory (int week) {
		int healthyTrees = 0;
		int weakTrees = 0;
		int deadTrees = 0;
		int beetles = 0;
		for (int row = 0; row < rows; row++) {
			for (int column = 0; column < columns; column++) {
				healthyTrees += patches [row][column].getHealthyTrees ();
				weakTrees += patches [row][column].getWeakTrees ();
				deadTrees += patches [row][column].getDeadTrees ();
				beetles += patches [row][column].getBeetles ();
			}
		}
		return (
			"\nWeek: " + week 
			+ "\nBeetles: " + beetles 
			+ "\nHealthy trees: " + healthyTrees 
			+ "\nWeak trees: " + weakTrees
			+ "\nDead trees: " + deadTrees
		);
	}

}        