// MICHAEL TARTAGLIA
// MARCH 27 2003
// PROJECT CONCERNING EVOLUTIONARY PROGRAMMING
//	POPULATION.JAVA >>>
//		virtual population of people with a certain number of traits
//		that, when used, can exemplify the properties of evolution


import java.util.Random;

public class Population {
	private Random rand = new Random(12345);		// RANDOM SEED
	private int[][] people = new int[102][102];	// PEOPLE AND THEIR GENETIC CODE
	private int[][] offspr = new int[102][102];	// OFFSPRING OF INDIVIDUALS
	private boolean[] mated = new boolean[102];	// PAIR MATED BOOLEAN
	private int numberOfPeople, numberOfGenes, mutateFactor;
	private String dna = "AGTC";						// ALLOWABLE DNA STRANDS
	private boolean addMutator, addFitness;

	public Population(int nOP, int nOG, boolean mut, boolean fit, int mF) {
	 numberOfPeople = nOP;		// ORIGINAL POPULATION DENSITY
	 numberOfGenes = nOG;		// ORIGINAL GENE DENSITY
	 mutateFactor = (Math.abs(mF) > 100 ? 100 : Math.abs(mF));
	 mutateFactor = mutateFactor*numberOfGenes/100;
	 addMutator = mut;		// USE MUTATION EFFECTS ?
	 addFitness = fit;		// USE "SURVIVAL OF FITTEST" EFFECTS ?
	 for (int p = 0; p < numberOfPeople; ++p) {
	  for (int g = 0; g < numberOfGenes; ++g) {
	   people[p][g] = rand.nextInt(dna.length());	// GIVE PEOPLE TRAITS
	   //people[p][g] = (int)Math.floor(Math.random()*dna.length());
	 }}
	 for (int i = 0; i < numberOfPeople; ++i) {mated[i]=false;}
	}



	public void setGeneration()
	// INPUT & OUTPUT: nothing
	// FUNCTION: method takes pool of people and generates offspring
	{
	 int[] s1, s2;			// SELECTED PERSONS WITH TRAITS
	 int swap, personB, personA;	// SELECTED PERSONS' NAMES
	 for (int i = 0; i < numberOfPeople; ++i) {mated[i]=false;}   // RESET
	 for (int p = 0; p < numberOfPeople/2; p++) {

	  // GET RANDOM PAIR, AND MAKE SURE THEY'VE (a) NOT ALREADY PAIRED UP,
	  // ... AND (b) DON'T PAIR UP WITH THEMSELVES
	  do {personA = (int)Math.floor(Math.random()*numberOfPeople);}
	      while (mated[personA]);
	  do {personB = (int)Math.floor(Math.random()*numberOfPeople);}
	      while (mated[personB] && personB == personA);

	  // MAKE AN EXACT COPY OF THE PAIR, AND IN THAT COPIED PAIR, SWAP
	  // ... A RANDOM HALF OF THE TRAITS OF THAT PAIR (TO ENSURE 50% OF
	  // ... EACH PARENT'S TRAITS ARE PRESENT IN EACH OFFSPRING)
	  System.arraycopy(people[personA],0,offspr[personA],0,numberOfGenes);
	  System.arraycopy(people[personB],0,offspr[personB],0,numberOfGenes);
	  for (int g = 0; g < numberOfGenes; g++) {
	   g += (int)Math.round(Math.random());
	   swap = offspr[personA][g];
	   offspr[personA][g] = offspr[personB][g];
	   offspr[personB][g] = swap;
	  }

	  // IF THE OPTION IS CHOSEN BY THE USER, MUTATE THE OFFSPRING
	  if (addMutator) {
	   mutateCheck(offspr[personB]);
	   mutateCheck(offspr[personA]);
	  }

	  // IF THE OPTION IS CHOSEN BY THE USER, GET THE STRONGEST TWO MEMBERS
	  // ... OF THE FAMILY OF FOUR (2 PARENTS, 2 CHILDREN)
	  if (addFitness) {
	   s1 = strongest(offspr[personA],offspr[personB],
			  people[personA],people[personB]);
	   s2 = secStrong(offspr[personA],offspr[personB],
			  people[personA],people[personB], s1);
	  } else {	// ... OTHERWISE, JUST SELECT THE CHILDREN
	   s1 = offspr[personB];
	   s2 = offspr[personA];
	  }

	  // PLACE THE TWO "CHOSEN" PERSONS BACK INTO THE ORIGINAL POPULATION
	  System.arraycopy(s1,0,people[personA],0,numberOfGenes);
	  System.arraycopy(s2,0,people[personB],0,numberOfGenes);

	  // CONSIDER THE NEWLY PLACED PERSONS UNPAIRABLE
	  mated[personA] = true; mated[personB] = true;
	 }
	}



	private int[] strongest(int[] a, int[] b, int[] c, int[] d)
	// INPUT: 4 references to 4 "persons"
	// OUTPUT: the reference to the person
	// FUNCTION: finds the greatest strength of the four "persons"
	{
	 int as = strength(a),
	     bs = strength(b),
	     cs = strength(c),
	     ds = strength(d);
	 if (as >= bs && as >= cs && as >= ds) return a;
	 else if (bs >= as && bs >= cs && bs >= ds) return b;
	 else if (cs >= as && cs >= bs && cs >= ds) return c;
	 else return d;
	}



	private int[] secStrong(int[] a, int[] b, int[] c, int[] d, int[] S)
	// INPUT: 4 references to 4 "persons" and a reference to the strongest
	// OUTPUT: the reference to the second strongest person
	// FUNCTION: fins the person with a strength greater than or equal to
	//	     two others in the group and also has not been found to be
	//	     the strongest of the four people
	{
	 int as = strength(a),
	     bs = strength(b),
	     cs = strength(c),
	     ds = strength(d);
	 if (((as >= bs && as >= cs) || (as >= cs && as >= ds)
		|| (as >= bs && as >= ds)) && a != S) return a;
	 else if (((bs >= as && bs >= cs) || (bs >= cs && bs >= ds)
		|| (bs >= as && bs >= ds)) && b != S) return b;
	 else if (((cs >= bs && cs >= as) || (cs >= as && cs >= ds)
		|| (cs >= bs && cs >= ds)) && c != S) return c;
	 else return d;
	}



	private int strength(int[] a)
	 // INPUT: person
	 // OUTPUT: integer representing strength
	 // FUNCTION: calculates strength, = summation of the squares of each
	 //	      trait 
	{
	 int strength = 0;
	 for (int g = 0; g < numberOfGenes; ++g) strength += (a[g]*a[g]);
	 return strength;
	}



	private void mutateCheck(int[] x)
	// INPUT: x, passed by reference (no output, therefore)
	// FUNCTION: mutates random trait based on percentage to mutate
	{
	 int mIndex;		// INDEX OF TRAIT TO MUTATE
	 for (int i = 0; i <= mutateFactor; ++i) {
	  mIndex = (int)Math.floor((numberOfGenes)*Math.random());
	  x[mIndex] = (x[mIndex]+1)%dna.length();
	 }
	}



	public String[] getGeneration()
	// INPUT: nothing
	// OUTPUT: array of strings, displaying the traits of each person
	// FUNCTION: converts numbered traits (0-3) to text-based traits;
	//	     0 = A, 1 = G, 2 = T, 3 = C
	{
	 String[] out = new String[numberOfPeople];
	 String outstring = "";
	 for (int p = 0; p < numberOfPeople; ++p) {	// FOR EACH PERSON
	  outstring = "";
	  for (int g = 0; g < numberOfGenes; ++g) {	// FOR EACH TRAIT
	   outstring = outstring + dna.charAt(people[p][g]);
	  }
	  out[p] = outstring + "  " + strength(people[p]);
	 }
	 return out;
	}
}
