// file: PoleZero.java
//
// include the necessary system files
//
// include the system files
//
//import java.awt.*;
import java.io.*;
import java.lang.*;
import javax.swing.*;


//include the necessary local files
//
//import Constants;
//import ZPlane;

// class: PoleZero
//
// this class implements a object representing a pole or a zero
// 
public class PoleZero implements Constants{
  
  // set class constants
  // 
  public static final int DEFAULT_PZ_INDICATOR = Constants.POLE_INDICATOR;

  // indicator of whether this is a pole or a zero
  //
  public int pz_indicator = DEFAULT_PZ_INDICATOR;

  // indicator of whether this is an analog or digital pole
  //
  public int type_indicator = Constants.DEFAULT_DESIGN;

  // declare variables for the frequency and bandwidth of a
  // pole or zero
  //
  public double frequency = 0.0;
  public double bandwidth = 0.0;

  // declare variables for the z-plane location of a pole or zero
  //
  public double z_real = 0.0;
  public double z_imag = 0.0;

  // location of this poles conjugate in an array and a flag to tell
  // if it has been conjugated already
  //
  public boolean conj_flag = false;

  //*****************************************************************
  //
  // constructor methods
  //
  //*****************************************************************
  

  // method: PoleZero
  //
  // parameters: 
  //     double freq_a: (input) frequency of the pole/zero
  //     double band_a: (input) bandwidth of the pole/zero
  // 
  // this method constructs a PoleZero object with the 
  // input values
  //
  public PoleZero(double freq_a, double band_a) {
    
    // initialize the real part or frequency and imaginary part or
    // bandwidth respectively to the parameter values
    //
    this.frequency = freq_a;
    this.bandwidth = band_a;

  }


  // method: PoleZero
  //
  // parameters: 
  //     int pz_type: (input) indicates whether the object should be a pole
  //               or zero.  
  //     double freq_a: (input) frequency of the pole/zero
  //     double band_a: (input) bandwidth of the pole/zero
  // 
  // this method constructs a PoleZero object with the indicated type
  // and values
  //
  public PoleZero(int pz_type, double freq_a, double band_a) {
    
    // initialize the pz_indicator, and the real and
    // imaginary portions to the parameter values
    //
    this(freq_a, band_a);
    this.pz_indicator = pz_type;

  }


  // method: PoleZero
  //
  // parameters: 
  //     int design_type: (input) indicates the design_type of the object
  //     int pz_type: (input) indicates whether the object should be a pole
  //               or zero.  
  //     double freq_a: (input) frequency of the pole/zero
  //     double band_a: (input) bandwidth of the pole/zero
  // 
  // this method constructs a PoleZero object with the indicated type
  // and values
  //
  public PoleZero(int design_type, int pz_type, double freq_a, double band_a) {
    
    // initialize the type_indicator, pz_indicator, and the real (freq) and
    // imaginary (bandwidth) portions to the parameter values
    //
    this(pz_type, freq_a, band_a);
    this.type_indicator = design_type;

  }

  
  //*****************************************************************
  //
  // private methods
  //
  //*****************************************************************

  
  //*****************************************************************
  //
  // public methods
  //
  //*****************************************************************

  // method: mag_response
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  //     double sample_frequency: (input) sample_frequency
  // 
  // this method returns the magnitude response of this pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double mag_response(double frequency_in, double sample_frequency) {

    // declare local variables
    //
    double conj_response = 1.0;
    double response = 0.0;
    double real_arg = -Math.PI * (bandwidth / sample_frequency);
    double freq_arg = Constants.ISIP_TWOPI * frequency_in / sample_frequency;
    double imag_arg = Constants.ISIP_TWOPI * frequency / sample_frequency;

    // calculate the magnitude response
    //
    if ((bandwidth == 0.0) && (Math.abs(frequency_in - frequency) < 1.0E-7)) {
	return (this.mag_response(frequency_in + 2.0E-7, sample_frequency));
    }
    else {
	response = 1.0 - (2 * Math.exp(real_arg) * Math.cos(imag_arg - freq_arg))+ Math.exp(2 * real_arg);

	response = Math.sqrt(response);
    
      // see if this pole/zero is a conjugate
      //
	if (conj_flag) {
	    conj_response = this.mag_response_conj(frequency_in, sample_frequency);
	}
      
      // exit and return the proper response for either a pole or a zero
      //
	if (pz_indicator == Constants.POLE_INDICATOR) {
	    if (response == 0.0) {
		return Constants.REALLY_LARGE_NUMBER;
	    }
	    return (1/response * conj_response);
	}
	else {
	    return (response * conj_response);
	}
    }
  }

  // method: mag_response
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  // 
  // this method returns the magnitude response of this analog pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double mag_response(double frequency_in) {

    // declare local variables
    //
    double conj_response = 1.0;
    double response = 0.0;
    double imaginary_mag = frequency_in - frequency;
    double real_mag = -0.5 *  bandwidth ;    

    // three cases for a pole or zero at 0 + j0, a real pole or zero
    // and a complex pole or zero
    //
    if (frequency == 0.0) {
      if (bandwidth == 0.0) {

	// single pole or zero at s = 0
	//
	response = (imaginary_mag * imaginary_mag) + 
	  (real_mag * real_mag);      
	response = Math.sqrt(response);
	
      }
      else {

	// single real pole or zero
	//
	response = (imaginary_mag * imaginary_mag) + 
	  (real_mag * real_mag);
	double normalizer = Math.sqrt(((bandwidth * bandwidth) / 4) + 
				      frequency * frequency);
	response = Math.sqrt(response) / normalizer;
	
      }
    }
    else {

      // complex pole or zero
      //
      response = (imaginary_mag * imaginary_mag) + 
	(real_mag * real_mag);
      double normalizer = Math.sqrt(((bandwidth * bandwidth) / 4) + 
				    frequency * frequency);
      response = Math.sqrt(response) / normalizer;
    }

    // see if this pole/zero is a conjugate
    //
    if (conj_flag) {
      conj_response = this.mag_response_conj(frequency_in);
    }

    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      if (response == 0.0) {
	return Constants.REALLY_LARGE_NUMBER;
      }
      else {
	return (1/response * conj_response);
      }
    }
    else {
      return (response * conj_response);
    }

  }


  // method: phase_response
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  //     double sample_frequency: (input) sample frequency
  // 
  // this method returns the phase response of this pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double phase_response(double frequency_in, double sample_frequency) {

    // declare local variables
    //
    double conj_response = 0.0;
    double response = 0.0;
    double mag,angle,rp,ip,omega,numerator,denominator;
    
    mag = Math.exp(-bandwidth * Math.PI / sample_frequency);
    angle = (frequency * Math.PI * 2) /sample_frequency;
    rp = mag * Math.cos(angle);
    ip = mag * Math.sin(angle);
    omega = 2.0 * Math.PI * frequency_in /sample_frequency;

    if (pz_indicator == Constants.POLE_INDICATOR) {
	numerator = ip * Math.cos(omega) - rp * Math.sin(omega);
	denominator = 1 - rp * Math.cos(omega) - ip * Math.sin(omega);

	if((numerator > 0.0) && (denominator > 0.0)) {
	    response = Math.atan(numerator/denominator);
	}

	else if((numerator > 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) + Math.PI;
	}

	else if((numerator < 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) - Math.PI;
	}
	
	else {
	    response = Math.atan(numerator/denominator);
	}
    }

    else {
	numerator = rp * Math.sin(omega) - ip * Math.cos(omega);
	denominator = 1 - rp * Math.cos(omega) - ip * Math.sin(omega);

	if((numerator > 0.0) && (denominator > 0.0)) {
	    response = Math.atan(numerator/denominator);
	}

	else if((numerator > 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) + Math.PI;
	}

	else if((numerator < 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) - Math.PI;
	}
	
	else {
	    response = Math.atan(numerator/denominator);
	}
    }
    
    // see if this pole/zero is a conjugate
    //
    if (conj_flag) {
	conj_response = this.phase_response_conj(frequency_in, sample_frequency);
    }
    
    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
	if (bandwidth > 0.0) {
	    return (0.0 + response + conj_response);
	}
	else {
	    //return (0.0 - response + conj_response);
	    return (0.0 + response + conj_response);
	}
    }
    else {
	if (bandwidth > 0.0) {
	    return (response + conj_response);
	}
	else {
	    return (response + conj_response);
	}
    }
  }
    
    
  // method: phase_response
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  // 
  // this method returns the phase response of this pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double phase_response(double frequency_in) {

    // declare local variables
    //
    double conj_response = 0.0;
    double response = 0.0;

    // compute the response
    //
    double arg = 0.0;

    if (bandwidth == 0.0) {

      if ((frequency_in - frequency) < 0.0) {

	// arctan of -infinity
	//
	response = -Math.PI/2;
      }

      else if ((frequency_in - frequency) >= 0.0 ) {
	response = Math.PI/2;
      }

    }
    else {
	double numerator = 2 * (frequency_in - frequency);
	double denominator = bandwidth;
	
	if((numerator > 0.0) && (denominator > 0.0)) {
	    response = Math.atan(numerator/denominator);
	}

	else if((numerator > 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) + Math.PI;
	}

	else if((numerator < 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) - Math.PI;
	}
	
	else {
	    response = Math.atan(numerator/denominator);
	}
	   //response = Math.atan(2 * (frequency_in - frequency) / bandwidth);
    }

    // see if this pole/zero is a conjugate
    //
    if (conj_flag) {
      conj_response = this.phase_response_conj(frequency_in);
    }

    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      return (0.0 - response + conj_response);
    }
    else {
      return (response + conj_response);
    }

  }


  // method: three_d_mag_resp
  //
  // parameters: 
  //     double real_freq: sigma
  //     double imag_freq: omega
  // 
  // this method returns the magnitude response of this analog pole-zero
  // object at the indicated complex frequency
  //
  // returns: double
  //  
  public double three_d_mag_resp(double real_freq, double imag_freq) {

    // declare local variables
    //
    double conj_response = 1.0;
    double response = 0.0;
    double imaginary_mag = imag_freq - frequency;
    double real_mag = real_freq + (0.5 *  bandwidth);    

    // three cases for a pole or zero at 0 + j0, a real pole or zero
    // and a complex pole or zero
    //
    if (frequency == 0.0) {
      if (bandwidth == 0.0) {

	// single pole or zero at s = 0
	//
	response = (imaginary_mag * imaginary_mag) + 
	  (real_mag * real_mag);      
	response = Math.sqrt(response);
	
      }
      else {

	// single real pole or zero
	//
	response = (imaginary_mag * imaginary_mag) + 
	  (real_mag * real_mag);
	double normalizer = Math.sqrt(((bandwidth * bandwidth) / 4) + 
				      frequency * frequency);
	response = Math.sqrt(response) / normalizer;
	
      }
    }
    else {

      // complex pole or zero
      //
      response = (imaginary_mag * imaginary_mag) + 
	(real_mag * real_mag);
      double normalizer = Math.sqrt(((bandwidth * bandwidth) / 4) + 
				    frequency * frequency);
      response = Math.sqrt(response) / normalizer;
    }

    // see if this pole/zero is a conjugate
    //
    if (conj_flag) {
      conj_response = this.three_d_mag_resp_conj(real_freq, imag_freq);
    }

    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      if (response == 0.0) {
	return Constants.REALLY_LARGE_NUMBER;
      }
      else {
	return (1/response * conj_response);
      }
    }
    else {
      return (response * conj_response);
    }

  }


  // method: three_d_mag_resp
  //
  // parameters: 
  //     double real_freq: sigma
  //     double imag_freq: omega
  //     double sample_freq: sample frequency
  // 
  // this method returns the magnitude response of this analog pole-zero
  // object at the indicated complex frequency
  //
  // returns: double
  //  
  public double three_d_mag_resp(double real_freq, double imag_freq, 
				 double sample_frequency) {

    // declare local variables
    //
    double conj_response = 1.0;
    double response = 0.0;
    double real_arg = Math.PI * (Math.abs(real_freq - bandwidth) / 
				 sample_frequency);
    double freq_arg = Constants.ISIP_TWOPI * imag_freq / sample_frequency;
    double imag_arg = Constants.ISIP_TWOPI * frequency / sample_frequency;

    // calculate the magnitude response
    //
    response = 1.0 - (2 * Math.exp(real_arg) * Math.cos(imag_arg - freq_arg)) +
      Math.exp(2 * real_arg);

    response = Math.sqrt(response);
    
    // see if this pole/zero is a conjugate
    //
    if (conj_flag) {
      conj_response = this.three_d_mag_resp_conj(real_freq, imag_freq, 
					     sample_frequency);
    }

    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      if (response == 0.0) {
	return Constants.REALLY_LARGE_NUMBER;
      }
      return (1/response * conj_response);
    }
    else {
      return (response * conj_response);
    }
    
  }


  // method: mag_response_conj
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  //     double sample_frequency: (input) sample_frequency
  // 
  // this method returns the magnitude response of this pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double mag_response_conj(double frequency_in, 
				  double sample_frequency) {

    // declare local variables
    //
    double response = 0.0;
    double real_arg = -Math.PI * (bandwidth / sample_frequency);
    double freq_arg = Constants.ISIP_TWOPI * frequency_in / sample_frequency;
    double imag_arg = Constants.ISIP_TWOPI * (-frequency) / sample_frequency;

    // calculate the magnitude response
    //
    response = 1.0 - (2 * Math.exp(real_arg) * Math.cos(imag_arg - freq_arg)) +
      Math.exp(2 * real_arg);

    response = Math.sqrt(response);
    
    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      if (response == 0.0) {
	return Constants.REALLY_LARGE_NUMBER;
      }
      return (1/response);
    }
    else {
      return (response);
    }
    
  }


  // method: mag_response_conj
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  // 
  // this method returns the magnitude response of this analog pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double mag_response_conj(double frequency_in) {

    // declare local variables
    //
    double response = 0.0;
    double imaginary_mag = frequency_in - (-frequency);
    double real_mag = -0.5 *  bandwidth ;    

    // three cases for a pole or zero at 0 + j0, a real pole or zero
    // and a complex pole or zero
    //
    if (frequency == 0.0) {
      if (bandwidth == 0.0) {

	// single pole or zero at s = 0
	//
	response = (imaginary_mag * imaginary_mag) + 
	  (real_mag * real_mag);      
	response = Math.sqrt(response);
	
      }
      else {

	// single real pole or zero
	//
	response = (imaginary_mag * imaginary_mag) + 
	  (real_mag * real_mag);
	double normalizer = Math.sqrt(((bandwidth * bandwidth) / 4) + 
				      (-frequency) * (-frequency));
	response = Math.sqrt(response) / normalizer;
	
      }
    }
    else {

      // complex pole or zero
      //
      response = (imaginary_mag * imaginary_mag) + 
	(real_mag * real_mag);
      double normalizer = Math.sqrt(((bandwidth * bandwidth) / 4) + 
				    (-frequency) * (-frequency));
      response = Math.sqrt(response) / normalizer;
    }

    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      if (response == 0.0) {
	return Constants.REALLY_LARGE_NUMBER;
      }
      else {
	return 1/response;
      }
    }
    else {
      return response;
    }
  }


  // method: phase_response_conj
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  //     double sample_frequency: (input) sample frequency
  // 
  // this method returns the phase response of this pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double phase_response_conj(double frequency_in,
				    double sample_frequency) {

    // declare local variables
    //
    double response = 0.0;
    double mag,angle,rp,ip,omega,numerator,denominator;
    
    mag = Math.exp(-bandwidth * Math.PI / sample_frequency);
    angle = (-frequency * Math.PI * 2) /sample_frequency;
    rp = mag * Math.cos(angle);
    ip = mag * Math.sin(angle);
    omega = 2.0 * Math.PI * frequency_in /sample_frequency;

    if (pz_indicator == Constants.POLE_INDICATOR) {
	numerator = ip * Math.cos(omega) - rp * Math.sin(omega);
	denominator = 1 - rp * Math.cos(omega) - ip * Math.sin(omega);

	if((numerator > 0.0) && (denominator > 0.0)) {
	    response = Math.atan(numerator/denominator);
	}

	else if((numerator > 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) + Math.PI;
	}

	else if((numerator < 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) - Math.PI;
	}
	
	else {
	    response = Math.atan(numerator/denominator);
	}
    }

    else {
	numerator = rp * Math.sin(omega) - ip * Math.cos(omega);
	denominator = 1 - rp * Math.cos(omega) - ip * Math.sin(omega);

	if((numerator > 0.0) && (denominator > 0.0)) {
	    response = Math.atan(numerator/denominator);
	}

	else if((numerator > 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) + Math.PI;
	}

	else if((numerator < 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) - Math.PI;
	}
	
	else {
	    response = Math.atan(numerator/denominator);
	}
    }

    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      if (bandwidth > 0.0) {
	return (0.0 + response);
      }
      else {
	  //return (0.0 - response);
	  return (0.0 + response);
      }
    }
    else {
      if (bandwidth > 0.0) {
	  //return (-response);
	  return (response);
      }
      else {
	return (response);
      }
    }
  }


  // method: phase_response_conj
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  // 
  // this method returns the phase response of this pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double phase_response_conj(double frequency_in) {

    // declare local variables
    //
    double response = 0.0;

    // compute the response
    //
    double arg = 0.0;

    if (bandwidth == 0.0) {

      if ((frequency_in - (-frequency)) < 0.0) {

	// arctan of -infinity
	//
	response = -Math.PI/2;
      }

      else if ((frequency_in - (-frequency)) >= 0.0 ) {
	response = Math.PI/2;
      }

    }
    else {
	double numerator = 2 * (frequency_in - (-frequency));
	double denominator = bandwidth;
	
	if((numerator > 0.0) && (denominator > 0.0)) {
	    response = Math.atan(numerator/denominator);
	}

	else if((numerator > 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) + Math.PI;
	}

	else if((numerator < 0.0) && (denominator < 0.0)) {
	    response = Math.atan(numerator/denominator) - Math.PI;
	}
	
	else {
	    response = Math.atan(numerator/denominator);
	}
	//response = Math.atan(2 * (frequency_in - (-frequency)) / bandwidth);
    }

    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      return (0.0 - response);
    }
    else {
      return response;
    }

  }


  // method: three_d_mag_resp_conj
  //
  // parameters: 
  //     double real_freq: real_freq
  //     double imag_freq: imag_freq
  // 
  // this method returns the magnitude response of this analog pole-zero
  // object at the indicated complex frequency
  //
  // returns: double
  //  
  public double three_d_mag_resp_conj(double real_freq, double imag_freq) {

    // declare local variables
    //
    double response = 0.0;
    double imaginary_mag = imag_freq - (-frequency);
    double real_mag = real_freq + (0.5 *  bandwidth);    

    // three cases for a pole or zero at 0 + j0, a real pole or zero
    // and a complex pole or zero
    //
    if (frequency == 0.0) {
      if (bandwidth == 0.0) {

	// single pole or zero at s = 0
	//
	response = (imaginary_mag * imaginary_mag) + 
	  (real_mag * real_mag);      
	response = Math.sqrt(response);
	
      }
      else {

	// single real pole or zero
	//
	response = (imaginary_mag * imaginary_mag) + 
	  (real_mag * real_mag);
	double normalizer = Math.sqrt(((bandwidth * bandwidth) / 4) + 
				      (-frequency) * (-frequency));
	response = Math.sqrt(response) / normalizer;
	
      }
    }
    else {

      // complex pole or zero
      //
      response = (imaginary_mag * imaginary_mag) + 
	(real_mag * real_mag);
      double normalizer = Math.sqrt(((bandwidth * bandwidth) / 4) + 
				    (-frequency) * (-frequency));
      response = Math.sqrt(response) / normalizer;
    }

    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      if (response == 0.0) {
	return Constants.REALLY_LARGE_NUMBER;
      }
      else {
	return 1/response;
      }
    }
    else {
      return response;
    }

  }


  // method: three_d_mag_resp_conj
  //
  // parameters: 
  //     double real_freq: sigma
  //     double imag_freq: omega
  //     double sample_freq: sample frequency
  // 
  // this method returns the magnitude response of this analog pole-zero
  // object at the indicated complex frequency
  //
  // returns: double
  //  
  public double three_d_mag_resp_conj(double real_freq, double imag_freq, 
				 double sample_frequency) {

    // declare local variables
    //
    double response = 0.0;
    double real_arg = Math.PI * (Math.abs(real_freq - bandwidth) / sample_frequency);
    double freq_arg = Constants.ISIP_TWOPI * imag_freq / sample_frequency;
    double imag_arg = Constants.ISIP_TWOPI * (-frequency) / sample_frequency;

    // calculate the magnitude response
    //
    response = 1.0 - (2 * Math.exp(real_arg) * Math.cos(imag_arg - freq_arg)) +
      Math.exp(2 * real_arg);

    response = Math.sqrt(response);
    
    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      if (response == 0.0) {
	return Constants.REALLY_LARGE_NUMBER;
      }
      return 1/response;
    }
    else {
      return response;
    }
    
  }


  // method: debug
  //
  // parameters: none
  // 
  // this method prints the variables of this class
  //
  // returns: boolean
  //  
  public boolean debug() {

    // print all member variables of this class
    //
    System.out.println("Object Name = " + this + "\n"
		       + "type = " + type_indicator + "\n"
		       + "real portion = " + frequency + "\n"
		       + "imag portion = " + bandwidth + "\n");

    // exit gracefully
    //
    return true;

  }

  // method: is_conjugate
  //
  // parameters:
  //
  // this is a method to check if the pole/zero has been conjugated
  //
  // returns: boolean
  //
  public boolean is_conjugate() {

    return conj_flag;
  }

   // method: phase_response
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  //     double sample_frequency: (input) sample frequency
  // 
  // this method returns the phase response of this pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double imp_phase_response(double frequency_in, 
				   double sample_frequency) {

    // declare local variables
    //
    double conj_response = 0.0;
    double response = 0.0;
    double real_arg;

    if (bandwidth > 0.0) {
      real_arg = Math.PI * (bandwidth / sample_frequency);
    }
    else {
      real_arg = -Math.PI * (bandwidth / sample_frequency);
    }
    double imag_arg = Constants.ISIP_TWOPI * frequency / sample_frequency;
    double freq_arg = Constants.ISIP_TWOPI * frequency_in / sample_frequency;
    double real_part = Math.cos(freq_arg) - (Math.exp(real_arg) * 
					     Math.cos(imag_arg));
    double imag_part = Math.sin(freq_arg) - (Math.exp(real_arg) *
					     Math.sin(imag_arg));

    // evaluate the phase response
    //
    if (real_part == 0.0) {
	if (imag_part == 0.0) {
	    return(this.imp_phase_response(frequency_in + 2.0E-7,
				       sample_frequency));
	}
	else {
	    if (imag_part < 0.0) {
		response = - Math.PI / 2;
	    }
	    else {
		response = Math.PI / 2;
	    }
	}
    }
    
      else {
	  if (imag_part == 0.0) {
	      response = 0.0;
	  }
	  else {
  	    response = Math.atan(imag_part/real_part);
	  }
      }
    
    // see if this pole/zero is a conjugate
    //
    if (conj_flag) {
	conj_response = this.imp_phase_response_conj(frequency_in, sample_frequency);
    }
    
    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
	if (bandwidth > 0.0) {
	    return (0.0 + response + conj_response);
	}
	else {
	    return (0.0 - response + conj_response);
	}
    }
    else {
	if (bandwidth > 0.0) {
	    return (-response + conj_response);
	}
	else {
	    return (response + conj_response);
	}
    }
  }

  // method: phase_response_conj
  //
  // parameters: 
  //     double frequency_in: (input) frequency to evaluate at
  //     double sample_frequency: (input) sample frequency
  // 
  // this method returns the phase response of this pole-zero
  // object at the indicated frequency
  //
  // returns: double
  //  
  public double imp_phase_response_conj(double frequency_in,
				    double sample_frequency) {

    // declare local variables
    //
    double response = 0.0;
    double real_arg;
    if (bandwidth > 0.0) {
      real_arg = Math.PI * (bandwidth / sample_frequency);
    }
    else {
      real_arg = -Math.PI * (bandwidth / sample_frequency);
    }
    double imag_arg = Constants.ISIP_TWOPI * (-frequency) / sample_frequency;
    double freq_arg = Constants.ISIP_TWOPI * frequency_in / sample_frequency;
    double real_part = Math.cos(freq_arg) - (Math.exp(real_arg) * 
					     Math.cos(imag_arg));
    double imag_part = Math.sin(freq_arg) - (Math.exp(real_arg) *
					     Math.sin(imag_arg));

    // evaluate the phase response
    //
    if (real_part == 0.0) {
        if (imag_part == 0.0) {
	    return(this.imp_phase_response(frequency_in + 2.0E-7, sample_frequency));
        }
        else {
	    if (imag_part < 0.0) {
		response = -Math.PI / 2;
	    }
	    else {
		response = Math.PI / 2;
	    }
        }
    }
    else {
        if (imag_part == 0.0) {
	    response = 0.0;
	    }
        else {
	    response = Math.atan(imag_part/real_part);
        }
    }

    // exit and return the proper response for either a pole or a zero
    //
    if (pz_indicator == Constants.POLE_INDICATOR) {
      if (bandwidth > 0.0) {
	return (0.0 + response);
      }
      else {
	return (0.0 - response);
      }
    }
    else {
      if (bandwidth > 0.0) {
	return (-response);
      }
      else {
	return (response);
      }
    }
  }
} // end class PoleZero
