/*
 * -------------------------------------------------------------------------
 *      $Id: CVPanel.java,v 1.3 2004/10/14 16:20:03 estewart Exp $
 * -------------------------------------------------------------------------
 *      Copyright (c) 2004 Visual Numerics Inc. All Rights Reserved.
 *
 *      This software is confidential information which is proprietary to
 *      and a trade secret of Visual Numerics, Inc.  Use, duplication or
 *      disclosure is subject to the terms of an appropriate license
 *      agreement.
 *
 *      VISUAL NUMERICS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
 *      SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
 *      BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 *      FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. VISUAL
 *      NUMERICS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 *      AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR
 *      ITS DERIVATIVES.
 *--------------------------------------------------------------------------
 */
/*
 *  CorrelationPicture
 *  Copyright (c), 2003, Gary H. McClelland
 *  Shows relationship between degree of scatter in a scattergram
 *  and the correlation coefficient.  A slider allows user to change
 *  the degree of scatter and observe the resulting changes in the
 *  correlation coefficient.
*/

/**
 *  @author     Gary H. McClelland
 *  @created    2 October 2003
*/

package com.imsl.demo.stats;
import com.imsl.chart.*;
import com.imsl.stat.Random;
import com.imsl.stat.*;
import java.awt.*;
import java.awt.event.*;
import java.text.DecimalFormat;
import javax.swing.*;
import javax.swing.event.*;

public class CVPanel extends JPanel {

   private JFrame parentFrame;
   private JPanelChart jPanelChart;
   private JPanel jPanel;
   private JLabel jLabel;
   private JSlider wtSlider;
   private JButton jButton;
   private Random rand;
   Chart chart;
   AxisXY axis;
   Data scat;

   double x[], y[];
   final int numberOfPoints = 30;

   public CVPanel(javax.swing.JFrame parent) {
      this.parentFrame = parent;

      initComponents();
      setPreferredSize(new java.awt.Dimension(
          parent.getSize().width, (int)(0.85*parent.getSize().height)));
      jPanelChart.setPreferredSize(new java.awt.Dimension(
         parent.getSize().width, (int)(0.66*parent.getSize().height)));

      //basic chart setup, layout of slider
      jPanelChart.setChart(new Chart());
      chart = jPanelChart.getChart();
      axis = new AxisXY(chart);
      //want fixed, unchanging range for both axes
      //allowing rescaling would make changes jumpy
      final double[] range = {0,100};
      final DecimalFormat df = new java.text.DecimalFormat("###");
      axis.getAxisX().setWindow(range);
      axis.getAxisY().setWindow(range);
      axis.getAxisX().setAutoscaleInput(Axis.AUTOSCALE_OFF);
      axis.getAxisY().setAutoscaleInput(Axis.AUTOSCALE_OFF);
      axis.getAxisX().getAxisTitle().setTitle("X Values");
      axis.getAxisY().getAxisTitle().setTitle("Y Values");
      axis.getAxisX().setTextFormat(df);
      axis.getAxisY().setTextFormat(df);

      //generate two random variables
      x = newSample(numberOfPoints,123321);
      y = newSample(numberOfPoints,321123);

      //correlation of the two random variables will be near zero
      //but we want it to be exactly zero
      x = adjustToZero(x,y);
      drawGraph(0.0);
   }

   private void initComponents() {
      jPanel = new JPanel();
      jPanelChart = new com.imsl.chart.JPanelChart();
      setLayout(new BorderLayout());

      jPanel.setLayout(new FlowLayout());
      jLabel = new JLabel("<html><center>Move the slider to adjust the "+
         "<br>correlation between -1 and +1.</center></html>");
      jPanel.add(jLabel);

      wtSlider = new JSlider(JSlider.HORIZONTAL,-500,500,0);
      wtSlider.setMajorTickSpacing(100);
      wtSlider.setMinorTickSpacing(25);
      wtSlider.setPaintTicks(true);
      wtSlider.addChangeListener(new ChangeListener() {
         public void stateChanged(ChangeEvent evt) {
            sliderChanged(evt);
         }
      });
      jPanel.add(wtSlider);
      add(jPanel, BorderLayout.NORTH);

      jPanelChart.setPreferredSize(new java.awt.Dimension(500, 500));
      add(jPanelChart, BorderLayout.CENTER);
   }

   // generates random normal variable of length k
   // rescaled to min = 0 and max = 100
   private double[] newSample(int k, int seed) {
      rand = new Random(seed);
      double v[] = new double[k];
      double min = 1000;
      double max = -1000;
      for(int i=0; i<=k-1; i++) {
         v[i] = rand.nextNormalAR();  //nextNormalAR();
         if(v[i] < min) min = v[i];
         if(v[i] > max) max = v[i];
      }
      for(int i=0; i<k; i++)
         v[i] = 100 * (v[i] - min)/(max - min);

      return v;
   }

   private double getCorrelation(double[] x, double[] y) {
      double mat[][] = new double[y.length][2];
      for(int i=0; i<= y.length-1; i++) {
         mat[i][0] = x[i];
         mat[i][1] = y[i];
      }

      Covariances cov = new Covariances(mat);
      double[][] cor = new double[2][2];
      try {
         cor = cov.compute(Covariances.STDEV_CORRELATION_MATRIX);
      } catch (Exception e) {}

      return cor[0][1];
   }

   //computes weighted average of two variables
   //weight ranges from -1 to +1
   private double[] applyWeight(double wt, double u[], double v[]) {
      double w[] = new double[v.length];
      for(int i=0; i<=v.length-1; i++)
         if(wt >= 0)
            w[i] = (1-wt)*v[i] + wt*u[i];
         else
            w[i] = (1+wt)*v[i] - wt*(100-u[i]);
      return w;
   }

   //finds weighted average of the two variables that produces a
   //zero correlation between that weighted average and one of the
   //variables.
   private double[] adjustToZero(double u[], double v[]) {
      double r = getCorrelation(v,u);

      double minwt = -1;
      double maxwt = 1;
      double wt = 0;
      double tol = .0001;
      int maxIter = 100;
      int iter = 0;

      while(Math.abs(r) > tol && iter < maxIter) {
         if(r > 0) {    //look down
            maxwt = wt;
            wt = (minwt + wt)/2;
         } else {       //look up
            minwt = wt;
            wt = (maxwt + wt)/2;
         }
         r = getCorrelation(v,applyWeight(wt,v,u));
         iter = iter + 1;
      }
      return applyWeight(wt,v,u);
   }

   //slider determines weight and hence correlation
   private void sliderChanged(ChangeEvent e) {
      JSlider source = (JSlider)e.getSource();
      //get new weight from slider
      double wt = (double)source.getValue()/500.;
      drawGraph(wt);
      repaint();
   }

   //draws scattergram with variable y and the x variable being
   //a weighted average of x and y.
   private void drawGraph(double wt) {
      double r = getCorrelation(applyWeight(wt,y,x),y);
      final DecimalFormat df = new DecimalFormat("0.000");
      try{
         chart.getChartTitle().setTitle("r = " + df.format(r));
         chart.getChartTitle().setFontSize(16);
         if(r < 0)
            chart.getChartTitle().setTextColor(Color.red);
         else
            chart.getChartTitle().setTextColor(Color.blue);


         //get rid of old data if it exists or it persists on graph
         if(scat != null) scat.remove();
         scat = new Data(axis, applyWeight(wt,y,x), y);
         scat.setDataType(Data.DATA_TYPE_MARKER);
         scat.setMarkerType(Data.MARKER_TYPE_FILLED_CIRCLE);
         scat.setMarkerColor(Color.black);
      }
      catch (Exception e) {}
   }
}