module Operator

  # Classic two-point crossover. It assumes two parents has the form of Arrays. 
  # First, the cutting points in both parents are randomly selected, 
  # then the middle parts are swapped, forming two new offsprings.
  #
  # Note the valid cutting point can be placed before the first element or after the last element of the array.
  #
  class CrossoverTwoPoints

    # Create a new two-point crossover operator.
    def initialize
      @random = Kernel
    end

    # The source of randomness, used for calling "random.rand( limit )", defaulting to 'Kernel' class.
    attr_accessor :random

    # Take parent1, parent2 arguments and produce [offspring1, offspring2].
    # For instance:
    # 
    #   parent1 = [ 1,  2,  3,  4,  5,  6,  7,  8,  9,  10 ]
    #   parent2 = [ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 ]
    #   xover = CrossoverTwoPoints.new
    # 
    # now producing the offsprings:
    #   offspring1, offspring2 = xover.crossover( parent1, parent2 )
    # offspring1 is [ 1, 2, 3, 4, 5,   16, 17, 18,   8, 9, 10 ] 
    # offspring2 is [ 11, 12, 13, 14,   6, 7,   15, 19, 20, 21, 22, 23 ]
    #
    # This is a stochastic process, thus repeating: 
    #   offspring1, offspring2 = xover.crossover( parent1, parent2 )
    # produces:  
    # offspring1 is [ 1, 2,   20 ] 
    # offspring2 is [ 11, 12, 13, 14, 15, 16, 17, 18, 19,    3,  4,  5,  6,  7,  8,  9,  10,    21, 22, 23  ]
    #    
    def crossover( parent1, parent2, dummy1=nil, dummy2=nil )

      return parent1.clone, parent2.clone if parent1.empty? or parent2.empty?
        
      pt11 = @random.rand(parent1.size+1)
      pt12 = @random.rand(parent1.size+1)     
      pt11,pt12 = [pt12,pt11] if pt11 > pt12

      pt21 = @random.rand(parent2.size+1)
      pt22 = @random.rand(parent2.size+1)     
      pt21,pt22 = [pt22,pt21] if pt21 > pt22   

      offs1 = parent1[0...pt11].clone
      offs1 = offs1.concat parent2[pt21...pt22]
      offs1 = offs1.concat parent1[pt12...parent1.size]

      offs2 = parent2[0...pt21].clone
      offs2 = offs2.concat parent1[pt11...pt12]
      offs2 = offs2.concat parent2[pt22...parent2.size]

      return offs1, offs2
     
    end

  end

end