def __init__(self):
     self.mutationRate = 50
     self.crossoverRate = 30
     self.elitismRate = 10
     self.newRate = 10
     self.maxEpoch = 100
     self.populationSize = 100
     self.generation = [] #list
     self.random = Go_Random(randomFunction)
     self.chromosomeLength = 50
     self.minCodon = 0
     self.maxCodon = 1
     self.calculatedFitness = {}
class Go_GeneticsAlgorithm(object):
    
    def __init__(self):
        self.mutationRate = 50
        self.crossoverRate = 30
        self.elitismRate = 10
        self.newRate = 10
        self.maxEpoch = 100
        self.populationSize = 100
        self.generation = [] #list
        self.random = Go_Random(randomFunction)
        self.chromosomeLength = 50
        self.minCodon = 0
        self.maxCodon = 1
        self.calculatedFitness = {}
    
    def __str__(self):
        string = ''
        for i in xrange(len(self.generation)):
            string+= '\nGeneration %d\n' % i
            for j in xrange(self.populationSize):
                fitness = str(j)
                string+= 'fitness #%s : %5.3f, ' % (fitness.rjust(3), self.generation[i][j]['fitness'])
            string += '\n'
        return string    
    
    
    def __currentGenerationIndex(self):
        return len(self.generation)-1
    
    def _mutation(self, chromosome): 
        #return new chromosome   
        i=0
        rnd_count = int(round(self.random.get(0,self.chromosomeLength-1)/2)) 
        while i<rnd_count:
            rnd_addition = int(round(self.random.get(self.minCodon,self.maxCodon)))
            rnd_index = int(round(self.random.get(0,self.chromosomeLength-1)))
            newCodon = self.minCodon+((chromosome[rnd_index]-self.minCodon+rnd_addition) % (self.maxCodon+1-self.minCodon))       
            chromosome[rnd_index] = newCodon
            i+=1
        return chromosome
    
    def _crossover(self, chromosome1, chromosome2): 
        #return new chromosome
        random = int(round(self.random.get(0,self.chromosomeLength-1)))
        segment1 = chromosome1[0:random]
        segment2 = chromosome2[random:self.chromosomeLength]
        chromosome = segment1+segment2
        return chromosome
    
    def _new(self): 
        #return new chromosome
        chromosome = []
        i=0
        while i< self.chromosomeLength:
            random = int(round(self.random.get(self.minCodon,self.maxCodon)))
            chromosome.append(random);
            i+=1
        return chromosome
    
    def _pick(self, index=None): 
        #return any chromosome from current generation
        currentGenerationIndex = self.__currentGenerationIndex()
        currentGeneration = self.generation[currentGenerationIndex]
        if index!=None:
            return currentGeneration[index]['chromosome'][:]
        else:
            maxCumSum = float(currentGeneration[self.populationSize-1]['cumsum'])           
            random = float(self.random.get(0,maxCumSum))
            chromosome = None
            i=0
            while random > currentGeneration[i]['cumsum'] and i<len(currentGeneration)-1:
                i += 1
            chromosome = currentGeneration[i]['chromosome'][:]
            return chromosome
    
    def _fitnessFunction(self, chromosome):
        #you surely need to override this        
        return sum(chromosome)
        
    def _makeNextGeneration(self):        
        newGeneration = []
        #the ratio below are related to the decision created                 
        elitismLimit = float(self.elitismRate)
        mutationLimit = elitismLimit+float(self.mutationRate)
        crossoverLimit = mutationLimit+float(self.crossoverRate)
        allLimit = crossoverLimit+float(self.newRate)
        elitismRatio = elitismLimit/allLimit
        mutationRatio = mutationLimit/allLimit
        crossoverRatio = crossoverLimit/allLimit
        
        for i in xrange(self.populationSize):
            newChromosome = []
            
            if(self.__currentGenerationIndex()==-1):
                newChromosome = self._new()
            else:
                ratio=float(i)/float(self.populationSize)
                newChromosome = []
                if ratio <= elitismRatio:
                    newChromosome = self._pick(i)
                elif ratio <= mutationRatio:
                    newChromosome = self._mutation(self._pick())
                elif ratio <= crossoverRatio:
                    newChromosome = self._crossover(self._pick(), self._pick())
                else:
                    newChromosome = self._new()
                    
            
            #calculate fitness if needed or take from self.calculatedFitness
            #if calculation has been performed before
            fitness=0   
            chromosomeKey = ' '.join(map(str,newChromosome))
            if not(chromosomeKey in self.calculatedFitness): 
                fitness = self._fitnessFunction(newChromosome) #calculate...
                self.calculatedFitness[chromosomeKey] = fitness #and save
            else:
                fitness = self.calculatedFitness[chromosomeKey] #take from calculated
            
            #new Individu
            newIndividu = {'chromosome' : newChromosome, 
                           'fitness' : fitness,
                           'cumsum' : 0}
            newGeneration.append(newIndividu)
                
        
        
        #sort the new generation based on their fitness      
        newGeneration_dec = map(lambda x: (x["fitness"],x), newGeneration)
        newGeneration_dec.sort(reverse=True) 
        newGeneration = map(lambda x: x[1],newGeneration_dec)
         
        #calculate cumsum
        cumsum=0
        for i in xrange(self.populationSize):
            cumsum += newGeneration[i]['fitness']
            newGeneration[i]['cumsum'] = cumsum
        self.generation.append(newGeneration)
        self._printProgress()
    
    def _printProgress(self):
        currentGenerationIndex = self.__currentGenerationIndex()
        currentGeneration = self.generation[currentGenerationIndex]
        bestFitness = currentGeneration[0]['fitness']
        
        print('Generation %d, best fitness : %5.5f'%(currentGenerationIndex, bestFitness))
        
    def train(self):
        i=0
        while i<self.maxEpoch: 
            self._makeNextGeneration()
            i+=1