//  BinaryReal.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.encodings.variable;

import jmetal.core.Variable;
import jmetal.util.JMException;

import java.util.BitSet;

/** This class extends the Binary class to represent a Real encodings.variable encoded by
 * a binary string
 */
public class BinaryReal extends Binary {
	
	/**
   * Defines the default number of bits used for binary coded variables.
   */	
	public static final int DEFAULT_PRECISION = 30; 
	
  /**
   * Stores the real value of the encodings.variable
   */
  private double value_;
  
  /**
   * Stores the lower limit for the encodings.variable
   */
  private double lowerBound_;
  
  /**
   * Stores the upper limit for the encodings.variable
   */
  private double upperBound_;  
     
  /**
   * Constructor.
   */
  public BinaryReal() {    
    super();
  } //BinaryReal

  /**
   * Constructor
   * @param numberOfBits Length of the binary string.
   * @param lowerBound The lower limit for the encodings.variable
   * @param upperBound The upper limit for the encodings.variable.
   */
  public BinaryReal(int numberOfBits, double lowerBound, double upperBound){
    super(numberOfBits);
    lowerBound_     = lowerBound;
    upperBound_     = upperBound;
    
    decode();                              
  } //BinaryReal
    
	/**
	 * @param bits  BitSet
	 * @param nbBits  Number of bits
	 * @param lowerBound  Lower bound
	 * @param upperBound  Upper bound
	 */
	public BinaryReal(BitSet bits, int nbBits, double lowerBound, double upperBound) {
		super(nbBits);
		bits_ = bits;
		lowerBound_ = lowerBound;
		upperBound_ = upperBound;
		decode();
	} // BinaryReal

  /**
   * Copy constructor
   * @param variable The encodings.variable to copy
   */
  public BinaryReal(BinaryReal variable){
    super(variable);

    lowerBound_   = variable.lowerBound_;
    upperBound_   = variable.upperBound_;
    /*
    numberOfBits_ = encodings.variable.numberOfBits_;
     
    bits_ = new BitSet(numberOfBits_);
    for (int i = 0; i < numberOfBits_; i++)
      bits_.set(i,encodings.variable.bits_.get(i));
    */  
    value_ = variable.value_;
  } //BinaryReal
    
  
  /**
   * Decodes the real value encoded in the binary string represented
   * by the <code>BinaryReal</code> object. The decoded value is stores in the 
   * <code>value_</code> field and can be accessed by the method
   * <code>getValue</code>.
   */
  public void decode(){
    double value = 0.0;        
    for (int i = 0; i < numberOfBits_; i++) {
      if (bits_.get(i)) {
        value += Math.pow(2.0,i);
      }
    }
        
    value_ = value * (upperBound_ - lowerBound_) /
                                              (Math.pow(2.0,numberOfBits_)-1.0);        
    value_ += lowerBound_;    
  } //decode
 
  /**
   * Returns the double value of the encodings.variable.
   * @return the double value.
   */
  public double getValue() {
    return value_;
  } //getValue

	/**
	 * This implementation is efficient for binary string of length up to 24
	 * bits, and for positive intervals.
	 * 
	 * @see jmetal.core.Variable#setValue(double)
	 * 
	 * Contributor: jl hippolyte
	 */
	@Override
	public void setValue(double value) throws JMException {
		if (numberOfBits_ <= 24 && lowerBound_ >= 0) {
			BitSet bitSet;
			if (value <= lowerBound_) {
				 bitSet = new BitSet(numberOfBits_);
				bitSet.clear();
			} else if (value >= upperBound_) {
				 bitSet = new BitSet(numberOfBits_);
				bitSet.set(0, numberOfBits_);
			} else {
				 bitSet = new BitSet(numberOfBits_);
				bitSet.clear();
				// value is the integerToCode-th possible value, what is integerToCode?
				int integerToCode = 0;
				double tmp = lowerBound_;
				double path = (upperBound_ - lowerBound_) / (Math.pow(2.0, numberOfBits_) - 1);
				while (tmp < value) {
					tmp +=  path;
					integerToCode++;
				}
				int remain = integerToCode;
				for (int i = numberOfBits_ - 1; i >= 0; i--) {
					//System.out.println("i= " + i);
					int ithPowerOf2 = (int) Math.pow(2, i);

					if (ithPowerOf2 <= remain) {
						//System.out
					//			.println(ithPowerOf2thValue + " <= " + remain);
						bitSet.set(i);
						remain -= ithPowerOf2;
					} else {
						bitSet.clear(i);
					}
				}		
			}
			this.bits_ = bitSet;
			this.decode();
			
		} else {
			if (lowerBound_ < 0)
				throw new JMException("Unsupported lowerbound: " + lowerBound_
						+ " > 0");
			if (numberOfBits_>= 24)
				throw new JMException("Unsupported bit string length"
						+ numberOfBits_ + " is > 24 bits");
		}
	}// setValue
  
  /**
   * Creates an exact copy of a <code>BinaryReal</code> object.
   * @return The copy of the object
   */
  public Variable deepCopy() {
    return new BinaryReal(this);
  } //deepCopy

  /**
   * Returns the lower bound of the encodings.variable.
   * @return the lower bound.
   */
  public double getLowerBound() {
    return lowerBound_;
  } // getLowerBound

  /**
   * Returns the upper bound of the encodings.variable.
   * @return the upper bound.
   */
  public double getUpperBound() {
    return upperBound_;
  } // getUpperBound

  /**
   * Sets the lower bound of the encodings.variable.
   * @param lowerBound the lower bound.
   */
  public void setLowerBound(double lowerBound) {
    lowerBound_ = lowerBound;
  } // setLowerBound

  /**
   * Sets the upper bound of the encodings.variable.
   * @param upperBound the upper bound.
   */
  public void setUpperBound(double upperBound) {
    upperBound_ = upperBound;
  } // setUpperBound
  
  /**
   * Returns a string representing the object.
   * @return the string.
   */
  @Override
  public String toString() {
    return value_+"";
  } // toString
} // BinaryReal
