/*
||  ProthWeight.java
||
||  Copyright 1998, 2002 John F. Brennen
||  Any noncommercial use of this code is permitted with only one
||  restriction: you must include the copyright line above and
||  this clause.
||
||
||  If you make improvements to this code or port it in any way,
||   please consider sending me a copy of your work.
*/

import java . awt . * ;
import java . awt . event . * ;
import java . applet . * ;
import java . math . * ;
import java . util . * ;

public class ProthWeight extends Applet implements ActionListener , Runnable
{
   private TextArea process_log_text ;
   private TextArea worst_k_text ;
   private TextArea best_k_text ;
   private TextField enumber_field ;
   private TextField number_field ;
   private TextField step_field ;
   private Button compute_button ;
   private Button stop_button ;
   private Button clog_button ;
   private Button cbest_button ;
   private Button cworst_button ;
   private Vector work_queue ;
   private BigInteger BigOne ;
   private Thread worker ;
   private BigInteger bestk [ ] ;
   private BigInteger worstk [ ] ;
   private double bestk_weight [ ] ;
   private double worstk_weight [ ] ;
   private int bestk_len ;
   private int worstk_len ;
   private boolean stop_it ;

   private void add ( Component c , int wx , int wy , int x , int y , int w , int h )
   {
      GridBagConstraints gbc ;

      gbc = new GridBagConstraints ( ) ;

      gbc . weightx = wx ;
      gbc . weighty = wy ;
      gbc . gridx = x ;
      gbc . gridy = y ;
      gbc . gridwidth = w ;
      gbc . gridheight = h ;

      add ( c , gbc ) ;
   }

   public void init ( )
   {
      setLayout ( new GridBagLayout ( ) ) ;

      add ( new Label ( "For k =" ) , 100 , 100 , 0 , 0 , 1 , 1 ) ;
      number_field = new TextField ( 9 ) ;
      add ( number_field , 100 , 100 , 1 , 0 , 2 , 1 ) ;
      add ( new Label ( "to" ) , 100 , 100 , 3 , 0 , 1 , 1 ) ;
      enumber_field = new TextField ( 9 ) ;
      add ( enumber_field , 100 , 100 , 4 , 0 , 2 , 1 ) ;
      add ( new Label ( "step" ) , 100 , 100 , 6 , 0 , 1 , 1 ) ;
      step_field = new TextField ( 9 ) ;
      add ( step_field , 100 , 100 , 7 , 0 , 2 , 1 ) ;

      compute_button = new Button ( "Compute Weight" ) ;
      compute_button . addActionListener ( this ) ;
      add ( compute_button , 100 , 100 , 7 , 1 , 2 , 1 ) ;

      stop_button = new Button ( "Stop" ) ;
      stop_button . addActionListener ( this ) ;
      add ( stop_button , 100 , 100 , 7 , 16 , 2 , 1 ) ;

      clog_button = new Button ( "Clear Log" ) ;
      clog_button . addActionListener ( this ) ;
      add ( clog_button , 100 , 100 , 5 , 15 , 2 , 1 ) ;

      cbest_button = new Button ( "Clear Best" ) ;
      cbest_button . addActionListener ( this ) ;
      add ( cbest_button , 100 , 100 , 2 , 2 , 2 , 1 ) ;

      cworst_button = new Button ( "Clear Worst" ) ;
      cworst_button . addActionListener ( this ) ;
      add ( cworst_button , 100 , 100 , 7 , 2 , 2 , 1 ) ;

      add ( new Label ( "Process Log:" ) , 100 , 100 , 2 , 15 , 3 , 1 ) ;
      process_log_text = new TextArea ( 5 , 30 ) ;
      process_log_text . setEditable ( false ) ;
      add ( process_log_text , 100 , 100 , 2 , 16 , 5 , 5 ) ;

      add ( new Label ( "Best k" ) , 100 , 100 , 0 , 2 , 2 , 1 ) ;
      best_k_text = new TextArea ( 12 , 30 ) ;
      best_k_text . setEditable ( false ) ;
      add ( best_k_text , 100 , 100 , 0 , 3 , 4 , 12 ) ;

      add ( new Label ( "Worst k" ) , 100 , 100 , 5 , 2 , 2 , 1 ) ;
      worst_k_text = new TextArea ( 12 , 30 ) ;
      worst_k_text . setEditable ( false ) ;
      add ( worst_k_text , 100 , 100 , 5 , 3 , 4 , 12 ) ;

      work_queue = new Vector ( ) ;

      BigOne = BigInteger . valueOf ( 1 ) ;

      bestk = new BigInteger [ 10 ] ;
      worstk = new BigInteger [ 10 ] ;
      bestk_weight = new double [ 10 ] ;
      worstk_weight = new double [ 10 ] ;
      bestk_len = 0 ;
      worstk_len = 0 ;
   }

   void new_best_weight ( BigInteger k , double w )
   {
      BigInteger tk ;
      String mystr ;
      double tw ;
      int j ;

      for ( j = 0 ; j < bestk_len ; j ++ ) {
         if ( bestk [ j ] . compareTo ( k ) == 0 )
            return ;
      }

      if ( bestk_len < bestk . length ) {
         bestk [ bestk_len ] = k ;
         bestk_weight [ bestk_len ] = w ;
         ++ bestk_len ;
      } else if ( w > bestk_weight [ bestk_len - 1 ] ) {
         bestk [ bestk_len - 1 ] = k ;
         bestk_weight [ bestk_len - 1 ] = w ;
      } else {
         return ;
      }

      j = bestk_len - 1 ;

      while ( j > 0 && bestk_weight [ j ] > bestk_weight [ j - 1 ] ) {
         tk = bestk [ j ] ;
         tw = bestk_weight [ j ] ;
         bestk [ j ] = bestk [ j - 1 ] ;
         bestk_weight [ j ] = bestk_weight [ j - 1 ] ;
         bestk [ j - 1 ] = tk ;
         bestk_weight [ j - 1 ] = tw ;
         -- j ;
      }

      mystr = new String ( ) ;

      for ( j = 0 ; j < bestk_len ; j ++ ) {
         mystr = mystr + "k : " + bestk [ j ] + ", w : " + bestk_weight [ j ] + "\n" ;
      }

      best_k_text . setText ( mystr ) ;
   }

   void new_worst_weight ( BigInteger k , double w )
   {
      BigInteger tk ;
      String mystr ;
      double tw ;
      int j ;

      if ( w == 0 )
         return ;

      for ( j = 0 ; j < worstk_len ; j ++ ) {
         if ( worstk [ j ] . compareTo ( k ) == 0 )
            return ;
      }

      if ( worstk_len < worstk . length ) {
         worstk [ worstk_len ] = k ;
         worstk_weight [ worstk_len ] = w ;
         ++ worstk_len ;
      } else if ( w < worstk_weight [ worstk_len - 1 ] ) {
         worstk [ worstk_len - 1 ] = k ;
         worstk_weight [ worstk_len - 1 ] = w ;
      } else {
         return ;
      }

      j = worstk_len - 1 ;

      while ( j > 0 && worstk_weight [ j ] < worstk_weight [ j - 1 ] ) {
         tk = worstk [ j ] ;
         tw = worstk_weight [ j ] ;
         worstk [ j ] = worstk [ j - 1 ] ;
         worstk_weight [ j ] = worstk_weight [ j - 1 ] ;
         worstk [ j - 1 ] = tk ;
         worstk_weight [ j - 1 ] = tw ;
         -- j ;
      }

      mystr = new String ( ) ;

      for ( j = 0 ; j < worstk_len ; j ++ ) {
         mystr = mystr + "k : " + worstk [ j ] + ", w : " + worstk_weight [ j ] + "\n" ;
      }

      worst_k_text . setText ( mystr ) ;
   }

   void compute_weight ( BigInteger k )
   {
      BigInteger ptab [ ] ;
      BigInteger r ;
      int evalue [ ] ;
      int dvalue [ ] ;
      byte sva [ ] ;
      int ecnt ;
      int n ;
      int i ;
      int d ;

/*
      process_log_text . append ( "Working on " + k + "\n" ) ;
*/

      ptab = new BigInteger [ 512 ] ;

/*
      process_log_text . append ( "Computing powers ..." ) ;
*/
      for ( n = 0 ; n <= 511 ; n ++ ) {
         ptab [ n ] = k . shiftLeft ( n ) . add ( BigOne ) ;
      }
/*
      process_log_text . append ( " done\n" ) ;
*/

      evalue = new int [ 500 ] ;
      dvalue = new int [ 500 ] ;

      ecnt = 0 ;
      for ( d = 1 ; d <= 256 ; d ++ ) {
         for ( i = 0 ; i < d ; i ++ ) {
            for ( n = 0 ; n < ecnt ; n ++ ) {
               if ( d % dvalue [ n ] == 0 &&
                    i % dvalue [ n ] == evalue [ n ] ) {
                  break ;
               }
            }
            if ( n >= ecnt ) {
               r = ptab [ i ] . gcd ( ptab [ i + d ] ) ;
               if ( r . compareTo ( BigOne ) > 0 ) {
/*
                  process_log_text . append ( "Eliminate " + i + ", step " + d + "\n" ) ;
*/
                  evalue [ ecnt ] = i ;
                  dvalue [ ecnt ] = d ;
                  ecnt ++ ;
               }
            }
         }
      }

      sva = new byte [ 10000 ] ;

      for ( i = 0 ; i < 10000 ; i ++ )
         sva [ i ] = 1 ;

      for ( n = 0 ; n < ecnt ; n ++ ) {
         i = evalue [ n ] ;
         d = dvalue [ n ] ;
         while ( i < 10000 ) {
            sva [ i ] = 0 ;
            i += d ;
         }
      }

      n = 0 ;
      for ( i = 0 ; i < 10000 ; i ++ )
         n += sva [ i ] ;
      process_log_text . append ( "Weight of " + k + " is " + ( n / 1751.542 ) + "\n" ) ;

      new_best_weight ( k , n / 1751.542 ) ;
      new_worst_weight ( k , n / 1751.542 ) ;
   }

   void do_job ( String w , String x , String y )
   {
      BigInteger k ;
      BigInteger z ;
      BigInteger d ;

      try {
         k = new BigInteger ( w ) ;
      } catch ( NumberFormatException e ) {
         process_log_text . append ( "Not a number: " + w + "\n" ) ;
         return ;
      }

      try {
         z = new BigInteger ( x ) ;
      } catch ( NumberFormatException e ) {
         process_log_text . append ( "Not a number: " + x + "\n" ) ;
         return ;
      }

      try {
         d = new BigInteger ( y ) ;
      } catch ( NumberFormatException e ) {
         process_log_text . append ( "Not a number: " + y + "\n" ) ;
         return ;
      }

      if ( d . signum ( ) == 0 ) {
         process_log_text . append ( "Can't step by 0!\n" ) ;
         return ;
      }

      stop_it = false ;

      for ( ; ; ) {
         if ( stop_it ) {
            process_log_text . append ( "Cancelled by user\n" ) ;
            break ;
         }
         if ( k . compareTo ( z ) == d . signum ( ) )
            break ;
         compute_weight ( k ) ;
         k = k . add ( d ) ;
      }
   }

   public void run ( )
   {
      String z ;
      String w ;
      String x ;
      String y ;

      w = null ;
      x = null ;
      y = null ;

      while ( isActive ( ) ) {
         synchronized ( work_queue ) {
            while ( work_queue . isEmpty ( ) ) {
               try {
                  work_queue . wait ( ) ;
               } catch ( InterruptedException e ) {
               }
            }
            z = ( String ) work_queue . firstElement ( ) ;
            work_queue . removeElementAt ( 0 ) ;
            if ( z . equals ( "C" ) ) {
               w = ( String ) work_queue . firstElement ( ) ;
               work_queue . removeElementAt ( 0 ) ;
               x = ( String ) work_queue . firstElement ( ) ;
               work_queue . removeElementAt ( 0 ) ;
               y = ( String ) work_queue . firstElement ( ) ;
               work_queue . removeElementAt ( 0 ) ;
            }
         }
         if ( z . equals ( "C" ) ) {
            do_job ( w , x , y ) ;
         }
         if ( z . equals ( "L" ) ) {
            process_log_text . setText ( "" ) ;
         }
         if ( z . equals ( "B" ) ) {
            best_k_text . setText ( "" ) ;
            bestk_len = 0 ;
         }
         if ( z . equals ( "W" ) ) {
            worst_k_text . setText ( "" ) ;
            worstk_len = 0 ;
         }
      }
      worker = null ;
   }

   public void start ( )
   {
      if ( worker == null ) {
         worker = new Thread ( this ) ;
         worker . start ( ) ;
      }
   }

   public void actionPerformed ( ActionEvent evt )
   {
      if ( evt . getActionCommand ( ) . equals ( "Compute Weight" ) ) {
         synchronized ( work_queue ) {
            work_queue . addElement ( "C" ) ;
            work_queue . addElement ( number_field . getText ( ) . trim ( ) ) ;
            work_queue . addElement ( enumber_field . getText ( ) . trim ( ) ) ;
            work_queue . addElement ( step_field . getText ( ) . trim ( ) ) ;
            work_queue . notify ( ) ;
         }
      }
      if ( evt . getActionCommand ( ) . equals ( "Stop" ) ) {
         stop_it = true ;
      }
      if ( evt . getActionCommand ( ) . equals ( "Clear Log" ) ) {
         synchronized ( work_queue ) {
            work_queue . addElement ( "L" ) ;
            work_queue . notify ( ) ;
         }
      }
      if ( evt . getActionCommand ( ) . equals ( "Clear Best" ) ) {
         synchronized ( work_queue ) {
            work_queue . addElement ( "B" ) ;
            work_queue . notify ( ) ;
         }
      }
      if ( evt . getActionCommand ( ) . equals ( "Clear Worst" ) ) {
         synchronized ( work_queue ) {
            work_queue . addElement ( "W" ) ;
            work_queue . notify ( ) ;
         }
      }
   }
}
