/*
	The charged particle model
	Computational implementation in java v1.4

	Updates:
	v 1.1: 2 Dec. 2002, added tetrahedral cell find algorithm. (SH)
	v 1.2: 10 Dec. 2002, added n-simplex cell find algorithm. (SH)
	v 1.3: 8 Jan. 2003, added n-crospolytope cell find algorithm. (SH)
	V 1.4: 9 jun. 2004, added algorithm to optionally follow 1/R^(n-1) law instead of 1/R.
	1/R^(n-1) law was suggested by Harmen de Boer from Vlaardingen, (NL) and dramatically increases 
	convergence. 
	In multistable configurations 1/R^(n-1) law is less likely to show the most optimal structures however.
	Since research focuses on monostable configurations mainly this is not a major drawback.

	You are allowed to install and use the applet locally.
	Improving the code is encouraged, please inform me about any better coding solutions.
	Useful contibutions will be mentioned below.
	Please inform me when placing the code at another website or when material is being used for publication.
	In all cases, please leave the original copyright notice in tact.
	Copyright 2004,
	Symen H. Hovinga
	email: Symen@2on.com
	the Netherlands
*/


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Hovinga extends JApplet {
	private	double version=1.4;
	private	int pmax=1000;
	private	int nmax=100;
	static	int p=16;
	static	int n=5;
	private	long	count=0;
	private	double pi=Math.PI;
	private	double[][] po = new double[nmax][pmax];
	private	double[][] pn = new double[nmax][pmax];
	private	double[][] ro = new double[pmax][pmax];
	private double	d;
	private double	oldd=0;
	private double	oldd1=1;
	private	double[][] rn = new double[pmax][pmax];
	private	double	r = 0;
	private	double	r1 = 0;
	private	double	r2 = 0;
	private int[][]	pnt = new int[pmax][pmax];
	private int	pnt1;
	private int	totalNoOfLines;
	private int	noOfLines;
	private String[]	name1 = new String[nmax];
	private String[]	name2 = new String[nmax];
	private String[]	fullname1 = new String[nmax];
	private String[]	fullname2 = new String[nmax];
	private int[]	noOfSimplices = new int[nmax-1];
	private int[]	totalNoOfSimplices = new int[nmax-1];
	private int[]	totalNoOfCrossPolytopes = new int[nmax-1];
	private int[]	noOfCrossPolytopes = new int[nmax-1]; 
        private int xmax = 200;
        private int ymax = 200;
        private int xofs = 600;
        private int yofs = 250;
	private boolean begin=true;
	static boolean law=false;
	private String details;

   Display canvas;  // The drawing surface on which the Mandelbrot
                    // set is displayed.
                    
   JButton stopButton, startButton , contButton, incpButton , decpButton , incnButton , decnButton, lawButton;  // Computation will start when
                                     // Start button is pressed and will
                                     // continue until it finishes or the
                                     // user presses the "Stop" button.
public void init() 
{
          // Initialize the applet by creating the canvas
          // and buttons and adding them to the applet's
          // content pane.
      	setBackground(Color.gray);
      
      	canvas = new Display();
      	getContentPane().add(canvas, BorderLayout.CENTER);
      
      	JPanel bottom = new JPanel();
      	bottom.setBackground(Color.gray);

      	startButton = new JButton("Start");
      	startButton.addActionListener(canvas);
      	bottom.add(startButton);
      	startButton.setEnabled(true);

      	contButton = new JButton("Continue");
      	contButton.addActionListener(canvas);
      	bottom.add(contButton);
      	contButton.setEnabled(false);

      	stopButton = new JButton("Stop");
      	stopButton.addActionListener(canvas);
      	bottom.add(stopButton);
      	stopButton.setEnabled(false);

	decnButton = new JButton("-n");
      	decnButton.addActionListener(canvas);
      	bottom.add(decnButton);

	incnButton = new JButton("+n");
      	incnButton.addActionListener(canvas);
      	bottom.add(incnButton);

	decpButton = new JButton("-p");
      	decpButton.addActionListener(canvas);
      	bottom.add(decpButton);

	incpButton = new JButton("+p");
      	incpButton.addActionListener(canvas);
      	bottom.add(incpButton);

	lawButton = new JButton("Law");
      	lawButton.addActionListener(canvas);
      	bottom.add(lawButton);

      	getContentPane().add(bottom, BorderLayout.SOUTH);

      	p=Integer.parseInt(getParameter("p"));
	n=Integer.parseInt(getParameter("n"));
	if (p==0) p=4;
	if (n==0) n=3;
	if(getParameter("law").equals("true"))
		law=true;
	else
		law=false;
	initpoints();
	name1[1]="Edges";
	name1[2]="Triang.F";
	name1[3]="TetraH.C";
	fullname1[1]=" Line Segment";
	fullname1[2]="n Equilateral Triangle";
	fullname1[3]=" Tetrahedron";
	for(int i=4;i<nmax;i++)
	{
		name1[i]=i+"-S";
		fullname1[i]=" "+i+"-Simplex";
	}
	name2[2]="Square.F";
	name2[3]="OctaH.C";
	fullname2[2]=" Square";
	fullname2[3]="n Octahedron";
	for(int i=4;i<nmax;i++)
	{
		name2[i]=i+"-CrP";
		fullname2[i]=" "+i+"-Cross Polytope";
	}

	oldd=0;
	oldd1=0;
        //canvas.startRunning();

} // end init();
   
   
   public Insets getInsets() {
         // Leave space around the applet that will show in the
         // background color, gray.
      return new Insets(2,2,2,2);
   }
   
   public void stop() {
         // This method is called by the system when the applet
         // is about to be temporarily or permanently stopped.
         // To the canvas to stop the computation thread, if
         // it is running.
      canvas.stopRunning();
   }


   // --- The following nested class represents the drawing surface
   // --- of the applet and also does all the work.
   
   class Display extends JPanel implements ActionListener, Runnable {
   
      Image OSI;    // An off-screen images that holds the picture
                    //    of the Mandelbrot set.  This is copied onto
                    //    the drawing surface, if it exists.  It is
                    //    created by the computational thread.
                    
      Graphics OSG; // A graphics context for drawing on OSI.

      Thread runner;    // A thread to do the computation.
      boolean running;  // This is set to true when the thread is running.
   
      public void paintComponent(Graphics g) {
            // Called by the system to paint the drawing surface.
            // This copies the off-screen image onto the screen,
            // if the off-screen image exists.  If not, it just
            // fills the drawing surface with black.
         if (OSI == null) {
            g.setColor(Color.white);
            g.fillRect(0,0,getWidth(),getHeight());
         }
         else {
            g.drawImage(OSI,0,0,null);
         }
      }
      
public void actionPerformed(ActionEvent evt)
{
            // This will be called when the user clicks on
            // the "Start" or "Stop" button.  It responds
            // by starting or stopping the animation.
	if(begin==true)
	{
		begin=false;
		stopButton.setEnabled(true);
		startButton.setEnabled(false);
		contButton.setEnabled(false);
        	startRunning();
	}

        String command = evt.getActionCommand();
        if (command.equals("Start"))
	{
		initpoints();
		count=0;
		contButton.setEnabled(false);
        	startRunning();
	}
        if (command.equals("Continue"))
	{
		contButton.setEnabled(false);
		oldd=0;
		oldd1=1;
        	startRunning();
	}
        if (command.equals("Stop"))
	{
            	stopRunning();
		contButton.setEnabled(true);
	}
        if (command.equals("+p"))
	{
            	p++;
		if(p==pmax-1)
		{
			incpButton.setEnabled(false);
		}
		for(int j=0;j<n;j++)
		{
			pn[j][p-1]=Math.random();
		}
		correct();
		decpButton.setEnabled(true);
		count=0;
		startRunning();
	}
        if (command.equals("-p"))
	{
            	p--;
		if(p==2)
		{
			decpButton.setEnabled(false);
		}
		incpButton.setEnabled(true);
		count=0;
		startRunning();
	}
        if (command.equals("+n"))
	{
            	n++;
		if(n==nmax-1)
		{
			incnButton.setEnabled(false);
		}
		for(int i=0;i<p;i++)
		{
			pn[n-1][i]=Math.random();
		}
		correct();
		decnButton.setEnabled(true);
		count=0;
		startRunning();
	}
        if (command.equals("-n"))
	{
            	n--;
		if(n==2)
		{
			decnButton.setEnabled(false);
		}
		incnButton.setEnabled(true);
		count=0;
		startRunning();
	}
	if (command.equals("Law"))
	{
		if(law)
			law=false;
		else
			law=true;
		lawButton.setEnabled(true);

	}
}
      
      void startRunning() {
           // A simple method that starts the computational thread,
           // unless it is already running.  (This should be
           // impossible since this method is only called when
           // the user clicks the "Start" button, and that button
           // is disabled when the thread is running.)
         if (running)
            return;
         runner = new Thread(this);
              // Creates a thread that will execute the run()
              // method in this Display class.
         running = true;
         runner.start();
      }
      
      void stopRunning() {
           // A simple method that is called to stop the computational
           // thread.  This is done by setting the value of the
           // variable, running.  The thread checks this value
           // regularly and will terminate when running becomes false.
         running = false;
      }
      
      
      Runnable painter = new Runnable() {
               // A Runnable object whose job is to paint a
               // square onto the off-screen canvas, and then
               // copy that square onto the screen.  It will do
               // this when its run method is called.  The data
               // for the square are given by the preceding four
               // variables.
            public void run() {
	boolean lineSymmetry=true;
	int prevNoOfLines=0;
	double prevDistance=0;
	String lawString;
	OSG.setColor(Color.white);
	OSG.fillRect(0,0,getWidth(),getHeight());
	totalNoOfLines=0;
	for(int i=0;i<p;i++)
	{
		for(int l=0;l<p;l++)	// Assign no's to all points
		{
			pnt[l][i]=l;
		}
		for(int k=0;k<p;k++)	// Sort shortest mutual distances
		{
			for(int l=0;l<(p-1);l++)
			{
				if(ro[i][l]>ro[i][l+1] )
				{
					r=ro[i][l];
					ro[i][l]=ro[i][l+1];
					ro[i][l+1]=r;

					pnt1=pnt[l][i];
					pnt[l][i]=pnt[l+1][i];
					pnt[l+1][i]=pnt1;

				}
			}
		}
		int l=1;		// Find line segments of equal length
		r=ro[i][l];
		String points="Point: "+(i+1)+" : ";
		noOfLines=1;
		for (l=2;l<p;l++)
		{
			if( ( Math.abs(ro[i][l]-r)/r )<.00001 )
			{
				noOfLines++;
			}
			else
			{
				break;
			}
		}
		totalNoOfLines+=noOfLines;
	   	for(int k=1;k<=noOfLines;k++)	// Sort point numbers of line segments of equal length
		{
			for(int j=1;j<=noOfLines-1;j++)
			{
				if(pnt[j][i]>pnt[j+1][i])
				{
					pnt1=pnt[j+1][i];
					pnt[j+1][i]=pnt[j][i];
					pnt[j][i]=pnt1;
				}
			}
		}
		for(int k=1;k<=noOfLines;k++) // Draw lines and points
		{
			points+=(pnt[k][i]+1);
			if(k!=noOfLines)
				points+=",";
			int x1=(int) (xofs+xmax*pn[0][i]);
			int y1=(int) (yofs+ymax*pn[1][i]);
			int x2=(int) (xofs+xmax*pn[0][pnt[k][i]]);
			int y2=(int) (yofs+ymax*pn[1][pnt[k][i]]);
			OSG.setColor(Color.red);
			OSG.drawLine(x1,y1,x2,y2);
			OSG.setColor(Color.blue);
			OSG.drawString(""+(pnt[k][i]+1),x2,y2);
		}
		if( (prevNoOfLines!=0 && noOfLines!=prevNoOfLines) || (noOfLines<2) || (prevDistance!=0 && Math.abs((prevDistance-r)/r)>=.00001) )
		{
			lineSymmetry=false;
		}
		prevDistance=r;
		prevNoOfLines=noOfLines;
		int xc=10;
		int yc=i*20+60;
		if(i<31)	// Display equal distances
		{
			OSG.setColor(Color.blue);
			OSG.drawString(points,xc,yc);
		}
	}
	for(int i=0;i<p;i++)
	{
		rn[i][i]=0;
	}
	if (oldd==oldd1)
	{
		running=false;
	}
	details = ", "+name1[1] + ": "+totalNoOfLines/2;
	if (lineSymmetry==true && d!=oldd && oldd!=oldd1)
	{
		details+= ". Found line symmetry, will search for simplices and cross-polytopes when fully stable...";
	}
	if (lineSymmetry==true && d==oldd && oldd!=oldd1)
	{
		details+= ". Structure is stable, searching for simplices and cross-polytopes...";
		if (p*n>100)
			details+=" Depending on the structure this might take some time!";
	}
	oldd1=oldd;
	oldd=d;
	if ( lineSymmetry==true && running==false)
	{
		findSimplices();
		findCrossPolytopes();
		int euler=p-totalNoOfSimplices[1]/2;
		int k=n;
		//if ( (n>4)&&((n+1)!=p)&&((2*n)!=p)  )
		//	k=n-1;		// Temporarely solution: Overcome counting bug with n-1 simplex do not show it for non-regular polytopes where n>4
		double h1=2;
		double h2=1;
		int totalNoOfCells;
		for(int i=n-1;i>1;i--)
		{
			if((totalNoOfSimplices[i]!=0)||(totalNoOfCrossPolytopes[i]!=0))
				break;
			k=i;
		}
		for(int i=2;i<k;i++)
		{
			h1*=(i+1);
			h2*=(i*2);
			totalNoOfSimplices[i]/=h1;
			totalNoOfCrossPolytopes[i]/=h2;
			totalNoOfCells=totalNoOfSimplices[i];
			details += ", "+name1[i] + ": "+totalNoOfSimplices[i];
			if(totalNoOfCrossPolytopes[i]!=0)
			{
				totalNoOfCells+=totalNoOfCrossPolytopes[i];
				details += ", "+name2[i] + ": "+totalNoOfCrossPolytopes[i];
			}
			euler+=totalNoOfCells*Math.pow((double) -1,(double) i);
		}
		euler+=Math.pow((double) -1,(double) n)-1;
		totalNoOfSimplices[n]/=h1*(n+1);
		totalNoOfCrossPolytopes[n]/=h2*n*2;
		/*if( lineSymmetry==true)
			details+=", with line symmetry";
		else
			details+=", has no line symmetry";*/
		if( ( totalNoOfSimplices[n]==1 || totalNoOfCrossPolytopes[n]==1) )//&& euler==0 )
		{
			if (totalNoOfSimplices[n]==1)
				details+=". Structure forms a"+fullname1[n]+".";
			else
				details+=". Structure forms a"+fullname2[n]+".";
		}
		else
		{
			if(p<=n && ( totalNoOfSimplices[p-1]==1 || totalNoOfCrossPolytopes[p-1]==1) )
			{
				if (totalNoOfSimplices[p-1]==1)
					details+=". Structure forms a"+fullname1[p-1]+".";
				else
					details+=". Structure forms a"+fullname2[p-1]+".";
			}
			else
			{
				if(euler==0)
					details+=", Euler = True";
				else
					details+=", Euler = "+euler+" missing";
			}
		}
	}
	/* Auto search for regular and semi-regular convex polytopes
	if (lineSymmetry==false && running==false)
	{
            	p++;
		for(int j=0;j<n;j++)
		{
			pn[j][p-1]=Math.random();
		}
            	p++;
		for(int j=0;j<n;j++)
		{
			pn[j][p-1]=Math.random();
		}
		correct();
		if(p<pmax-1)
		{
			running=true;
		}
		count=0;
	}
	*/
        OSG.setColor(Color.black);
	if(law)
		lawString="Law 1/R^"+(n-1)+", ";
	else
		lawString="Law 1/R, ";
	OSG.drawString("CP v"+version+" Dimensions: "+n+" Points: "+p+details,10,20);
	OSG.drawString(lawString+"Iterations: "+count+" Distance point: "+(pnt[0][p-1]+1)+"-"+(pnt[1][p-1]+1)+":"+rn[pnt[0][p-1]][pnt[1][p-1]]+" Total distance: "+d,10,40);
	paintImmediately(0,0,getWidth(),getHeight());
      }
  };
      

  public void run() 
   {
            // This is the run method that is executed by the
            // computational thread.  It performs the graphical output
         startButton.setEnabled(false);  // Disable "Start" button
         stopButton.setEnabled(true);    //    and enable "Stop" button
                                         //    while thread is running.

         int width = getWidth();   // Current size of this canvas.
         int height = getHeight();

         OSI = createImage(width,height);
             // Create the off-screen image where the picture will
             // be stored, and fill it with black to start.
         OSG = OSI.getGraphics();
         OSG.setColor(Color.white);
         OSG.fillRect(0,0,width,height);
         
	while(running==true)
	{
		for(int l=0;l<1000;l++)
		{
			d=0;			// Actual algorithm
			for(int i=1;i<p;i++)		// for each point..
			{
				for(int k=0;k<i;k++)	// in relation to each other point..
				{
					r=1;
					for(int j=0;j<n;j++)  // in each dimension..
					{
						r-=po[j][i]*po[j][k];	// calculate the resulting distance
					}
					r1=Math.sqrt(2*r);	// r1 = mutual distance
					rn[i][k]=r1;	// Save mutual distance for later use
					rn[k][i]=r1;
					d+=r1;		// Calculate total distance
					double r3=r1;
					if(law)
					{
						for(int j=1;j<n-1;j++) // in enabled follow 1/R^(n-1) law (else 1/R)
							r3*=r1;
					}
					else
							r3*=0.0001;
					for(int j=0;j<n;j++)
					{
						r2=(po[j][i]-po[j][k])/r3;   //n[i][k];  // move the 2 points to their new location
						pn[j][i]+=r2;
						pn[j][k]-=r2;
					}
				}
			}
			correct();	// Correct movements into spere with radius 1 and update them
			count++;

		}
		try
		{
			SwingUtilities.invokeAndWait(painter);	// Display on screen
        	}
	        catch (Exception e) 
		{
		}
		Thread.yield();  // Give other threads a chance to run.
	}
	startButton.setEnabled(true);  // Reset states of buttons.
	stopButton.setEnabled(false);
	contButton.setEnabled(true);

   } // end run()

	public void	findCrossPolytopes()// n D-Cross-Polytope find alghorithm
	{
		for(int i=1;i<n;i++)
		{
			totalNoOfCrossPolytopes[i]=0;
			noOfCrossPolytopes[i]=0;
		}
		for(int l=0;l<p;l++)
		{
			for(int i=1;i<=noOfLines-1;i++)
			{
				for(int j=i+1;j<=noOfLines;j++)
				{
					r=rn[pnt[i][l]][pnt[j][l]]/rn[l][pnt[i][l]];
					if( Math.abs(r-1.414213562373)<0.000000000001)	//search for all triangles with a 90 degrees angle
					{
						int[] point={l,pnt[i][l],pnt[j][l]};
						findCrossPolytope(point);
					}
				}
			}
		}	
	}

	public void	findCrossPolytope(int[] point)  // point.length Cross-Polytope search alghorithm
	{
		for(int l=0;l<p;l++)  // evaluate all points connected to point l
		{
			boolean unequal=true;
			if(point.length%2==1)
				r=rn[point[0]][l]/rn[point[1]][l];
			if( Math.abs(r-1.414213562373)>=0.000000000001 && point.length%2==1)
			{
				unequal=false;
			}
			else
			{
				for(int m=0;m<point.length;m++)
				{
					if(l==point[m])
					{
						unequal=false;
						break;
					}
				}
			}
			if(unequal==true)	// if point l does not equal any of the points searched and angle is right.
			{
				for(int k=1;k<point.length;k++)  // Sort the points of (point.length-1)-cross polytope lowest first
				{
					if(point.length%2==0) // if no. of points is even, sort the points
					{
						for(int i=0;i<point.length-1;i++)
						{
							if(point[i]>point[i+1])
							{
								pnt1=point[i+1];
								point[i+1]=point[i];
								point[i]=pnt1;
							}
						}
					}
				}
				searchCrossPolytopePoints(0,1,l,point);	// count the matches of array point with all connected points of l
			}

		}
	}
	public void searchCrossPolytopePoints(int k,int j,int l,int[] point)
	{
		int m=0;
		if(point.length%2==1) // if no. of points is odd, don't compare first point
		{
			m=1;
		}
		for(int i=k+1;i<=noOfLines-(point.length-m)+j;i++)
		{
			boolean equal=false;
			if(pnt[i][l]==point[j-1+m])
			{
				equal=true;
			}
			if(equal==true)
			{				// new point found which is at equal distance of (point.length/2)-D-Cross-Polytope
				if(j==(point.length-m))
				{
					if(m==1)
					{
						r=rn[point[0]][l]/rn[point[1]][l];
						if( Math.abs(r-1.414213562373)>=0.000000000001 )
							return;
						noOfCrossPolytopes[point.length/2+1]++;
						totalNoOfCrossPolytopes[point.length/2+1]++;
						totalNoOfCrossPolytopes[point.length/2]=0; // Erase all intersecting cross polytope count of 1 dimension less
					}
					if(point.length==2*n-1) //return if point.lenth in the recursive search reached (n-1)-Cross Polytope search
					{
						return;
					}
					else	// point.length/2-Crosspolytope found now find Cross-polytopes of the order (point.lenth+1)/2 Dim. when m=0, else find other half of the current Cross-polytope
					{
						int[]	point1 = new int[point.length+1];
						for(int o=1;o<=point.length;o++)
						{
							point1[o]=point[o-1];
						}
						point1[0]=l;
						findCrossPolytope(point1);
						return;
					}
				}
				else
				{
					searchCrossPolytopePoints(i,j+1,l,point);	// compare other points
					return;
				}
			}
		}
	}


	public void 	findSimplices() // Triangular face and n D-simplices find alghorithm
	{
		for(int i=1;i<n;i++)
		{
			totalNoOfSimplices[i]=0;
			noOfSimplices[i]=0;
		}
		for(int l=0;l<p;l++)  
		{
			for(int i=1;i<=noOfLines;i++)
			{
				noOfSimplices[1]++;
				//if( (noOfSimplices[1]%2)==0)
				{
					totalNoOfSimplices[1]++;
					int[] point={l,pnt[i][l]};
					findSimplex(point);
				}
			}

		}
	}

	public void	findSimplex(int[] point)  // point.length simplex search alghorithm
	{
		for(int l=0;l<p;l++)  // evaluate all points connected to point l
		{
			boolean unequal=true;
			for(int m=0;m<point.length;m++)
			{
				if(l==point[m])
				{
					unequal=false;
					break;
				}
			}
			if(unequal==true)	// if point l does not equal any of the points searched.
			{
				for(int k=0;k<point.length;k++)  // Sort the points of (point.length-1)-simplex lowest first
				{
					for(int i=0;i<point.length-1;i++)
					{
						if(point[i]>point[i+1])
						{
							pnt1=point[i+1];
							point[i+1]=point[i];
							point[i]=pnt1;
						}
					}
				}
				searchSimplexPoints(0,1,l,point);	// count the matches of array point with all connected points of l
			}

		}
	}

	public void searchSimplexPoints(int k,int j,int l,int[] point)
	{
		for(int i=k+1;i<=noOfLines-point.length+j;i++)
		{
			if(pnt[i][l]==point[j-1]) 
			{				// new point found which is at equal distance of (point.length-1)-simplex
				if(j==point.length)
				{
					noOfSimplices[point.length]++;
					//if(noOfFaces[point.length]%(point.length+1)!=0) // To save time, count only every (point.length+1)th (point.length-1)-simplex except for n-1 which are counted all.
					//	return;
					totalNoOfSimplices[point.length]++;
					if(point.length==n) //return if point.lenth in the recursive search reached n-simplex search
					{
						return;
					}
					else	// point.length-simplex found now find simplices of the order point.lenth+1
					{
						int[]	point1 = new int[point.length+1];
						for(int o=1;o<=point.length;o++)
						{
							point1[o]=point[o-1];
						}
						point1[0]=l;
						findSimplex(point1);
						return;
					}
				}
				else
				{
					searchSimplexPoints(i,j+1,l,point);
					return;
				}
			}
		}
	}

} // end nested class Display


public void initpoints()
{
	for(int i=0;i<p;i++)
	{
		r=0;
		for(int j=0;j<n;j++)
		{
			pn[j][i]=Math.random();
			r+=pn[j][i]*pn[j][i];
		}
		for(int j=0;j<n;j++)
		{
			pn[j][i]/=Math.sqrt(r);
			po[j][i]=pn[j][i];
		}
	}
	correct();
	oldd=0;
	oldd1=1;
}

public void correct()
{
	for(int i=0;i<p;i++)	// Correct data within radius 1 sphere and update model
	{
		r=0;
		for(int j=0;j<n;j++)
		{
			r+=pn[j][i]*pn[j][i];
		}
		r=Math.sqrt(r);
		for(int j=0;j<n;j++)
		{
			pn[j][i]/=r;
			po[j][i]=pn[j][i];
		}
		for(int j=0;j<p;j++)
		{
			ro[i][j]=rn[i][j];
		}
	}
}


} // end class Hovinga

