// $Id: Reappear.java,v 1.1 1999/05/19 17:52:25 schmidt Exp $

// Integers That Reappear in Their Squares
// Copyright (C) 1999 Eric A. Schmidt
// 
// This applet generates any N-digit integer that reappears in its square.
// Correctness was my main focus.  There is room for optimization.

import java.awt.*;
import java.awt.event.*;
import java.applet.*;
import java.math.*;

public class Reappear
	extends Applet
	implements ActionListener
{
	static BigInteger m_zero = new BigInteger( "0" );
	static BigInteger m_one = new BigInteger( "1" );
	static BigInteger m_five = new BigInteger( "5" );
	static BigInteger m_six = new BigInteger( "6" );
	static BigInteger m_ten = new BigInteger( "10" );

	String m_n;
	BigInteger  m_max = new BigInteger( "500" );

	TextArea m_messageArea = null;
	TextField m_nTextField = null;
	TextField m_5TextField = null;
	TextField m_5SquaredTextField = null;
	TextField m_6TextField = null;
	TextField m_6SquaredTextField = null;
	TextField m_sumTextField = null;

	public void init()
	{
		m_n = System.getProperty( "line.separator" );

		GridBagLayout gridBag = new GridBagLayout();
		setLayout( gridBag );

		GridBagConstraints c = new GridBagConstraints();

		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.BOTH;
		c.weightx = 1.0;
		c.weighty = 1.0;
		m_messageArea = new TextArea(
			"", 10, 50, TextArea.SCROLLBARS_VERTICAL_ONLY );
		m_messageArea.setEditable( false );
		gridBag.setConstraints( m_messageArea, c ); 
		add( m_messageArea );

		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.weighty = 0.0;
		m_nTextField = new TextField(
			"Enter integer N here and press ENTER.", 50 );
		m_nTextField.addActionListener( this );
		gridBag.setConstraints( m_nTextField, c ); 
		add( m_nTextField );

		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.weighty = 0.0;
		m_5TextField = new TextField(
			"The 5-based reapearing integer will appear here.", 50 );
		m_5TextField.setEditable( false );
		gridBag.setConstraints( m_5TextField, c ); 
		add( m_5TextField );

		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.weighty = 0.0;
		m_6TextField = new TextField(
			"The 6-based reapearing integer will appear here.", 50 );
		m_6TextField.setEditable( false );
		gridBag.setConstraints( m_6TextField, c ); 
		add( m_6TextField );

		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.weighty = 0.0;
		m_5SquaredTextField = new TextField(
			"The square of the 5-based integer will appear here.", 50 );
		m_5SquaredTextField.setEditable( false );
		gridBag.setConstraints( m_5SquaredTextField, c ); 
		add( m_5SquaredTextField );

		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.weighty = 0.0;
		m_6SquaredTextField = new TextField(
			"The square of the 6-based integer will appear here.", 50 );
		m_6SquaredTextField.setEditable( false );
		gridBag.setConstraints( m_6SquaredTextField, c ); 
		add( m_6SquaredTextField );

		c.gridwidth = GridBagConstraints.REMAINDER;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.weightx = 1.0;
		c.weighty = 0.0;
		m_sumTextField = new TextField(
			"The sum of the 5- and 6-based integers will appear here.", 50 );
		m_sumTextField.setEditable( false );
		gridBag.setConstraints( m_sumTextField, c ); 
		add( m_sumTextField );

		displayMessage(
			"Enter the number of digits, N, in the first field below " +
			"and press ENTER to calculate both N-digit integers " +
			"that reappear in their squares." );
		displayMessage(
			"Their respective squares and their sum will also be calculated." );
		displayMessage(
			"For example, input 3 for N.  Press ENTER and you will see " +
			"625 and 376 appear because they are the two 3-digit integers " +
			"that reappear in their squares." );
		displayMessage(
			"The larger the input, the longer the calculation time." );
	}

    public void actionPerformed( ActionEvent event )
	{
		TextField field = m_nTextField;
		String input = field.getText();
		BigInteger i = m_zero;

		if ( input.length() == 0 )
		{
			displayMessage(
				"Please enter number of digits, N, and press ENTER." );

			return;
		}

		try
		{
			i = new BigInteger( input );
		}
		catch ( NumberFormatException e )
		{
			displayMessage( "Please enter only numerical characters." );
			return;
		}

		if ( i.compareTo( m_max ) > 0 )
		{
			displayMessage(
				"For this applet, input must not exceed " +
				m_max.toString() + "." );
			return;
		}

		// A way to strip off any leading zeros.
		input = i.toString();
		field.setText( input );

		if ( i.compareTo( m_zero ) <= 0 )
		{
			displayMessage( "Input must be positive." );
			return;
		}

		displayMessage( "Calculating..." );

		BigInteger int5 = A( i.intValue() );
		BigInteger int6 = B( i.intValue() );
		m_5TextField.setText( "A = " + int5.toString() );
		m_6TextField.setText( "B = " + int6.toString() );

		BigInteger int5Squared = int5.multiply( int5 );
		BigInteger int6Squared = int6.multiply( int6 );
		m_5SquaredTextField.setText( "A squared = " + int5Squared.toString() );
		m_6SquaredTextField.setText( "B squared = " + int6Squared.toString() );

		BigInteger sum = int5.add( int6 );
		m_sumTextField.setText( "A + B = " + sum.toString() );

		displayMessage( "...done." );
	}

	public void displayMessage( String message )
	{
		m_messageArea.append( ":: " + message + m_n );
	}

	// int_pow( x, y ) == (BigInteger)pow( x, y ).
	private BigInteger int_pow( BigInteger x, int y )
	{
		BigInteger result = m_one;

		// Don't modify input.
		int y_ = y;

		// FIXME - This can be optimized by breaking y down into binary.
		while ( y_ > 0 )
		{
			--y_;
			result = result.multiply( x );
		}

		return result;
	}

	// Calculate A sub j.
	public BigInteger A( int j )
	{
		// Recursion end.
		if ( j == 1 ) return m_five;

		BigInteger A_ = A( j - 1 );
		return
			( A_.multiply( A_ ).subtract( A_ ) ).
			mod( int_pow( m_ten, j ) ).
			add( A_ );
	}

	// Calculate B sub j.
	public BigInteger B( int j )
	{
		// Recursion end.
		if ( j == 1 ) return m_six;

		BigInteger B_ = B( j - 1 );
		return
			( B_.subtract( B_.multiply( B_ ) ) ).
			mod( int_pow( m_ten, j ) ).
			add( B_ );
	}
}

