/**
 * This class calculates parameters b^k_j, \rho^k_j, \theta^k_j
 * that are used in Tuc07a: arXiv:0706.0479 (version 2).
 * For more guidance, see, in particular, the Appendix of that paper that
 * is entitled: Special Case: d_\lambda = \sqrt{2}g for all \lambda
 */
public class Nautilus {
	private double _strength;
	private int _numCompartments;
	private int _deltaLam;
	private double[] _b;
	private double[] _rhos;
	private double[] _thetas;
	
/**
 *
 */
	private void calculateBs(){
		
		double strenghtPrime = _deltaLam==0?_strength:
			_strength*( 1 + _strength*_strength/6);

		_b = new double[_numCompartments + 1];
		double rt2 = Math.sqrt(2);
		if(_numCompartments==0){
			_b[0] = strenghtPrime*rt2;
			return;
		}

    	int k, row, col, index;
		///////////////////////////	calling clapack
		char compz_j = 'I';
    	int n_j = _numCompartments + 1;
    	int dimSq = n_j*n_j;
    	double[] d_j = new double[n_j];
    	double[] e_j = new double[n_j - 1];
    	for(k=0; k< n_j - 1; k++){
    		d_j[k] = 0;
    		e_j[k] = 1;   		
    	}
    	if(n_j - 1 >= 0){
    		d_j[n_j - 1] = 0;
    	}
    	
    	
    	double[] z_j = new double[dimSq];
    	for(index = 0; index< dimSq; index++){
    		row = index%n_j;
    		col = index/n_j;
    		if(row == col){
    			z_j[index] = 1;
    		}else{
    			z_j[index] = 0;
    		}
    	}
    	
    	int ldz_j = n_j;
    	double[] work_j = new double[Math.max(1,2*n_j - 2)];
    	int[] info_j = {0};
    	
    	NativeLinAlg.dsteqr(	compz_j,
    			n_j,
    			d_j,
    			e_j,
    			z_j,
    			ldz_j,
    			work_j,
    			info_j);    	
    	Throw.If(info_j[0] != 0, "clapack error");
   	
    	//eigenvectors are columns of omega
    	double[][] omega = new double[n_j][n_j];
     	for(row=0; row < n_j; row++){
    		for(col=0; col < n_j; col++){
    			omega[row][col] = z_j[col*n_j + row];
    		}
    	}
     	
/*     	for(col=0; col < n_j; col++){
     		System.out.println("col, eigenvalue " +
 				col + ", " +
				d_j[col]);
    		for(row=0; row < n_j; row++){
    			System.out.println("row, col, omega " +
    					row + ", " +
    					col + ", " +
    					omega[row][col]);
    		}
    	}
*/
     	///////////////////////////
     	
     	//the _b are defined as the last row of
     	//the matrix sinc(A/2)exp(iA/2),
     	//where A is the matrix we just diagonalized
     	
     	//The last _b component is always real
		boolean isReal = true;
		for(index=_numCompartments; index >= 0; index--){
     		//k sums over eigenvectors
     		_b[index] = 0;
 			if(isReal==true){
 				for(k=0; k < n_j; k++){
	 				_b[index] += strenghtPrime*rt2
	 					*Globals.sinc(d_j[k]*_strength/rt2)
	 					*Math.cos(d_j[k]*_strength/rt2)//cosine
	 					*omega[n_j-1][k]*omega[index][k];
	 			}
 			}else{
 				for(k=0; k < n_j; k++){
	 				_b[index] += strenghtPrime*rt2
	 					*Globals.sinc(d_j[k]*_strength/rt2)
	 					*Math.sin(d_j[k]*_strength/rt2)//sine
	 					*omega[n_j-1][k]*omega[index][k];
	 			}
 			}
     		
     		//flip isReal
     		isReal = (!isReal);
     	}
     	
     	
	}
/**
 *
 */
	public Nautilus(
		double strength,
		int numCompartments,
		int deltaLam
	){
		_strength = strength;
		_numCompartments = numCompartments;
		_deltaLam = deltaLam;
		Throw.If(numCompartments<0, 
			"nautilus must have >=0 compartments");
		_rhos = new double [_numCompartments + 1];
		_thetas = new double [_numCompartments + 1];
		calculateBs();
		
		_rhos[0] = _b[0];
		_thetas[0] = 0.0;//not used
		
		int parity = numCompartments%2;		
		int sign = parity==0? -1:1;
		for(int j=1; j<=numCompartments; j++){
			_rhos[j] = Math.sqrt(
				Math.pow(_rhos[j-1], 2) + Math.pow(_b[j],2) 
			);
			_thetas[j] = Math.atan2(_rhos[j-1]*sign, _b[j]);
			//switch sign
			sign *= (-1);
		}
	}
/**
 * 
 */
	public double getB(int index){
		return _b[index];
	}
/**
 * 
 */
	public double getRho(int index){
		return _rhos[index];
	}
/**
 * 
 */
	public double getTheta(int index){
		return _thetas[index];
	}
/**
 * 
 */
	public  static void main(String[] args){
    	double strength = 1/Math.sqrt(2);
    	int numCompartments;
    	for(numCompartments=0; numCompartments<3; numCompartments++){
	    	Nautilus naut = new Nautilus(strength, numCompartments, 0);
	    	System.out.println("numCompartments " + numCompartments);
	    	for(int k=0; k<=numCompartments; k++){
	    		System.out.println("k, b, rho/2rho(degs), theta/2theta(degs) " +
	    				k + ", " +
	    				naut.getB(k) + ", " +
	    				naut.getRho(k) + "/" +
	    				naut.getRho(k)*360/Math.PI + ", " +
	    				naut.getTheta(k) + "/" +
	    				naut.getTheta(k)*360/Math.PI);
	    	}
    	}
    }
	
	
}
