public class SRLARSJava extends FitnessFunction {

    private final DataJava data;
    private int pow;
    private final boolean USE_INT;
    
    /**
     * Fitness key
     */
    public static String FITNESS_KEY = Parameters.Operators.SR_JAVA_FITNESS;
    
    /**
     * Maximizing function
     */
    public Boolean isMaximizingFunction = true;
    private int numThreads;
    /**
     * Create a new fitness operator, using the provided data, for assessing
     * individual solutions to Symbolic Regression problems. There are two
     * parameters for this fitness evaluation:
     * <p>
     * <ul>
     * <li>The power <i>p</i> to use in computing the mean of the errors. See
     * {@link #getMeanFromP(int)} for recognized values and more information.
     * This parameter is set to the value specified by the key
     * {@value algorithm.Parameters.Names#MEAN_POW} in the properties file. The
     * default value is {@value algorithm.Parameters.Defaults#MEAN_POW}.
     * <li>A boolean flag specifying if the predictions should be forced to
     * integers because the output variable is integer-valued (ie you might want
     * to set this to true if your output is integer-valued). This parameter is
     * set to the boolean at the key
     * {@value algorithm.Parameters.Names#COERCE_TO_INT}. If there is no value
     * specified, the default is
     * {@value algorithm.Parameters.Defaults#COERCE_TO_INT}.
     * </ul>
     * 
     * @param data
     *            The dataset (training cases, output variable) to use in
     *            computing the fitness of individuals.
     */
    public SRLARSJava(DataJava data) {
        this(data, 2, false,1);
    }
    
    /**
     * Constructor that allows to specify the number of threads for parallel evaluation
     * @param aData
     * @param aPow
     * @param is_int
     * @param anumThreads 
     */
    public SRLARSJava(DataJava aData, int aPow, boolean is_int,int anumThreads) {
        this.data = aData;
        pow = aPow;
        USE_INT = is_int;
        numThreads = anumThreads;
    }
    /**
     * Should this fitness function be minimized (i.e. mean squared error) or
     * maximized?
     * @return 
     */
    @Override
    public Boolean isMaximizingFunction() {
        return this.isMaximizingFunction;
    }

    /**
     * Simple "factory"-like method for returning the proper generalized mean
     * object to use, given the parameter p (see
     * http://en.wikipedia.org/wiki/Generalized_mean). Since we are only
     * concerned with means of errors, we don't utilize all the valid values of
     * p defined for generalized means.
     * 
     * @param p
     *            power to use in computing a mean
     * @return an instance of {@link PowerMean} if p > 1; an instance of
     *         {@link ArithmeticMean} if p == 1; otherwise, an instance of
     *         {@link Maximum}.
     */
    public static Mean getMeanFromP(int p) {
        if (p == 1) {
            return new ArithmeticMean();
        } else if (p > 1) {
            return new PowerMean(p);
        } else {
            return new Maximum();
        }
    }

    /**
     * @param ind
     * @throws java.lang.Exception
     * @see Function
     */
    public void eval(Individual ind) throws Exception {
        
        Tree genotype = (Tree) ind.getGenotype();
        Mean MEAN_FUNC = getMeanFromP(pow);
        Function func = genotype.generate();
        List<Double> d;
        ArrayList<Double> interVals;
        double[][] inputValuesAux = data.getInputValues();
        Tree tAux = (Tree) ind.getGenotype();
        //double[] targetAux = data.getScaledTargetValues();
        double[] targetAux = data.getTargetValues();
        float[][] intermediateValues = new float[data.getNumberOfFitnessCases()][tAux.getSize()];
        for (int i = 0; i < data.getNumberOfFitnessCases(); i++) {
            d = new ArrayList<Double>();
            for (int j = 0; j < data.getNumberOfFeatures(); j++) {
                d.add(j, inputValuesAux[i][j]);
            }
            interVals = new ArrayList<Double>();
            Double val = func.evalIntermediate(d,interVals);
            for(int t=0;t<interVals.size();t++){
                intermediateValues[i][t] = interVals.get(t).floatValue();
            }
            d.clear();
            interVals.clear();
        }

        /*
         * LassoFitGenerator is initialized
         */
        LassoFitGenerator fitGenerator = new LassoFitGenerator();
        int numObservations = data.getNumberOfFitnessCases();
        fitGenerator.init(tAux.getSize(), numObservations);
        for (int i = 0; i < numObservations; i++) {
            fitGenerator.setObservationValues(i,intermediateValues[i] );
            fitGenerator.setTarget(i, targetAux[i]);
        }

        /*
         * Generate the Lasso fit. The -1 arguments means that
         * there would be no limit on the maximum number of 
         * features per model
         */
        LassoFit fit = fitGenerator.fit(-1);

        
        // We pick the first value of lamda that includes all the features in the model
        int indexWeights = 0;
        int usedVars=0;
        for(int i=0;i<fit.lambdas.length;i++){
            if(fit.nonZeroWeights[i]>usedVars){
                indexWeights = i;
                usedVars = fit.nonZeroWeights[i];
            }
        }
        ArrayList<String> alWeights = new ArrayList<String>();
        double lassoIntercept = 0;
        double fitness = 0;
        //if(fit.nonZeroWeights[indexWeights]==tAux.getSize()){
            //double[] lassoWeights = fit.compressedWeights[indexWeights];
            double[] lassoWeights = fit.getWeights(indexWeights);
            for(int j=0;j<lassoWeights.length;j++){
                alWeights.add(Double.toString(lassoWeights[j]));
            }
            lassoIntercept = fit.intercepts[indexWeights];
            //SRPhenotype phenotype_tmp = new SRPhenotype();
            for (int i = 0; i < data.getTargetValues().length; i++) {
                double prediction = 0;
                for(int j=0;j<lassoWeights.length;j++){
                    prediction += intermediateValues[i][j]*lassoWeights[j];
                }
                prediction += lassoIntercept;
                //phenotype_tmp.addNewDataValue(prediction);
                if (this.USE_INT) {
                    prediction = Math.round(prediction);
                }
                MEAN_FUNC.addValue(Math.abs(targetAux[i] - prediction));
            }
            /*final ScaledTransform scalePrediction = new ScaledTransform(phenotype_tmp.min, phenotype_tmp.max);
            final ScaledTransform scaleTarget = new ScaledTransform(this.data.getTargetMin(), this.data.getTargetMax());
            for (int i = 0; i < this.getTarget().length; i++) {
                Double scaled_val = scalePrediction.scaleValue(phenotype_tmp.getDataValue(i));
                if (this.USE_INT) {
                    int rounded_val = scaleTarget.unScaleValue(scaled_val).intValue();
                    scaled_val = scaleTarget.scaleValue((double) rounded_val);
                }
                MEAN_FUNC.addValue(Math.abs(targetAux[i] - scaled_val));
            }
            phenotype_tmp = null;
            */
            Double error = MEAN_FUNC.getMean();
            fitness=errorToFitness(error);
            
        /*}else{
            for(int j=0;j<tAux.getSize();j++){
                alWeights.add("0");
            }
        }*/
        ind.setWeights(alWeights);
        ind.setLassoIntercept(Double.toString(lassoIntercept));
        ind.setFitness(SRLARSJava.FITNESS_KEY, fitness);
}
    
    /**
     * Transform errors to fitness values. For errors, smaller values are
     * better, while for fitness, values closer to 1 are better. This particular
     * transformation also places a greater emphasis on small changes close to
     * the solution (fitness == 1.0 represents a complete solution). However,
     * this transformation also assumes that the error is in the range [0,1].
     * 
     * @param error
     *            Error on training set; value in range [0,1].
     * @return 0 if error is not a number (NaN) or outside of the range [0,1].
     *         Otherwise, return the fitness.
     */
    private Double errorToFitness(Double error) {
        if(error==0){
            return 1.0;
        }else if (error.isNaN() || error < 0.0 ) {
            return 0.0;
        } else {
            return (1.0) / error;
        }
    }

    @Override
    public void evalPop(Population pop) {
        
        ArrayList<SRJavaThread> alThreads = new ArrayList<SRJavaThread>();
        for(int i=0;i<numThreads;i++){
            SRJavaThread threadAux = new SRJavaThread(i, pop,numThreads);
            alThreads.add(threadAux);
        }
        
        for(int i=0;i<numThreads;i++){
            SRJavaThread threadAux = alThreads.get(i);
            threadAux.start();
        }
        
        for(int i=0;i<numThreads;i++){
            SRJavaThread threadAux = alThreads.get(i);
            try {
                threadAux.join();
            } catch (InterruptedException ex) {
                Logger.getLogger(SRLARSJava.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        

    }

    /**
     * @return the fitnessCases
     */
    public double[][] getFitnessCases() {
        return data.getInputValues();
    }

    /**
     * @return the scaled_target
     */
    public double[] getTarget() {
        return data.getScaledTargetValues();
    }

    /**
     * Utility class for computing the element-wise normalization of a vector.
     * That is, for a given vector <code>A</code>, where all values are in the
     * range <code>[min(A), max(A)]</code>, we want an easy way to compute the
     * scaled vector <code>B</code> such that every value in B is in the range
     * <code>[0, 1]</code>.
     * <p>
     * In particular, if the ith element of <code>A</code> has value
     * <code>a_i</code>, then the ith element of <code>B</code> will have the
     * value <code>(a_i-min(A))/(max(A)-min(A))</code>.
     * <p>
     * Also, provide the inverse transformation, to recover the values of
     * <code>A</code>.
     * 
     * @author Owen Derby
     * @see ScaledData
     */
    private class ScaledTransform {
        private final double min, range;

        public ScaledTransform(double min, double max) {
            this.min = min;
            this.range = max - min;
        }

        public Double scaleValue(Double val) {
            return (val - min) / range;
        }

        public Double unScaleValue(Double scaled_val) {
            return (scaled_val * range) + min;
        }
    }
    
    /**
     * Auxiliary thread class to perform parallel evaluation of the population
     * Each thread evaluates a subset of the populaiton
     */
    public class SRJavaThread extends Thread{
        private int indexThread, totalThreads;
        private Population pop;
        
        /**
         * Constructor of the thread class
         * @param anIndex
         * @param aPop
         * @param aTotalThreads 
         */
        public SRJavaThread(int anIndex, Population aPop,int aTotalThreads){
            indexThread = anIndex;
            pop = aPop;
            totalThreads = aTotalThreads;
        }

        
        /**
         * Each thread evaluates a subset of the population.
         * The subset is selected with the indices of the thread and that of individuals
         */
        @Override
        public void run(){
            int indexIndi = 0;
            for (Individual individual : pop) {
                if(indexIndi%totalThreads==indexThread){
                    try {
                        eval(individual);
                    } catch (Exception ex) {
                        Logger.getLogger(SRLARSJava.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                indexIndi++;
            }
        }
     }
        
} public class Population extends ArrayList<Individual> implements Serializable {
	private static final long serialVersionUID = 6111020814262385165L;

        /**
         * default constructor
         */
	public Population() {
		super();
	}

	/**
         * a constructor which yields the combination of populations
         * @param populations 
         */
        public Population(Population... populations) {
            super();
            for (Population population : populations) {
                this.addAll(population);
            }
	}

        /**
         * equals method
         * @param other
         * @return 
         */
	@Override
	public boolean equals(Object other) {
		if (!this.getClass().equals(other.getClass()))
			return false;
		ArrayList<Individual> otherL = (ArrayList<Individual>) other;
		if (otherL.size() != this.size())
			return false;
		for (int i = 0; i < otherL.size(); i++) {
			if (!this.get(i).equals(otherL.get(i)))
				return false;
		}
		return true;
	}

        /**
         * perform a deep copy of the population
         * @return 
         */
	public Population deepCopy() {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos;
		try {
			oos = new ObjectOutputStream(bos);
			oos.writeObject(this);
			ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
			return (Population) ois.readObject();
		} catch (IOException e) {
			System.err.println("IO Exception in deepCopy");
		} catch (ClassNotFoundException e) {
			System.err.println("Class not found Exception in deepCopy");
		}
		return null;
	}

	/**
	 * Calculates the euclidean distance of all individuals in the first front. Others will keep a distance of Double.MAX_VALUE
	 * @param fitnessFunctions
	 */
	public void calculateEuclideanDistances(LinkedHashMap<String, FitnessFunction> fitnessFunctions) {
		// first get the mins and maxes for the first front only
		LinkedHashMap<String, Double>[] minMax = getMinMax(this, fitnessFunctions, true);
		LinkedHashMap<String, Double> mins = minMax[0];
		LinkedHashMap<String, Double> maxes = minMax[1];
		// convert the mins and maxes to standardized form
		for (String key : mins.keySet()) {
			// get new standardized min and max. swap values if this fitness function isn't minimizing already
			if (fitnessFunctions.get(key).isMaximizingFunction()) { // swap min and max since they've both been inverted
				Double standardizedMin = FitnessComparisonStandardizer.getFitnessForMinimization(maxes, key, fitnessFunctions);
				Double standardizedMax = FitnessComparisonStandardizer.getFitnessForMinimization(mins, key, fitnessFunctions);
				mins.put(key, standardizedMin);
				maxes.put(key, standardizedMax);
			} else {
				Double standardizedMin = FitnessComparisonStandardizer.getFitnessForMinimization(mins, key, fitnessFunctions);
				Double standardizedMax = FitnessComparisonStandardizer.getFitnessForMinimization(maxes, key, fitnessFunctions);
				mins.put(key, standardizedMin);
				maxes.put(key, standardizedMax);
			}
		}
		// create the ranges needed for scaling
		LinkedHashMap<String, Double> ranges = new LinkedHashMap<String, Double>();
		for (String key : mins.keySet()) {
			ranges.put(key, Math.abs(maxes.get(key) - mins.get(key)));
		}
		// compute euclidean distances for the first front only
		for (Individual individual : this) {
			if (individual.getDominationCount() > 0) continue;
			individual.calculateEuclideanDistance(fitnessFunctions, mins, ranges);
		}
	}
	
        /**
	 * Find the min and max value for each fitness function
	 * 
	 * @param pop
         * @param fitnessFunctions
	 * @param onlyFirstFront if true, mins/maxes calculated only for individuals in the first front
         * @return 
	 */
	public static LinkedHashMap<String, Double>[] getMinMax(Population pop,LinkedHashMap<String, FitnessFunction> fitnessFunctions, Boolean onlyFirstFront) {
		LinkedHashMap<String, Double>[] minMax = new LinkedHashMap[2];
		LinkedHashMap<String, Double> mins = new LinkedHashMap<String, Double>();
		LinkedHashMap<String, Double> maxes = new LinkedHashMap<String, Double>();
		
		// establish order of fitness functions in mins and maxes
		for (String id : fitnessFunctions.keySet()) {
			mins.put(id, pop.get(0).getFitness(id));
			maxes.put(id, pop.get(0).getFitness(id));
		}

		// find mins and maxes
		for (Individual i : pop) {
			if (onlyFirstFront && i.getDominationCount() > 0) continue;
			for (String funcName : i.getFitnessNames()) {
				Double iFitness = i.getFitness(funcName);
				if (iFitness < mins.get(funcName)) // lower min
					mins.put(funcName, iFitness);
				if (iFitness > maxes.get(funcName)) // higher max
					maxes.put(funcName, iFitness);
			}
		}
		minMax[0] = mins;
		minMax[1] = maxes;
		return minMax;
	}
	
        /**
         * Non-dominating sort of the population
         */
        public void sort() {
            Collections.sort(this, new DominationCrowdingSortComparator(false));
	}

    /**
     * 
     * @return 
     */
    @Override
    public int hashCode() {
        int hash = 7;
        return hash;
    }
        
        /**
         * Comparator to compare the domination relationship between individuals of the population
         */
	public class DominationCrowdingSortComparator implements Comparator<Individual> {
		// can't use crowding distance for comparison if it's not being computed a level above
		boolean crowdingDistanceEnabled;

                /**
                 * default constructor
                 */
		public DominationCrowdingSortComparator() {
			this(false);
		}
                
                /**
                 * whether to use crowding distance to break ties
                 * @param _crowdingDistanceEnabled 
                 */
		public DominationCrowdingSortComparator(boolean _crowdingDistanceEnabled) {
			crowdingDistanceEnabled = _crowdingDistanceEnabled;
		}
		
                /**
                 * compare two individuals
                 * @param a
                 * @param b
                 * @return 
                 */
		@Override
		public int compare(Individual a, Individual b) {
			Integer ad = a.getDominationCount();
			Integer bd = b.getDominationCount();
			// sort by domination count: lower is better
			if (ad > bd) return 1;
			else if (ad < bd) return -1;
			// don't proceed to crowding distance sorting if we're not computing crowding distance
			if (!crowdingDistanceEnabled) return 0;
			// now sort by crowding distance: higher is better
			Double ac = a.getCrowdingDistance();
			Double bc = b.getCrowdingDistance();
			if (ac < bc) return 1;
			else if (ac > bc) return -1;
			else return 0;
		}
	}
	
        /**
         * this class sorts individuals according to their fitness in validation data
         */
	public class CrossValSortComparator implements Comparator<Individual>{
                /**
                 * default constructor
                 */
		public CrossValSortComparator() {
		}
		
		/**
                 * compare 2 individuals
                 * @param a
                 * @param b
                 * @return 
                 */
                @Override
		public int compare(Individual a, Individual b) {
			double aCVF = a.getCrossValFitness();
			double bCVF = b.getCrossValFitness();
			// sort by domination count: lower is better
                        if (aCVF == bCVF) {
                            return 0;
                        } else if (aCVF > bCVF){
                            return 1;
                        }else {
                            return -1;
                        }
		}
	}
        
        /**
         * sort individuals according to non-domination
         * @param crowdingSortEnabled 
         */
	public void sort(boolean crowdingSortEnabled) {
            Collections.sort(this, new DominationCrowdingSortComparator(crowdingSortEnabled));
	}
        
        /**
         * sort individuals according to the fitness on validation
         */
        public void sortCrossVal() {
            Collections.sort(this, new CrossValSortComparator());
	}
                
} public final class Parameters {
    /**
     * Names used to identify properties in the properties file. Every key in
     * the properties file is matched against these strings by
     * {@link AlgorithmBase} to extract the new property value.
     * 
     * @author Owen Derby
     */
    public final static class Names {

        public static final String MUTATION_RATE = "mutation_rate";
        public static final String XOVER_RATE = "xover_rate";
        public static final String POP_SIZE = "pop_size";
        public static final String NUM_GENS = "num_gens";
        public static final String TIME_OUT = "timeout";
        public static final String PROBLEM = "problem";
        public static final String CROSS_VAL_SET = "cross_validation_set";
        public static final String PROBLEM_TYPE = "problem_type";
        public static final String PROBLEM_SIZE = "problem_size";
        public static final String EXTERNAL_THREADS = "external_threads";
        public static final String FUNCTION_SET = "function_set";
        public static final String FUNCTION_SET_SIZE = "function_set_size";
        public static final String UNARY_FUNCTION_SET = "unary_function_set";
        public static final String TERMINAL_SET = "terminal_set";
        public static final String MUTATE = "mutate_op";
        public static final String XOVER = "xover_op";
        public static final String SELECTION = "selection_op";
        public static final String FITNESS = "fitness_op";
        public static final String FALSE_NEGATIVE_WEIGHT = "false_negative_weight";
        public static final String INITIALIZE = "initialize_op";
        public static final String EQUALIZER = "equalizer_op";
        public static final String BIN_WIDTH = "bin_width";
        public static final String NUM_TRIALS = "num_trials";
        public static final String SEED = "rng_seed";
        public static final String TREE_INIT_MAX_DEPTH = "tree_initial_max_depth";
        public static final String MEAN_POW = "fitness_mean_pow";
        /**
         * Is the output variable for our problem integer-valued, such that we
         * can "cheat" and force our models to output integer values?
         */
        public static final String COERCE_TO_INT = "integer_fitness";
        public static final String BROOD_SIZE = "brood_size";
        public static final String KOZA_FUNC_RATE = "koza_function_rate";
        public static final String TREE_XOVER_MAX_DEPTH = "tree_xover_max_depth";
        public static final String TREE_XOVER_TRIES = "tree_xover_tries";
        public static final String TREE_MUTATE_MAX_DEPTH = "tree_mutate_max_depth";
        public static final String TOURNEY_SIZE = "tourney_size";
        /**
         * For multi-objective optimization
         */
        // methods of sorting within fronts
        public static final String FRONT_RANK_METHOD = "front_rank_method";
        public static final String EUCLIDEAN = "euclidean"; // use euclidean
        public static final String FIRST_FITNESS = "first_fitness"; // use first
        /**
         * The main json log
         */
        public static final String JSON_LOG_PATH= "json_path";
        /**
         * For logging the population
         */
        public static final String POP_SAVE_FILENAME = "pop_save_filename";
        public static final String POP_LOAD_FILENAME = "pop_load_filename";
        public static final String POP_DATA_PATH = "pop_data_path";
        public static final String POP_DATA_PATH_PREFIX = "pop_data_path_prefix";
        /**
         * For logging models
         */
        public static final String MODELS_PATH = "models_path";	
        /**
         * Enable population logging?
         */
        public static final String ENABLE_POP_LOG = "enable_pop_log";
        /**
         * In what format should tree models be displayed?
         */
        public static final String PREFIX_SCHEME = "prefix";
        public static final String INFIX_MATLAB = "infix";
        /**
         * Should JSON logs be enabled?
         */
        public static final String SAVE_JSON = "save_json";
    }

    /**
     * Names for specific operators, as understood by the library when reading
     * in values from the properties file.
     * 
     * @author Owen Derby
     */
    public final static class Operators {

        // FITNESS values

        // Symbolic Regression
        public static final String SR_JAVA_FITNESS = "fitness.SRFitness.Java";
        public static final String SR_CPP_FITNESS = "fitness.SRFitness.Cpp";
        public static final String SR_CUDA_FITNESS = "fitness.SRFitness.Cuda";
        public static final String SR_CUDA_FITNESS_DUAL = "fitness.SRFitness.CudaDual";
        public static final String SR_CUDA_FITNESS_CORRELATION = "fitness.SRFitness.CudaCorrelation";
        public static final String SR_CUDA_FITNESS_CORRELATION_DUAL = "fitness.SRFitness.CudaCorrelationDual";

        // GPFunction Classification
        public static final String GPFUNCTION_JAVA = "fitness.GPFunctionFitness.Java";
        public static final String GPFUNCTION_CV_JAVA = "fitness.GPFunctionCVFitness.Java";
        public static final String GPFUNCTION_CPP = "fitness.GPFunctionFitness.Cpp";
        public static final String GPFUNCTION_CUDA = "fitness.GPFunctionFitness.Cuda";
        public static final String GPFUNCTION_CV_CPP = "fitness.GPFunctionCVFitness.Cpp";
        public static final String GPFUNCTION_CV_CUDA = "fitness.GPFunctionCVFitness.Cuda";
        public static final String GPFUNCTION_PRED_CUDA = "fitness.GPFunctionPredFitness.Cuda";
        
        // GPFunction-KDE Classification
        public static final String GPFUNCTION_KDE_JAVA = "fitness.GPFunctionKDEFitness.Java";
        public static final String GPFUNCTION_KDE_CPP = "fitness.GPFunctionKDEFitness.Cpp";

        // RULE TREE CLASSIFIER
        public static final String RT_COST_JAVA_FITNESS = "fitness.RT_Cost_Fitness.Java";
        public static final String RT_MO_JAVA_FITNESS = "fitness.RT_MO_Fitness.Java";
        public static final String RT_FP_JAVA_FITNESS = "fitness.RT_FP_Fitness.Java";
        public static final String RT_FN_JAVA_FITNESS = "fitness.RT_FN_Fitness.Java";
        
        // Complexity
        public static final String SUBTREE_COMPLEXITY_FITNESS = "fitness.SubtreeComplexity";

        // INITIALIZE values
        public static final String TREE_INITIALIZE = "operator.TreeInitialize";

        // SELECTION values
        public static final String TOURNEY_SELECT = "operator.TournamentSelection";
        public static final String CROWD_SELECT = "operator.CrowdedTournamentSelection";

        // EQUALIZER values
        public static final String TOURNEY_EQUAL = "operator.TournamentEqualization";
        public static final String TREE_DYN_EQUAL = "operator.TreeDynamicEqualizer";
        public static final String DUMB_EQUAL = "operator.DummyEqualizer";
        public static final String DUMB_TREE_EQUAL = "operator.DummyTreeEqualizer";

        // MUTATE values
        //public static final String SUBTREE_MUTATE = "operator.SubtreeMutate";
        public static final String SUBTREE_MUTATE = "operator.SubtreeMutateConstants";

        // XOVER values
        public static final String BROOD_XOVER = "operator.BroodSelection";
        // single point uniform crossover
        public static final String SPU_XOVER = "operator.SinglePointUniformCrossover";
        // single point Koza crossover
        public static final String SPK_XOVER = "operator.SinglePointKozaCrossover";
    }

    /**
     * All default values for running the library.
     * <p>
     * To specify other values, please use the properties file.
     * 
     * @author Owen Derby
     */
    public final static class Defaults {
        /**
         * verbosity flag. Helpful for debugging.
         */
        public static final Boolean VERBOSE = false;

        
        public static final int POP_SIZE = 1000;
        public static final int NUM_GENS = 10000;
        public static final int TIME_OUT = 60;
        // Frequency for selecting each operator
        public static final double MUTATION_RATE = 0.1;
        public static final double XOVER_RATE = 0.5;
        // reproduction/replication frequency is implicitly defined as
        // (1 - XOVER_RATE - MUTATION_RATE)

        /**
         * number of best individuals to carry over to next generation
         */
        public static final int ELITE = 3;
        // public static final int NUM_TRIALS = 30;
        public static final int TREE_INIT_MAX_DEPTH = 17;
        public static final int BIN_WIDTH = 5;
        /**
         * The power p to use in the power mean for computing the absolute
         * error.
         * 
         * @see Mean
         */
        public static final int MEAN_POW = 2;
        public static final int BROOD_SIZE = 1;
        // default value of 90% suggested by Koza.
        public static final double KOZA_FUNC_RATE = .9;
        public static final int TREE_XOVER_MAX_DEPTH = 17;
        public static final int TREE_XOVER_TRIES = 10;
        public static final int TREE_MUTATE_MAX_DEPTH = 17;
        public static final int TOURNEY_SIZE = 7;
        

        public static final int PROBLEM_SIZE = 3;
        public static final String PROBLEM_TYPE = "SRFunction";
        public static final String SRFITNESS = "fitness.SRFitness.Java, fitness.SubtreeComplexity";
        public static final String GPFUNCTION_FITNESS = "fitness.GPFunctionFitness.Java, fitness.SubtreeComplexity";
        public static final String GPFUNCTIONKDE_FITNESS = "fitness.GPFunctionKDEFitness.Java, fitness.SubtreeComplexity";
        public static final String RULETREE_FITNESS = "fitness.RT_MO_Fitness.Java, fitness.SubtreeComplexity";
        public static final String PROBLEM = "ProblemData/TrainDatasetBalanced2.txt";
        public static final String CROSS_VAL_SET = "ProblemData/TrainDatasetBalanced2.txt";

        public static final int EXTERNAL_THREADS = 4;
        public static final int TARGET_NUMBER = 1;
        
        public static final double FALSE_NEGATIVE_WEIGHT = 0.5;
        /**
         * the initial seed to use for the rng in the algorithm.
         */
        public static final long SEED = System.currentTimeMillis();
        /**
         * Normally regression is over real numbers. Sometimes we want to do
         * regression over Integers. Set this to true to do so.
         */
        public static final boolean COERCE_TO_INT = false;

        public static final String INITIALIZE = Operators.TREE_INITIALIZE;
        public static final String SELECT = Operators.CROWD_SELECT;
        public static final String MUTATE = Operators.SUBTREE_MUTATE;
        public static final String EQUALIZER = Operators.TREE_DYN_EQUAL;
        public static final String XOVER = Operators.SPU_XOVER;
        /**
         * To handle support for multiple fitness functions, this field can be
         * filled with any number of comma-separated fitness operator class
         * names
         */
        public static final List<String> TERMINALS;
        static {
                TERMINALS = new ArrayList<String>();
                TERMINALS.add("X1");
        }
        public static final List<String> FUNCTIONS;
        static {
                FUNCTIONS = new ArrayList<String>();
                FUNCTIONS.add("+");
                FUNCTIONS.add("*");
                FUNCTIONS.add("-");
                FUNCTIONS.add("mydivide");
                
                FUNCTIONS.add("mylog");
                FUNCTIONS.add("exp");
                FUNCTIONS.add("sin");
                FUNCTIONS.add("cos");
                FUNCTIONS.add("sqrt");
                FUNCTIONS.add("square");
                FUNCTIONS.add("cube");
                FUNCTIONS.add("quart");
        }
        
        // the main JSON log's default name
        public static final String JSON_LOG_PATH= "evogpj-log.json";

        // TODO dylan clear up this mess
        // for serializing the population
        public static final String POP_SAVE_FILENAME = "evogpj-population.ser";

        // for deserializing the population
        public static final String POP_LOAD_FILENAME = POP_SAVE_FILENAME;

        // controls the location at which to save population dumps
        public static final String POP_DATA_PATH_PREFIX = "populationLog";
        public static final String POP_DATA_PATH = POP_DATA_PATH_PREFIX
                        + "-unspecifiedAlgorighmType-unspecifiedSeed.txt";

        /**
         * For logging models
         */
        public static final String MODELS_PATH = "bestModelGeneration.txt";
        public static final String CONDITIONS_PATH = "conditions.txt";
        public static final String MODELS_CV_PATH = "bestCrossValidation.txt";
        public static final String PARETO_PATH = "pareto.txt";
        public static final String LEAST_COMPLEX_PATH = "leastComplex.txt";
        public static final String MOST_ACCURATE_PATH = "mostAccurate.txt";
        public static final String KNEE_PATH = "knee.txt";
        public static final String FUSED_PATH = "fusedModel.txt";
        public static final String RESULT_PATH = "result.txt";

        public static final String ENABLE_POP_LOG = "false";
        public static final String CACHE_PREDICTIONS = "false";

        public static final String FRONT_RANK_METHOD = Names.FIRST_FITNESS;
        public static final String SAVE_JSON = "false";
    }
} public abstract class AbstractMessage implements Message {
	private static final long serialVersionUID = -704333880928760788L;
	// a send timeout value, in seconds
	public static final int SEND_TIMEOUT = 45;
	
	// the id of the source node
	public final String srcID;
	// the message's id
	public final MsgID msgID;
	// the message type, set through MessageType
	public final int msgType;

	// the srcIP and srcPort, used internally for received messages
	// note: srcIP and srcPort are transmitted over the network
	public String srcIP;
	public int srcPort;
	// the subnet ID of the src node
	public String srcSubnetID;
	// time this packet was created
	public Timestamp time;

	/**
	 * The default constructor for a message
	 * 
	 * @param _msgID
	 * @param _srcID
         * @param _srcIP
	 * @param _srcPort
	 * @param _srcSubnetID
	 * @param _msgType
	 */
	public AbstractMessage(MsgID _msgID, String _srcID, String _srcIP, int _srcPort, String _srcSubnetID, int _msgType) {
		msgID = _msgID;
		srcID = _srcID;
		srcIP = _srcIP;
		srcPort = _srcPort;
		srcSubnetID = _srcSubnetID;
		msgType = _msgType;
		time = NodeList.currTime();
	}

        /**
         * 
         * @return 
         */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((msgID == null) ? 0 : msgID.hashCode());
		result = prime * result + msgType;
		return result;
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof AbstractMessage))
			return false;
		AbstractMessage other = (AbstractMessage) obj;
		if (msgID == null) {
			if (other.msgID != null)
				return false;
		} else if (!msgID.equals(other.msgID))
			return false;
		if (msgType != other.msgType)
			return false;
		if (!srcID.equals(other.srcID))
			return false;
		if (!srcSubnetID.equals(other.srcSubnetID))
			return false;
		return true;
	}

    /**
     *
     * @return
     */
    @Override
	public String toString() {
		return "AbstractMessage [msgID=" + msgID + ", msgType=" + msgType
				+ ", srcID=" + srcID + ", srcIP=" + srcIP + ", srcPort="
				+ srcPort + ", srcSubnetID=" + srcSubnetID + ", time=" + time
				+ "]";
	}

	/**
	 * Public method to be called in order to send this message
	 * @param nodelist the master nodelist, for logging success/failure of sending
     * @param destID
	 * @param nle_dest
	 */
	public void scheduleSend(NodeList nodelist, String destID, NodeListEntry nle_dest) {
		MessageSender sw = new MessageSender(nodelist, destID, nle_dest, this, SEND_TIMEOUT);
		sw.start();
		System.out.println("Sent message");
	}

	/**
	 * Send this message through a socket
	 * 
     * @param nle_dest
	 * @throws IOException
     * @throws java.net.SocketTimeoutException
     * @throws java.net.SocketException
     * @throws java.net.ConnectException
     * @throws java.io.StreamCorruptedException
     * @throws java.lang.InterruptedException
	 * @throws UnknownHostException
	 */
	public void send(NodeListEntry nle_dest) throws UnknownHostException,
			SocketTimeoutException, SocketException, ConnectException,
			StreamCorruptedException, InterruptedException, IOException {
		String ip = nle_dest.ip;
		int port = nle_dest.port;
		String destSubnetID = nle_dest.subnetLabel;
		Mailbox.println(String.format("Sending message %s to %s:%d on subnet %s",msgID,ip,port,destSubnetID));
		Socket socket = new Socket();
		InetSocketAddress isa = new InetSocketAddress(ip, port);
		Mailbox.println("Connecting to socket at " + isa.toString());
		socket.connect(isa, 300);
		Mailbox.println("Default socket timeout:" + socket.getSoTimeout());
		// socket.setSoTimeout(100);
		// Mailbox.println("New socket timeout:" + socket.getSoTimeout());

		Mailbox.println("Getting object output stream");
		OutputStream soo = socket.getOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(
				new BufferedOutputStream(soo));
		// make sure we can connect to the address before proceeding
		// send this through the socket
		Mailbox.println("Sending object through socket");

		oos.writeObject(this);
		oos.flush();
		Mailbox.println("Closing socket");
		socket.close();
		Mailbox.println("Message has been sent successfully");
	}

        /**
         * Send message
         * @param m
         * @param destIP
         * @param destPort
         * @throws UnknownHostException
         * @throws IOException 
         */
	public static void sendObject(AbstractMessage m, String destIP, int destPort)
			throws UnknownHostException, IOException {
		Socket socket = new Socket(destIP, destPort);
		ObjectOutputStream oos = new ObjectOutputStream(
				socket.getOutputStream());
		oos.writeObject(m);
		socket.close();
	}	
} public abstract class EvolveMessage extends AbstractMessage {

	/**
	 * 
	 */
	private static final long serialVersionUID = 4481401951762535848L;

	public EvolveMessage(MsgID _msgID, String _srcID, String _srcIP, int _srcPort, String _srcSubnetID, int _msgType) {
		super(_msgID, _srcID, _srcIP, _srcPort, _srcSubnetID, _msgType);
	}

	/**
	 * Given an instance of this message, and access to the Evolve thread's
	 * resources and methods, take the appropriate action.
         * @param e: evolve (MRGP) thread
	 */
	public abstract void act(Evolve e);

} public class MessageWrapper<T extends AbstractMessage> {

	public final T msg;
	public final String destID;
	public final NodeListEntry nle;
	
	public MessageWrapper(T m, String _destID, NodeListEntry _nle) {
		msg = m;
		destID = _destID;
		nle = _nle;
	}

	@Override
	public String toString() {
		return destID + ":" + nle.ip + ":" + nle.port + "-" + msg.msgType;
	}

} public class AlgorithmException extends Exception {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1221203738037833999L;

	public AlgorithmException(String string) {
		super(string);
	}
} public class ArgParser {
	// the lexed arguments
	public final HashMap<String, String> groups;

	// the command-line flags
	// verbosity
	public final static String VERBOSE = "-v";
	public final int verbosity;
	// local ip address - easiest to just have system tell us
	public final static String MY_IP = "-i";
	public final String my_ip_string;
	public final IP my_ip;
	// ip list
	public final static String IPS = "-n";
	public final ArrayList<IP> ips;
	// target node count
	public final static String TARGET_NODE_COUNT = "-t";
	public final int targetNodeCount;
	// is migration enabled?
	public final static String MIGRATION_ENABLED = "--migration";
	public final boolean migrationEnabled;
	// generations per emigration
	public final static String MIGRATION_RATE = "--migration-rate";
	public final int migrationRate;
	// generation # in which to begin migration
	public final static String MIGRATION_START = "--migration-start";
	public final int migrationStart;
	// # of migrants per migration
	public final static String MIGRATION_SIZE = "--migration-size";
	public final int migrationSize;

	// Library Args - any string after this are passed through to the underlying
	// library
	public final static String LIB_ARGS = "--args";
	public final String lib_args;
	// Data file - file encoding all the information needed to access the
	// training data
	// The proper database interface is selected via the file extension
	public final static String DATA_FILE = "-d";
	public final String data_file;

	// max messages allowed per queue
	public final static String MSG_COUNT = "-m";
	public final int maxMsgs;
	private final static int maxMsgsDefault = 100;

	// subnet ID
	public final static String SUBNET_ID = "-s";
	public final String subnetID;
	private final static String subnetIDDefault = NodeList.DEFAULT_SUBNET_ID;

	public ArgParser(HashMap<String, String> _groups)
			throws UnhandledArgException {
		groups = _groups;

		// REQUIRED args
		// require this ip address
		if (!(groups.containsKey(MY_IP)))
			throw new UnhandledArgException("no argument for my IP");
		my_ip_string = groups.get(MY_IP);
		// parse port number too
		my_ip = IP.fromString(my_ip_string);
		// target node count
		if (groups.containsKey(TARGET_NODE_COUNT))
			targetNodeCount = Integer.valueOf(groups.get(TARGET_NODE_COUNT));
		else
			throw new UnhandledArgException("no argument for target node count");
		if (groups.containsKey(LIB_ARGS))
			lib_args = groups.get(LIB_ARGS);
		else {
			System.out.println("no arguments provided for underlying library");
			lib_args = null;
		}
		if (groups.containsKey(DATA_FILE))
			data_file = groups.get(DATA_FILE);
		else {
			System.out.println("no database file provided");
			data_file = null;
		}

		// parse list of known IPs
		ips = (groups.containsKey(IPS)) ? parseIPs(groups.get(IPS)) : new ArrayList<IP>();

		// OPTIONAL args
		// verbosity -- ignored for now
		verbosity = (groups.containsKey(VERBOSE)) ? Integer.valueOf(groups.get(VERBOSE)) : 0;
		maxMsgs = (groups.containsKey(MSG_COUNT)) ? Integer.valueOf(groups.get(MSG_COUNT)) : maxMsgsDefault;
		migrationEnabled = (groups.containsKey(MIGRATION_ENABLED)) ? Boolean.valueOf(groups.get(MIGRATION_ENABLED)) : false;
		migrationRate = (groups.containsKey(MIGRATION_RATE)) ? Integer.valueOf(groups.get(MIGRATION_RATE)) : 1;
		migrationStart = (groups.containsKey(MIGRATION_START)) ? Integer.valueOf(groups.get(MIGRATION_START)) : 0;
		migrationSize = (groups.containsKey(MIGRATION_SIZE)) ? Integer.valueOf(groups.get(MIGRATION_SIZE)) : 25;
		subnetID = (groups.containsKey(SUBNET_ID)) ? groups.get(SUBNET_ID) : subnetIDDefault;
	}

	/**
	 * Parse IPs of the form "id1:ip1:port1 id2:ip2:port2 id3:ip3:port3" etc
	 * 
	 * @param ips
	 * @return
	 * @throws UnhandledArgException
	 */
	public ArrayList<IP> parseIPs(String rawIPs) throws UnhandledArgException {
		// TODO think of the best way for this to interface with
		// Control/NodeList
		// remember, there are no node IDs yet
		// one idea: have a special stack of unidentified ppl
		ArrayList<IP> ips = new ArrayList<IP>();
		if (rawIPs.isEmpty()) {
			System.out.println("ArgParser: no ips specified");
		}
		String[] splitted = rawIPs.split(" ");
		// process each potential ip
		for (String ip : splitted) {
			ips.add(IP.fromString(ip));
			// ips.add(new IP(IP.ipToInt(ip), ip, Control.PORT));
		}
		return ips;
	}

	public class UnhandledArgException extends Exception {
		private static final long serialVersionUID = 1256884202673890421L;

		String msg;

		public UnhandledArgException(String _msg) {
			msg = _msg;
		}

		@Override
		public String toString() {
			return "UnhandledArgException: " + msg;
		}
	}
} public class MsgIDFactory {

	private long id;
	private final String nodeID;

	public MsgIDFactory(String _nodeID) {
		id = 0;
		nodeID = _nodeID;
	}

	/**
	 * Get a new message id. Synchronized because Control and Evolve threads
	 * access it
	 * @return
	 */
	public synchronized MsgID get() {
		return new MsgID(nodeID, ++id);
	}

} public class TextFormatter extends Formatter {

	Date dat = new Date();
	private final static String format = "{0,date} {0,time}";
	private MessageFormat formatter;

	private Object args[] = new Object[1];

	@Override
	public String format(LogRecord record) {
		StringBuffer sb = new StringBuffer();
		// Minimize memory allocations here.
		dat.setTime(record.getMillis());
		args[0] = dat;
		StringBuffer text = new StringBuffer();
		if (formatter == null) {
			formatter = new MessageFormat(format);
		}
		formatter.format(args, text, null);
		sb.append(record.getMillis() + " (" + text + ")" + " ");
		String message = formatMessage(record);
		sb.append(record.getLevel().getLocalizedName());
		sb.append(": ");
		sb.append(message);
		sb.append("\n");
		if (record.getThrown() != null) {
			try {
				StringWriter sw = new StringWriter();
				PrintWriter pw = new PrintWriter(sw);
				record.getThrown().printStackTrace(pw);
				pw.close();
				sb.append(sw.toString());
			} catch (Exception ex) {
			}
		}
		return sb.toString();
	}

}
import net.minidev.json.JSONObject;
import network.AbstractMessage;
import network.EvolveMessage;
import network.MessageWrapper;
import node.Algorithm.AlgorithmException;
import utility.ArgParser;
import utility.MsgIDFactory;
import utility.TextFormatter;
import evogpj.evaluation.java.SRLARSJava;
import evogpj.gp.Population;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import evogpj.algorithm.Parameters;
/**
 * Copyright (c) 2012 Evolutionary Design and Optimization Group
 * 
 * Licensed under the MIT License.
 * 
 * See the "LICENSE" file for a copy of the license.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.  
 *
 */
package node;





/**
 * The Evolve thread maintains evolutionary computation while providing access
 * from Control
 * 
 * @author Owen Derby
 */
public class Evolve extends AbstractThread {

	private Algorithm alg;
	private static Logger log = Logger.getLogger("node.Evolve");
	// should we report things via the reporter?
	private final Boolean REPORT;
	private final BlockingQueue<JSONObject> reportSend;

	// the queue for received Evolve messages
	public BlockingQueue<EvolveMessage> evolveMsgsRecv;
	// the queue for Evolve messages to send;
	public BlockingQueue<MessageWrapper<AbstractMessage>> evolveMsgsSend;