//  Neighborhood.java
//
//  Author:
//       Antonio J. Nebro <antonio@lcc.uma.es>
//       Juan J. Durillo <durillo@lcc.uma.es>
//
//  Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Lesser General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Lesser General Public License for more details.
// 
//  You should have received a copy of the GNU Lesser General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

package jmetal.util;

import jmetal.core.SolutionSet;

/**
 * Class representing neighborhoods for a <code>Solution</code> into a
 * <code>SolutionSet</code>.
 */ 
public class Neighborhood {    
  
  /**
   * Maximum rate considered
   */
  private static int MAXRADIO = 2;
  
  /**
   * Stores the neighborhood.
   * structure_ [i] represents a neighborhood for a solution.
   * structure_ [i][j] represents a neighborhood with a ratio.
   * structure_ [i][j][k] represents a neighbor of a solution.
   */
  private int [][][] structure_;
  
  /**
   * Stores the size of the solutionSet.
   */
  private int solutionSetSize_;
  
  /**
   * Stores the size for each row
   */
  private int rowSize_;
  
  /**
   * Enum type for defining the North, South, East, West, North-West, South-West,
   * North-East, South-East neighbor.
   */
  enum Row {N, S, E, W, NW, SW, NE, SE}

    /**
   * Constructor.
   * Defines a neighborhood of a given size.
   * @param solutionSetSize The size.
   */
  public Neighborhood(int solutionSetSize) {            
    solutionSetSize_ = solutionSetSize;
    //Create the structure_ for store the neighborhood
    structure_ = new int[solutionSetSize_][MAXRADIO][];
        
    //For each individual, and different rates the individual has a different 
    //number of neighborhoods
    for (int ind = 0; ind < solutionSetSize_; ind ++) {
      for (int radio = 0; radio < MAXRADIO; radio ++) {
        if (radio == 0) {//neighboors whit rate 1
          structure_[ind][radio] = new int[8];
        } else if (radio == 1) { //neighboors whit rate 2
          structure_[ind][radio] = new int[24];       
        } // if
      } // for
    } // for
        
    //Calculate the size of a row
    rowSize_ = (int) Math.sqrt((double)solutionSetSize_);
        
        
    //Calculates the neighbors of a individual 
    for (int ind = 0; ind < solutionSetSize_; ind++){
      //rate 1
      //North neighbors
      if (ind > rowSize_ - 1){
        structure_[ind][0][Row.N.ordinal()] = ind - rowSize_;
      } else {
        structure_[ind][0][Row.N.ordinal()] = 
       (ind - rowSize_ + solutionSetSize) % solutionSetSize;                                                          
      }
            
      //East neighbors
      if  ((ind + 1) % rowSize_ == 0)
        structure_[ind][0][Row.E.ordinal()] = (ind - (rowSize_ - 1));
      else
        structure_[ind][0][Row.E.ordinal()] = (ind + 1);

      //Western neigbors
      if (ind % rowSize_ == 0) {
        structure_[ind][0][Row.W.ordinal()] = (ind + (rowSize_ - 1));
      } else {
        structure_[ind][0][Row.W.ordinal()] = (ind - 1);
      }

      //South neigbors
      structure_[ind][0][Row.S.ordinal()] = (ind + rowSize_) % solutionSetSize;                        
    }                
        
    for (int ind = 0; ind < solutionSetSize_; ind++){
      structure_[ind][0][Row.NE.ordinal()] = 
        structure_[structure_[ind][0][Row.N.ordinal()]][0][Row.E.ordinal()];
      structure_[ind][0][Row.NW.ordinal()] = 
        structure_[structure_[ind][0][Row.N.ordinal()]][0][Row.W.ordinal()];
      structure_[ind][0][Row.SE.ordinal()] = 
        structure_[structure_[ind][0][Row.S.ordinal()]][0][Row.E.ordinal()];
      structure_[ind][0][Row.SW.ordinal()] = 
        structure_[structure_[ind][0][Row.S.ordinal()]][0][Row.W.ordinal()];
    }
  } // Neighborhood
    
  /**
   * Returns a <code>SolutionSet</code> with the North, Sout, East and West
   * neighbors solutions of ratio 0 of a given location into a given 
   * <code>SolutionSet</code>.
   * @param solutionSet The <code>SolutionSet</code>.
   * @param location The location.
   * @return a <code>SolutionSet</code> with the neighbors.
   */
  public SolutionSet getFourNeighbors(SolutionSet solutionSet, int location){
    //SolutionSet that contains the neighbors (to return)
    SolutionSet neighbors;
        
    //instance the solutionSet to a non dominated li of individuals
    neighbors = new SolutionSet(24);
        
    //Gets the neighboords N, S, E, W
    int index;        
        
    //North
    index = structure_[location][0][Row.N.ordinal()];        
    neighbors.add(solutionSet.get(index));
      
    //South
    index = structure_[location][0][Row.S.ordinal()];
    neighbors.add(solutionSet.get(index));

    //East
    index = structure_[location][0][Row.E.ordinal()];
    neighbors.add(solutionSet.get(index));

    //West
    index = structure_[location][0][Row.W.ordinal()];
    neighbors.add(solutionSet.get(index));         
    
    //Return the list of non-dominated individuals
    return neighbors;        
  } //getFourNeighbors           
    
  /**
   * Returns a <code>SolutionSet</code> with the North, Sout, East, West, 
   * North-West, South-West, North-East and South-East neighbors solutions of
   * ratio 0 of a given location into a given <code>SolutionSet</code>.
   * solutions of a given location into a given <code>SolutionSet</code>.
   * @param population The <code>SolutionSet</code>.
   * @param individual The individual.
   * @return a <code>SolutionSet</code> with the neighbors.
   */
  public SolutionSet getEightNeighbors(SolutionSet population, int individual){
    //SolutionSet that contains the neighbors (to return)
    SolutionSet neighbors;

    //instance the population to a non dominated li of individuals
    neighbors = new SolutionSet(24);

    //Gets the neighboords N, S, E, W
    int index;        
        
    //N
    index = this.structure_[individual][0][Row.N.ordinal()];        
    neighbors.add(population.get(index));

    //S
    index = this.structure_[individual][0][Row.S.ordinal()];
    neighbors.add(population.get(index));

    //E
    index = this.structure_[individual][0][Row.E.ordinal()];
    neighbors.add(population.get(index));

    //W
    index = this.structure_[individual][0][Row.W.ordinal()];
    neighbors.add(population.get(index));

    //NE
    index = this.structure_[individual][0][Row.NE.ordinal()];
    neighbors.add(population.get(index));

    //NW
    index = this.structure_[individual][0][Row.NW.ordinal()];
    neighbors.add(population.get(index));

    //SE
    index = this.structure_[individual][0][Row.SE.ordinal()];
    neighbors.add(population.get(index));

    //SW
    index = this.structure_[individual][0][Row.SW.ordinal()];
    neighbors.add(population.get(index));


    //Return the list of non-dominated individuals
    return neighbors;        
  }  // getEightNeighbors
} // Neighborhood
