import byucc.jhdl.apps.Stimulator.IntegerValueProvider;
import byucc.jhdl.base.BV;
import byucc.jhdl.util.BVFormat.BVFormat;

/** Class to demonstrate the use of ValueProvider for custom function
 * generation with Stimulators.
 * @author Anthony L. Slade */
public class Cosine implements IntegerValueProvider {
  /** @param startTheta the first angle (in radians) that will be used
   * in the cosine calculation.
   * @param incrementDivisor the angle will be incremented on every
   * clock cycle by PI divided by incrementDivisor */
  public Cosine(double startTheta,
		int incrementDivisor) {
    _startTheta = startTheta;
    _increment = Math.PI / incrementDivisor;
    _theta = startTheta - _increment;
  }
  /** Returns the value of
   * COSINE(startTheta+CYCLE*(PI/incrementDivisor))*/
  public int getNextIntegerValue() {
    _theta += _increment;
    return (int)(SHIFT*Math.cos(_theta));
  }
  /** Returns the value of COSINE(startTheta)*/
  public int getResetIntegerValue() {
    _theta = _startTheta;
    return (int)(SHIFT*Math.cos(_theta));
  }
  /** Returns the implemented function displayed as a String */
  public String getForceSchedule(int radix) {
    return getForceSchedule(null);
  }
  /** Returns the implemented function displayed as a String */
  public String getForceSchedule(String format) {
    return "COSINE("
      +_startTheta
      +"+CYCLE*(PI/"
      +(Math.PI/_increment)
      +"))";
  }
  private static final String FORMAT_32_BIT = "%d[31:30.29:0]";
  /** Takes an int BV to format it as a fixed-point value representing
   * the value from [-1,1] for the cosine
   * @return the formatted string */
  public static String formatIntBV( BV value ) {
    try {
      return BVFormat.formatBV(FORMAT_32_BIT,
			       value);
    } catch ( Exception e ) {
      return "BVFormat Error:"+e.getMessage();
    }
  }
  /** For a given BV (presumbly from 32-bit wire whose value is
   * somehow derived from the output of this class) returns the double
   * value.  This is done by converting the value into a double and
   * then dividing it by the same shift value that was used to create
   * the integer */
  public static double getDoubleValue( BV value ) {
    int intVal = value.intValue();
    double dblVal = (double)intVal;
    return dblVal/SHIFT;
  }
  /** 2^30.  Used to normalize to a 32-bit fixed point value. */
  private static final int SHIFT = 0x40000000; // = 1073741824
  /** The current angle in radians */
  private double _theta;
  /** The angle (in radians) to start at */
  private double _startTheta;
  /** The amount to increment _theta on each clock cycle */
  private double _increment;


  public static void main(String[] args) {
    System.out.println();
    System.out.println("************************************");
    System.out.println("Testing Cosine in PI/16 increments...");
    Cosine cosine = new Cosine(0.0,16);
    BV cosineBV = new BV(32);
    double theta = 0.0,
      increment = Math.PI/16;
    for ( int itr = 0; itr < 34; ++itr ) {
      int cosineInt = cosine.getNextIntegerValue();
      cosineBV.setValue(cosineInt);
      System.out.print("COSINE("+itr+"*PI/16)="
			 +formatIntBV(cosineBV));
      System.out.println("\t\tCOSINE("+itr+"*PI/16)="
			 +Math.cos(theta));
      theta += increment;
    }
    System.out.println("************************************");
    System.out.println();
  }
}
