// file: SystemPolynomial.java
//

// include the necessary system files
//
//import java.awt.*;
import java.lang.*;
import javax.swing.*;

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

// class: SystemPolynomial
//
// this class implements methods to determine the characteristic
// polynomial of a system based on the poles and zeros of the system
//
public class SystemPolynomial implements Runnable, Constants {
    
    // constant to define the sleep time of the thread
    // measured in milliseconds
    //
    public final static long THREAD_SLEEP_TIME = 600;
    
    // constant to define the threshold before something is zerod
    //
    public final static double THRESH_ZERO = 1E-20;
    
    // set class variables
    //
    private double sample_frequency = Constants.DEFAULT_SAMPLE_FREQ;
    
    // set class variables
    //
    private double dc_gain = 1;
    
    // set up a flag to determine if the display needs to be
    // updated and initialize it to false
    //
    boolean update_flag = false;
    
    // array to hold the poles and zeros necessary to evaluate the filter
    //
    private PoleZero pz_array[];
    
    // variable for the type of response: analog/digital
    //
    private int design_type = Constants.DEFAULT_DESIGN;
    
    // variable for the coefficients of the polynomial the size is based
    // on the maximum order allowed for the system
    //
    private double num_coeffs[] = new double[4*(Constants.MAX_ARRAY_SIZE) + 2];
    private double den_coeffs[] = new double[4*(Constants.MAX_ARRAY_SIZE) + 2];
    private double scale_factor[] = new double[2];
    
    // define the order of the system and the maximum possible order
    //
    private int num_order = 0;
    private int den_order = 0;
    private int max_order = 2*Constants.MAX_ARRAY_SIZE + 1;
    
    // set up a thread to take care of updating the display
    //
    Thread sys_poly_thread;
    
    //*****************************************************************
    //
    // constructor methods
    //
    //*****************************************************************
    
    // method: SystemPolynomial
    //
    // parameters: 
    //     double frequency: (input) sample frequency for digital designs
    //     PoleZero pz_array_a[]: (input) pointer to the poles and zeros
    //     int type: (input) type of output: analog/digital
    // 
    // this method constructs a System Polynomial object
    //
    public SystemPolynomial(double frequency, PoleZero pz_array_a[], int type) {
	
	// initialize the frequency and set up a pointer to the
	// array of poles and zeros.
	//
	sample_frequency = frequency;
	pz_array = pz_array_a;
	int design_type = type;
	
	// initiate the thread for this object
	//
	sys_poly_thread = new Thread(this);
	sys_poly_thread.setPriority(Thread.MAX_PRIORITY);
	sys_poly_thread.start();
    }
    
    
    //****************************************************************
    //
    // private methods
    //
    //****************************************************************
    
    
    // method: calc_analog
    //
    // parameters: none
    // 
    // this method determines the analog system polynomial for the system 
    // described by the poles and zeros in pz_array
    //
    // returns: boolean
    //
    private boolean calc_analog() {
	
	// declare local variables
	//
	int max_index = max_order << 1;
	double first_factor[] = new double[2];
	double zero_factor[] = new double[2];
	double real, imag;
	double working_array[] = new double[max_index];
	
	// zero out the orders
	//
	num_order = 0;
	den_order = 0;
	
	
	// zero the coefficients
	//
	for (int i = 0; i < max_index; i++) {
	    num_coeffs[i] = 0.0;
	    den_coeffs[i] = 0.0;
	}
	
	// set the zero order term for the numerator.
	//
	num_coeffs[0] = 1;
	
	// make sure that there is at least one pole or zero.  if not then
	// the system has a function of H(s) = 1. set this and return
	//
	if (pz_array[0] == null) {
	    
	    // do nothing since the zero order numerator term is already set to
	    // 1
	    //
	}
	else {
	    
	    // loop through all poles and zeros
	    //
	    int i = 0;
	    while (pz_array[i] != null) {
		
		// make sure it is a pole
		//
		if (pz_array[i].pz_indicator == Constants.POLE_INDICATOR) {
		    
		    // take care of the first term
		    //
		    if (den_order == 0) {
			
			// determine the scale factor for s and set the
			// coefficients accordingly
			//
			imag = - (pz_array[i].frequency * Constants.ISIP_TWOPI);
			real = pz_array[i].bandwidth * 0.5 * Constants.ISIP_TWOPI;
			invert(first_factor, zero_factor, real, imag);
			den_coeffs[3] = first_factor[1];
			den_coeffs[2] = first_factor[0];
			den_coeffs[1] = zero_factor[1];
			den_coeffs[0] = zero_factor[0];
			den_order++;
			
			// check for a conjugate if it is then perform the necessary
			// steps
			//
			if (pz_array[i].is_conjugate()) {
			    
			    // determine the scale factor for the s term
			    //
			    imag = (pz_array[i].frequency * Constants.ISIP_TWOPI);
			    real = pz_array[i].bandwidth * 0.5 * Constants.ISIP_TWOPI;
			    invert(first_factor, zero_factor, real, imag);
			    
			    // multiply by the first order term
			    //
			    int j = 1;
			    for (int k = 0; k <= den_order; k++, j++) {
				
				// multiply and copy the first order term by all other terms
				//
				working_array[2*j] = (first_factor[0] * den_coeffs[2*k])
				    - (first_factor[1] * den_coeffs[2*k+1]);
				working_array[2*j+1] = (first_factor[0] * den_coeffs[2*k+1])
				    + (first_factor[1] * den_coeffs[2*k]);
				
			    }
			    
			    // zero the zero order term
			    //
			    working_array[1] = working_array[0] = 0.0;
			    
			    // multiply and add by zero order terms
			    //
			    for (int k = 0; k <= den_order; k++) {
				working_array[2*k] += (zero_factor[0] * den_coeffs[2*k])
				    - (zero_factor[1] * den_coeffs[2*k+1]);
				working_array[2*k+1] += (zero_factor[0] * den_coeffs[2*k+1])
				    - (zero_factor[1] * den_coeffs[2*k]);
			    }
			    
			    // increase the order of the denominator
			    //
			    den_order++;
			    
			    // copy over the working array
			    //
			    for (int k = 0; k <= den_order; k++) {
				den_coeffs[2*k] = working_array[2*k];
				den_coeffs[2*k+1] = working_array[2*k+1];
			    }  
			}
		    }
		    else {
			
			// determine the scale factor for the s term
			//
			imag = - (pz_array[i].frequency * Constants.ISIP_TWOPI);
			real = pz_array[i].bandwidth * 0.5 * Constants.ISIP_TWOPI;
			invert(first_factor, zero_factor, real, imag);
			
			// multiply by the first order term
			//
			int j = 1;
			for (int k = 0; k <= den_order; k++, j++) {
			    
			    // multiply and copy the first order term by all other terms
			    //
			    working_array[2*j] = (first_factor[0] * den_coeffs[2*k])
				- (first_factor[1] * den_coeffs[2*k+1]);
			    working_array[2*j+1] = (first_factor[0] * den_coeffs[2*k+1])
				+ (first_factor[1] * den_coeffs[2*k]);
			    
			}
			
			// zero the zero order term
			//
			working_array[1] = working_array[0] = 0.0;
			
			// multiply and add by zero order terms
			//
			for (int k = 0; k <= den_order; k++) {
			    working_array[2*k] += (zero_factor[0] * den_coeffs[2*k])
				- (zero_factor[1] * den_coeffs[2*k+1]);
			    working_array[2*k+1] += (zero_factor[0] * den_coeffs[2*k+1])
				- (zero_factor[1] * den_coeffs[2*k]);
			}
			
			// increase the order of the denominator
			//
			den_order++;
			
			// copy over the working array
			//
			for (int k = 0; k <= den_order; k++) {
			    den_coeffs[2*k] = working_array[2*k];
			    
			    den_coeffs[2*k+1] = working_array[2*k+1];
			    
			    // check for the zero threshold
			    //
			    if (Math.abs(den_coeffs[2*k+1]) < THRESH_ZERO) {
				den_coeffs[2*k+1] = 0.0;
			    }
			    
			}  
			
			// check for a conjugate if it is then perform the necessary
			// steps
			//
			if (pz_array[i].is_conjugate()) {
			    
			    // determine the scale factor for the s term
			    //
			    imag = (pz_array[i].frequency * Constants.ISIP_TWOPI);
			    real = pz_array[i].bandwidth * 0.5 * Constants.ISIP_TWOPI;
			    invert(first_factor, zero_factor, real, imag);
			    
			    // multiply by the first order term
			    //
			    j = 1;
			    for (int k = 0; k <= den_order; k++, j++) {
				
				// multiply and copy the first order term by all other terms
				//
				working_array[2*j] = (first_factor[0] * den_coeffs[2*k])
				    - (first_factor[1] * den_coeffs[2*k+1]);
				working_array[2*j+1] = (first_factor[0] * den_coeffs[2*k+1])
				    + (first_factor[1] * den_coeffs[2*k]);
				
			    }
			    
			    // zero the zero order term
			    //
			    working_array[1] = working_array[0] = 0.0;
			    
			    // multiply and add by zero order terms
			    //
			    for (int k = 0; k <= den_order; k++) {
				working_array[2*k] += (zero_factor[0] * den_coeffs[2*k])
				    - (zero_factor[1] * den_coeffs[2*k+1]);
				working_array[2*k+1] += (zero_factor[0] * den_coeffs[2*k+1])
				    - (zero_factor[1] * den_coeffs[2*k]);
			    }
			    
			    // increase the order of the denominator
			    //
			    den_order++;
			    
			    // copy over the working array
			    //
			    for (int k = 0; k <= den_order; k++) {
				den_coeffs[2*k] = working_array[2*k];
				
				den_coeffs[2*k+1] = working_array[2*k+1];
				
				// check for the zero threshold
				//
				if (Math.abs(den_coeffs[2*k+1]) < THRESH_ZERO) {
				    den_coeffs[2*k+1] = 0.0;
				}
			    }  
			}
		    }
		}
		else {
		    
		    // take care of the first term
		    //
		    if (num_order == 0) {
			
			// determine the scale factor for s and set the
			// coefficients accordingly
			//
			imag = - (pz_array[i].frequency * Constants.ISIP_TWOPI);
			real = pz_array[i].bandwidth * 0.5 * Constants.ISIP_TWOPI;
			invert(first_factor, zero_factor, real, imag);
			num_coeffs[3] = first_factor[1];
			num_coeffs[2] = first_factor[0];
			num_coeffs[1] = zero_factor[1];
			num_coeffs[0] = zero_factor[0];
			num_order++;
			
			// check for conjugate.  if so then perform the necessary steps
			// on the conjugate
			// 
			if (pz_array[i].is_conjugate()) {
			    
			    // determine the scale factor for the s term
			    //
			    imag = (pz_array[i].frequency * Constants.ISIP_TWOPI);
			    real = pz_array[i].bandwidth * 0.5 * Constants.ISIP_TWOPI;
			    invert(first_factor, zero_factor, real, imag);
			    
			    // multiply by the first order term
			    //
			    int j = 1;
			    for (int k = 0; k <= num_order; k++, j++) {
				
				// multiply and copy the first order term by all other terms
				//
				working_array[2*j] = (first_factor[0] * num_coeffs[2*k])
				    - (first_factor[1] * num_coeffs[2*k+1]);
				working_array[2*j+1] = (first_factor[0] * num_coeffs[2*k+1])
				    - (first_factor[1] * num_coeffs[2*k]);
				
			    }
			    
			    // zero the zero order term
			    //
			    working_array[1] = working_array[0] = 0.0;
			    
			    // multiply and add by zero order terms
			    //
			    for (int k = 0; k <= num_order; k++) {
				working_array[2*k] += (zero_factor[0] * num_coeffs[2*k])
				    - (zero_factor[1] * num_coeffs[2*k+1]);
				working_array[2*k+1] += (zero_factor[0] * num_coeffs[2*k+1])
				    - (zero_factor[1] * num_coeffs[2*k]);
			    }
			    
			    // increase the order of the denominator
			    //
			    num_order++;
			    
			    // copy over the working array
			    //
			    for (int k = 0; k <= num_order; k++) {
				num_coeffs[2*k] = working_array[2*k];
				
				num_coeffs[2*k+1] = working_array[2*k+1];
				
				// check for the zero threshold
				//
				if (Math.abs(num_coeffs[2*k+1]) < THRESH_ZERO) {
				    num_coeffs[2*k+1] = 0.0;
				}
			    }  
			}
		    }
		    else {
			
			// determine the scale factor for the s term
			//
			imag = - (pz_array[i].frequency * Constants.ISIP_TWOPI);
			real = pz_array[i].bandwidth * 0.5 * Constants.ISIP_TWOPI;
			invert(first_factor, zero_factor, real, imag);
			
			// multiply by the first order term
			//
			int j = 1;
			for (int k = 0; k <= num_order; k++, j++) {
			    
			    // multiply and copy the first order term by all other terms
			    //
			    working_array[2*j] = (first_factor[0] * num_coeffs[2*k])
				- (first_factor[1] * num_coeffs[2*k+1]);
			    working_array[2*j+1] = (first_factor[0] * num_coeffs[2*k+1])
				- (first_factor[1] * num_coeffs[2*k]);
			}
			
			// zero the zero order term
			//
			working_array[1] = working_array[0] = 0.0;
			
			// multiply and add by zero order terms
			//
			for (int k = 0; k <= num_order; k++) {
			    working_array[2*k] += (zero_factor[0] * num_coeffs[2*k])
				- (zero_factor[1] * num_coeffs[2*k+1]);
			    working_array[2*k+1] += (zero_factor[0] * num_coeffs[2*k+1])
				- (zero_factor[1] * num_coeffs[2*k]);
			}
			
			// increase the order of the denominator
			//
			num_order++;
			
			// copy over the working array
			//
			for (int k = 0; k <= num_order; k++) {
			    num_coeffs[2*k] = working_array[2*k];
			    
			    num_coeffs[2*k+1] = working_array[2*k+1];
			    
			    // check for the zero threshold
			    //
			    if (Math.abs(num_coeffs[2*k+1]) < THRESH_ZERO) {
				num_coeffs[2*k+1] = 0.0;
			    }
			    
			}  
			
			// check for conjugate.  if so then perform the necessary steps
			// on the conjugate
			// 
			if (pz_array[i].is_conjugate()) {
			    
			    // determine the scale factor for the s term
			    //
			    imag = (pz_array[i].frequency * Constants.ISIP_TWOPI);
			    real = pz_array[i].bandwidth * 0.5 * Constants.ISIP_TWOPI;
			    invert(first_factor, zero_factor, real, imag);
			    
			    // multiply by the first order term
			    //
			    j = 1;
			    for (int k = 0; k <= num_order; k++, j++) {
				
				// multiply and copy the first order term by all other terms
				//
				working_array[2*j] = (first_factor[0] * num_coeffs[2*k])
				    - (first_factor[1] * num_coeffs[2*k+1]);
				working_array[2*j+1] = (first_factor[0] * num_coeffs[2*k+1])
				    - (first_factor[1] * num_coeffs[2*k]);
				
			    }
			    
			    // zero the zero order term
			    //
			    working_array[1] = working_array[0] = 0.0;
			    
			    // multiply and add by zero order terms
			    //
			    for (int k = 0; k <= num_order; k++) {
				working_array[2*k] += (zero_factor[0] * num_coeffs[2*k])
				    - (zero_factor[1] * num_coeffs[2*k+1]);
				working_array[2*k+1] += (zero_factor[0] * num_coeffs[2*k+1])
				    - (zero_factor[1] * num_coeffs[2*k]);
			    }
			    
			    // increase the order of the denominator
			    //
			    num_order++;
			    
			    // copy over the working array
			    //
			    for (int k = 0; k <= num_order; k++) {
				num_coeffs[2*k] = working_array[2*k];
				
				num_coeffs[2*k+1] = working_array[2*k+1];
				
				// check for the zero threshold
				//
				if (Math.abs(num_coeffs[2*k+1]) < THRESH_ZERO) {
				    num_coeffs[2*k+1] = 0.0;
				}
			    }  
			}
		    }
		}
		
		// increment counter
		//
		i++;
	    }
	}
	
	
	// multiply all zero terms by the dc gain
	//
	for (int i = 0; i <= num_order; i++) {
	    num_coeffs[2*i] *= dc_gain;
	    num_coeffs[2*i+1] *= dc_gain;
	}
	
	// normalize the numerator and denominator by the coefficient of the
	// highest order term
	//
	// determine the scale factor to be pulled out
	//
	double num_factor[] = new double[2];
	double den_factor[] = new double[2];
	
	// assign the values to the various factors
	//
	num_factor[1] = num_coeffs[2*num_order+1];
	num_factor[0] = num_coeffs[2*num_order];
	den_factor[1] = den_coeffs[2*den_order+1];
	den_factor[0] = den_coeffs[2*den_order];
	scale_factor[1] = ((num_factor[1] * den_factor[0]) - 
			   (num_factor[0] * den_factor[1])) /
	    (den_factor[0] * den_factor[0] + 
	     den_factor[1] * den_factor[1]);
	
	scale_factor[0] = ((num_factor[0] * den_factor[0]) + 
			   (num_factor[1] * den_factor[1])) /
	    (den_factor[0] * den_factor[0] + 
	     den_factor[1] * den_factor[1]);
	
	// normalize to the highest order
	//
	for (int kk = 0; kk <= num_order; kk++) {
	    real = num_coeffs[2*kk];
	    imag = num_coeffs[2*kk+1];
	    num_coeffs[2*kk+1] = ((imag * num_factor[0]) - 
				  (real * num_factor[1])) /
		(num_factor[0] * num_factor[0] + 
		 num_factor[1] * num_factor[1]);
	    
	    num_coeffs[2*kk] = ((real * num_factor[0]) + 
				(imag * num_factor[1])) /
		(num_factor[0] * num_factor[0] + 
		 num_factor[1] * num_factor[1]);
	}
	
	for (int kk = 0; kk <= den_order; kk++) {
	    real = den_coeffs[2*kk];
	    imag = den_coeffs[2*kk+1];
	    den_coeffs[2*kk+1] = ((imag * den_factor[0]) - 
				  (real * den_factor[1])) /
		(den_factor[0] * den_factor[0] + 
		 den_factor[1] * den_factor[1]);
	    
	    den_coeffs[2*kk] = ((real * den_factor[0]) + 
				(imag * den_factor[1])) /
		(den_factor[0] * den_factor[0] + 
		 den_factor[1] * den_factor[1]);
	}
	
	// exit gracefully
	//
	return true;
    }
    
    
    // method: debug
    //
    // parameters: none
    //     
    // this method prints debugging information
    //
    // returns boolean
    //
    public boolean debug() {
	
	if (design_type == Constants.ANALOG) {
	    
	    // print out the scale_factor
	    //
	    
	    // print out the numerator
	    //
	    for (int kk = num_order; kk >= 0; kk--) {
		
	    }
	    // print out the denominator
	    //
	    for (int kk = den_order; kk >= 0; kk--) {
		
	    }
	    
	}
	else {
	    
	    // print out the scale_factor
	    //
	    
	    // print out the numerator
	    //
	    for (int kk = num_order; kk >= 0; kk--) {
		
	    }
	    
	    // print out the denominator
	    //
	    for (int kk = den_order; kk >= 0; kk--) {
		
	    }
	}
	
	// exit gracefully
	//
	return true;
    }
    
    
    // method: invert
    //
    // parameters: 
    //     double first_order[]: first order term to find
    //     double zero_order[]: zero order term to find
    //     double real: real portion of monomial
    //     double imag: imaginary portion of monomial
    // 
    // this method inverts a single s plane term to standard form
    // for example: (s + (a + jb)) ==> (s/(a+jb) + 1)
    //
    // returns: boolean
    //
    private boolean invert(double first_order[], double zero_order[],
			   double real, double imag) {
	
	// declare temporary variables
	//
	double temp_real, temp_imag;
	
	// check all cases
	//
	if (imag == 0.0) {
	    if (real == 0.0) {
		first_order[0] = 1.0;
		first_order[1] = 0.0;
		zero_order[0] = 0.0;
		zero_order[1] = 0.0;
	    }
	    else {
		first_order[0] = 1.0 / real;
		first_order[1] = 0.0;
		zero_order[0] = 1.0;
		zero_order[1] = 0.0;
	    }
	}
	else {
	    if (real == 0.0) {
		first_order[0] = 0.0;
		first_order[1] = -1.0/imag;
		zero_order[0] = 1.0;
		zero_order[1] = 0.0;
	    }
	    else {
		first_order[0] = real / (real*real + imag*imag);
		first_order[1] = -1.0 * imag / (real*real + imag*imag);
		zero_order[0] = 1.0;
		zero_order[1] = 0.0;
	    }
	}
	
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: calc_digital
    //
    // parameters: none
    // 
    // this method determines the digital system polynomial for the system 
    // described by the poles and zeros in pz_array
    //
    // returns: boolean
    //
    private boolean calc_digital() {
	
	// declare local variables
	//
	int max_index = max_order << 1;
	double first_factor[] = new double[2];
	double zero_factor[] = new double[2];
	double real, imag;
	double working_array[] = new double[max_index];
	
	// zero out the orders
	//
	num_order = 0;
	den_order = 0;
	
	
	// zero the coefficients
	//
	for (int i = 0; i < max_index; i++) {
	    num_coeffs[i] = 0.0;
	    den_coeffs[i] = 0.0;
	}
	
	// set the zero order term for the numerator.
	//
	num_coeffs[0] = 1;
	
	// make sure that there is at least one pole or zero.  if not then
	// the system has a function of H(s) = 1. set this and return
	//
	if (pz_array[0] == null) {
	    
	    // do nothing since the zero order numerator term is already set to
	    // 1
	    //
	}
	else {
	    
	    // loop through all poles and zeros
	    //
	    int i = 0;
	    while (pz_array[i] != null) {
		
		// make sure it is a pole
		//
		if (pz_array[i].pz_indicator == Constants.POLE_INDICATOR) {
		    
		    // take care of the first term
		    //
		    if (den_order == 0) {
			
			// determine the scale factor for s and set the
			// coefficients accordingly
			//
			imag = - ZPlane.get_imag(pz_array[i].frequency, 
						 pz_array[i].bandwidth,
						 sample_frequency);
			real = - ZPlane.get_real(pz_array[i].frequency, 
						 pz_array[i].bandwidth,
						 sample_frequency);

			invert(first_factor, zero_factor, real, imag);
			den_coeffs[3] = first_factor[1];
			den_coeffs[2] = first_factor[0];
			den_coeffs[1] = zero_factor[1];
			den_coeffs[0] = zero_factor[0];
			den_order++;
			this.debug();
			
			// check for a conjugate if it is then perform the necessary
			// steps
			//
			if (pz_array[i].is_conjugate()) {
			    
			    // determine the scale factor for the s term
			    //
			    imag = - ZPlane.get_imag((-pz_array[i].frequency), 
						     pz_array[i].bandwidth,
						     sample_frequency);
			    real = - ZPlane.get_real(-pz_array[i].frequency, 
						     pz_array[i].bandwidth,
						     sample_frequency);
			    invert(first_factor, zero_factor, real, imag);
			    
			    // multiply by the first order term
			    //
			    int j = 1;
			    for (int k = 0; k <= den_order; k++, j++) {
				
				// multiply and copy the first order term by all other terms
				//
				working_array[2*j] = (first_factor[0] * den_coeffs[2*k])
				    - (first_factor[1] * den_coeffs[2*k+1]);
				working_array[2*j+1] = (first_factor[0] * den_coeffs[2*k+1])
				    + (first_factor[1] * den_coeffs[2*k]);
				
			    }
			    
			    // zero the zero order term
			    //
			    working_array[1] = working_array[0] = 0.0;
			    
			    // multiply and add by zero order terms
			    //
			    for (int k = 0; k <= den_order; k++) {
				working_array[2*k] += (zero_factor[0] * den_coeffs[2*k])
				    - (zero_factor[1] * den_coeffs[2*k+1]);
				working_array[2*k+1] += (zero_factor[0] * den_coeffs[2*k+1])
				    - (zero_factor[1] * den_coeffs[2*k]);
			    }
			    
			    // increase the order of the denominator
			    //
			    den_order++;
			    
			    // copy over the working array
			    //
			    for (int k = 0; k <= den_order; k++) {
				den_coeffs[2*k] = working_array[2*k];
				den_coeffs[2*k+1] = working_array[2*k+1];
			    }  
			}
		    }
		    else {
			
			// determine the scale factor for the s term
			//
			imag = - ZPlane.get_imag(pz_array[i].frequency, 
						 pz_array[i].bandwidth,
						 sample_frequency);
			real = - ZPlane.get_real(pz_array[i].frequency, 
						 pz_array[i].bandwidth,
						 sample_frequency);
			invert(first_factor, zero_factor, real, imag);
			
			// multiply by the first order term
			//
			int j = 1;
			for (int k = 0; k <= den_order; k++, j++) {
			    
			    // multiply and copy the first order term by all other terms
			    //
			    working_array[2*j] = (first_factor[0] * den_coeffs[2*k])
				- (first_factor[1] * den_coeffs[2*k+1]);
			    working_array[2*j+1] = (first_factor[0] * den_coeffs[2*k+1])
				+ (first_factor[1] * den_coeffs[2*k]);
			    
			}
			
			// zero the zero order term
			//
			working_array[1] = working_array[0] = 0.0;
			
			// multiply and add by zero order terms
			//
			for (int k = 0; k <= den_order; k++) {
			    working_array[2*k] += (zero_factor[0] * den_coeffs[2*k])
				- (zero_factor[1] * den_coeffs[2*k+1]);
			    working_array[2*k+1] += (zero_factor[0] * den_coeffs[2*k+1])
				- (zero_factor[1] * den_coeffs[2*k]);
			}
			
			// increase the order of the denominator
			//
			den_order++;
			
			// copy over the working array
			//
			for (int k = 0; k <= den_order; k++) {
			    den_coeffs[2*k] = working_array[2*k];
			    
			    den_coeffs[2*k+1] = working_array[2*k+1];
			    
			    // check for the zero threshold
			    //
			    if (Math.abs(den_coeffs[2*k+1]) < THRESH_ZERO) {
				den_coeffs[2*k+1] = 0.0;
			    }
			    
			}  
			
			// check for a conjugate if it is then perform the necessary
			// steps
			//
			if (pz_array[i].is_conjugate()) {
			    
			    // determine the scale factor for the s term
			    //
			    imag = - ZPlane.get_imag((-pz_array[i].frequency), 
						     pz_array[i].bandwidth,
						     sample_frequency);
			    real = - ZPlane.get_real(-pz_array[i].frequency, 
						     pz_array[i].bandwidth,
						     sample_frequency);
			    invert(first_factor, zero_factor, real, imag);
			    
			    // multiply by the first order term
			    //
			    j = 1;
			    for (int k = 0; k <= den_order; k++, j++) {
				
				// multiply and copy the first order term by all other terms
				//
				working_array[2*j] = (first_factor[0] * den_coeffs[2*k])
				    - (first_factor[1] * den_coeffs[2*k+1]);
				working_array[2*j+1] = (first_factor[0] * den_coeffs[2*k+1])
				    + (first_factor[1] * den_coeffs[2*k]);
				
			    }
			    
			    // zero the zero order term
			    //
			    working_array[1] = working_array[0] = 0.0;
			    
			    // multiply and add by zero order terms
			    //
			    for (int k = 0; k <= den_order; k++) {
				working_array[2*k] += (zero_factor[0] * den_coeffs[2*k])
				    - (zero_factor[1] * den_coeffs[2*k+1]);
				working_array[2*k+1] += (zero_factor[0] * den_coeffs[2*k+1])
				    - (zero_factor[1] * den_coeffs[2*k]);
			    }
			    
			    // increase the order of the denominator
			    //
			    den_order++;
			    
			    // copy over the working array
			    //
			    for (int k = 0; k <= den_order; k++) {
				den_coeffs[2*k] = working_array[2*k];
				
				den_coeffs[2*k+1] = working_array[2*k+1];
				
				// check for the zero threshold
				//
				if (Math.abs(den_coeffs[2*k+1]) < THRESH_ZERO) {
				    den_coeffs[2*k+1] = 0.0;
				}
			    }  
			}
		    }
		}
		else {
		    
		    // take care of the first term
		    //
		    if (num_order == 0) {
			
			// determine the scale factor for s and set the
			// coefficients accordingly
			//
			imag = - ZPlane.get_imag(pz_array[i].frequency, 
						 pz_array[i].bandwidth,
						 sample_frequency);
			real = - ZPlane.get_real(pz_array[i].frequency, 
						 pz_array[i].bandwidth,
						 sample_frequency);
			invert(first_factor, zero_factor, real, imag);
			num_coeffs[3] = first_factor[1];
			num_coeffs[2] = first_factor[0];
			num_coeffs[1] = zero_factor[1];
			num_coeffs[0] = zero_factor[0];
			num_order++;
			
			// check for conjugate.  if so then perform the necessary steps
			// on the conjugate
			// 
			if (pz_array[i].is_conjugate()) {
			    
			    // determine the scale factor for the s term
			    //
			    imag = - ZPlane.get_imag((-pz_array[i].frequency), 
						     pz_array[i].bandwidth,
						     sample_frequency);
			    real = - ZPlane.get_real(-pz_array[i].frequency, 
						     pz_array[i].bandwidth,
						     sample_frequency);
			    invert(first_factor, zero_factor, real, imag);
			    
			    // multiply by the first order term
			    //
			    int j = 1;
			    for (int k = 0; k <= num_order; k++, j++) {
				
				// multiply and copy the first order term by all other terms
				//
				working_array[2*j] = (first_factor[0] * num_coeffs[2*k])
				    - (first_factor[1] * num_coeffs[2*k+1]);
				working_array[2*j+1] = (first_factor[0] * num_coeffs[2*k+1])
				    - (first_factor[1] * num_coeffs[2*k]);
				
			    }
			    
			    // zero the zero order term
			    //
			    working_array[1] = working_array[0] = 0.0;
			    
			    // multiply and add by zero order terms
			    //
			    for (int k = 0; k <= num_order; k++) {
				working_array[2*k] += (zero_factor[0] * num_coeffs[2*k])
				    - (zero_factor[1] * num_coeffs[2*k+1]);
				working_array[2*k+1] += (zero_factor[0] * num_coeffs[2*k+1])
				    - (zero_factor[1] * num_coeffs[2*k]);
			    }
			    
			    // increase the order of the denominator
			    //
			    num_order++;
			    
			    // copy over the working array
			    //
			    for (int k = 0; k <= num_order; k++) {
				num_coeffs[2*k] = working_array[2*k];
				
				num_coeffs[2*k+1] = working_array[2*k+1];
				
				// check for the zero threshold
				//
				if (Math.abs(num_coeffs[2*k+1]) < THRESH_ZERO) {
				    num_coeffs[2*k+1] = 0.0;
				}
			    }  
			}
		    }
		    else {
			
			// determine the scale factor for the s term
			//
			imag = - ZPlane.get_imag(pz_array[i].frequency, 
						 pz_array[i].bandwidth,
						 sample_frequency);
			real = - ZPlane.get_real(pz_array[i].frequency, 
						 pz_array[i].bandwidth,
						 sample_frequency);
			invert(first_factor, zero_factor, real, imag);
			
			// multiply by the first order term
			//
			int j = 1;
			for (int k = 0; k <= num_order; k++, j++) {
			    
			    // multiply and copy the first order term by all other terms
			    //
			    working_array[2*j] = (first_factor[0] * num_coeffs[2*k])
				- (first_factor[1] * num_coeffs[2*k+1]);
			    working_array[2*j+1] = (first_factor[0] * num_coeffs[2*k+1])
				- (first_factor[1] * num_coeffs[2*k]);
			}
			
			// zero the zero order term
			//
			working_array[1] = working_array[0] = 0.0;
			
			// multiply and add by zero order terms
			//
			for (int k = 0; k <= num_order; k++) {
			    working_array[2*k] += (zero_factor[0] * num_coeffs[2*k])
				- (zero_factor[1] * num_coeffs[2*k+1]);
			    working_array[2*k+1] += (zero_factor[0] * num_coeffs[2*k+1])
				- (zero_factor[1] * num_coeffs[2*k]);
			}
			
			// increase the order of the denominator
			//
			num_order++;
			
			// copy over the working array
			//
			for (int k = 0; k <= num_order; k++) {
			    num_coeffs[2*k] = working_array[2*k];
			    
			    num_coeffs[2*k+1] = working_array[2*k+1];
			    
			    // check for the zero threshold
			    //
			    if (Math.abs(num_coeffs[2*k+1]) < THRESH_ZERO) {
				num_coeffs[2*k+1] = 0.0;
			    }
			    
			}  
			
			// check for conjugate.  if so then perform the necessary steps
			// on the conjugate
			// 
			if (pz_array[i].is_conjugate()) {
			    
			    // determine the scale factor for the s term
			    //
			    imag = - ZPlane.get_imag((-pz_array[i].frequency), 
						     pz_array[i].bandwidth,
						     sample_frequency);
			    real = - ZPlane.get_real(-pz_array[i].frequency, 
						     pz_array[i].bandwidth,
						     sample_frequency);
			    invert(first_factor, zero_factor, real, imag);
			    
			    // multiply by the first order term
			    //
			    j = 1;
			    for (int k = 0; k <= num_order; k++, j++) {
				
				// multiply and copy the first order term by all other terms
				//
				working_array[2*j] = (first_factor[0] * num_coeffs[2*k])
				    - (first_factor[1] * num_coeffs[2*k+1]);
				working_array[2*j+1] = (first_factor[0] * num_coeffs[2*k+1])
				    - (first_factor[1] * num_coeffs[2*k]);
				
			    }
			    
			    // zero the zero order term
			    //
			    working_array[1] = working_array[0] = 0.0;
			    
			    // multiply and add by zero order terms
			    //
			    for (int k = 0; k <= num_order; k++) {
				working_array[2*k] += (zero_factor[0] * num_coeffs[2*k])
				    - (zero_factor[1] * num_coeffs[2*k+1]);
				working_array[2*k+1] += (zero_factor[0] * num_coeffs[2*k+1])
				    - (zero_factor[1] * num_coeffs[2*k]);
			    }
			    
			    // increase the order of the denominator
			    //
			    num_order++;
			    
			    // copy over the working array
			    //
			    for (int k = 0; k <= num_order; k++) {
				num_coeffs[2*k] = working_array[2*k];
				
				num_coeffs[2*k+1] = working_array[2*k+1];
				
				// check for the zero threshold
				//
				if (Math.abs(num_coeffs[2*k+1]) < THRESH_ZERO) {
				    num_coeffs[2*k+1] = 0.0;
				}
			    }  
			}
		    }
		}
		
		// increment counter
		//
		i++;
	    }
	}
	
	
	// multiply all zero terms by the dc gain
	//
	for (int i = 0; i <= num_order; i++) {
	    num_coeffs[2*i] *= dc_gain;
	    num_coeffs[2*i+1] *= dc_gain;
	}
	
	this.debug();
	
	// normalize the numerator and denominator by the coefficient of the
	// highest order term
	//
	// determine the scale factor to be pulled out
	//
	double num_factor[] = new double[2];
	double den_factor[] = new double[2];
	
	// assign the values to the various factors
	//
	num_factor[1] = num_coeffs[2*num_order+1];
	num_factor[0] = num_coeffs[2*num_order];
	den_factor[1] = den_coeffs[2*den_order+1];
	den_factor[0] = den_coeffs[2*den_order];
	scale_factor[1] = ((num_factor[1] * den_factor[0]) - 
			   (num_factor[0] * den_factor[1])) /
	    (den_factor[0] * den_factor[0] + 
	     den_factor[1] * den_factor[1]);
	
	scale_factor[0] = ((num_factor[0] * den_factor[0]) + 
			   (num_factor[1] * den_factor[1])) /
	    (den_factor[0] * den_factor[0] + 
	     den_factor[1] * den_factor[1]);
	
	// normalize to the highest order
	//
	for (int kk = 0; kk <= num_order; kk++) {
	    real = num_coeffs[2*kk];
	    imag = num_coeffs[2*kk+1];
	    num_coeffs[2*kk+1] = ((imag * num_factor[0]) - 
				  (real * num_factor[1])) /
		(num_factor[0] * num_factor[0] + 
		 num_factor[1] * num_factor[1]);
	    
	    num_coeffs[2*kk] = ((real * num_factor[0]) + 
				(imag * num_factor[1])) /
		(num_factor[0] * num_factor[0] + 
		 num_factor[1] * num_factor[1]);
	}
	
	for (int kk = 0; kk <= den_order; kk++) {
	    real = den_coeffs[2*kk];
	    imag = den_coeffs[2*kk+1];
	    den_coeffs[2*kk+1] = ((imag * den_factor[0]) - 
				  (real * den_factor[1])) /
		(den_factor[0] * den_factor[0] + 
		 den_factor[1] * den_factor[1]);
	    
	    den_coeffs[2*kk] = ((real * den_factor[0]) + 
				(imag * den_factor[1])) /
		(den_factor[0] * den_factor[0] + 
		 den_factor[1] * den_factor[1]);
	}
	
	this.debug();
	
	// exit gracefully
	//
	return true;
    }
    
    
    // method: set_design_type 
    //
    // parameters:
    //     integer new_type: (input) design type to change to
    //
    // this is a method to set the design type.  the design type will
    // be set to the value passed in to new_type
    //
    // returns: boolean
    //
    public boolean set_design_type(int new_type) {
	
	// set the type
	//
	design_type = new_type;
	
	// exit gracefully
	//
	return true;
    }
    
    
    // method: set_pz_array
    //
    // parameters:
    //     PoleZero pz_array_a[]: (input) array of poles and zeros
    // 
    // this method sets the pz_array to point to the input variable
    //
    // returns: boolean
    //
    public boolean set_pz_array(PoleZero pz_array_a[]) {
	
	// set the pointer
	//
	pz_array = pz_array_a;
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: set_dc_gain
    //
    // parameters:
    //     double new_dc_gain: (input) dc gain
    // 
    // this method sets the dc gain of the filter
    //
    // returns: boolean
    //
    public boolean set_dc_gain(double new_dc_gain) {
	
	// set the frequency in all of the sub-components
	//
	dc_gain = new_dc_gain;
	
	// update the screen
	//
	this.update_screen();
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: set_frequency
    //
    // parameters:
    //     double frequency: (input) frequency input
    // 
    // this method sets the sample frequency or maximum frequency
    //
    // returns: boolean
    //
    public boolean set_frequency(double frequency) {
	
	// set the frequency
	//
	sample_frequency = frequency;
	
	// cause the screen to update
	//
	this.update_screen();
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: update_screen
    //
    // parameters: (none)
    // 
    // this method sets the update_flag causing a repaint of the
    // canvas
    //
    // returns: boolean
    //
    public boolean update_screen() {
	
	// set the update_flag
	//
	update_flag = true;
	
	// exit gracefully
	//
	return true;
	
    }
    
    
    // method: run
    //
    // parameters: (none)
    // 
    // this method is the body of the thread.  it will update the
    // display area when necessary.  the necessity is based on the
    // condition of the update_flag
    //
    // returns: none
    //
    public void run() {
	
	// loop until the thread is terminated
	//
	while (sys_poly_thread.isAlive()) {
	    
	    // sleep for an appropriate number of milliseconds
	    //
	    try {
		sys_poly_thread.sleep(THREAD_SLEEP_TIME);
	    } catch (InterruptedException e) { }
	    
	    // update the screen if necessary
	    //
	    if (update_flag == true) {
		
		if (design_type == Constants.ANALOG) {
		    this.calc_analog();
		}
		else {
		    this.calc_digital();
		}
		
		// reset the update flag
		//
		update_flag = false;
	    }
	}
    }
    
} // end class SystemPolynomial
