/* ******************************************************************************** * This applet implements the Cable TV Consumer Preference model presented * * in Example 2 on p.91 of Larson and Edwards' book, Elementary Linear Algebra, * * 4th Ed., Houghton-Mifflin (2000) * * * * Author : Tom Kimber * * Date : 2 January 2001 * ******************************************************************************** */ import java.awt.* ; import java.applet.* ; import java.awt.event.* ; import java.util.* ; public class tvcpmApplet extends Applet implements ActionListener { // --------- Variables ------------ Matrix mat = new Matrix( 3 , 3 , 0.0 ) ; Matrix vec = new Matrix( 3 , 1 , 0.0 ) ; int intNumYears ; // --------- GUI elements --------- MatrixPanel pnlMatrix = new MatrixPanel( "Transition Matrix:" , mat ) ; MatrixPanel pnlVector = new MatrixPanel( "Initial States:" , vec ) ; LabelTextBox txtNumYears = new LabelTextBox( "Number of Years:" ) ; Label lblActions = new Label( "Actions:" , Label.CENTER ) ; Button bttnCalculate = new Button( "Calculate" ) ; Button bttnDefaults = new Button( "Restore Default Inputs" ) ; Button bttnClear = new Button( "Clear Output" ) ; Label lblOutput = new Label( "Model Output:" , Label.CENTER ) ; TextArea txtOutput = new TextArea( "Year | Company A | Company B | No cable \n-----|-----------|-----------|---------" , 20 , 40 ) ; public void init( ) { // First layout GUI: setBackground( Color.cyan ) ; setLayout( new BorderLayout( ) ) ; Panel pnlNorth = new Panel( ) ; pnlNorth.setLayout( new GridLayout( 1 , 3 ) ) ; pnlNorth.add( pnlMatrix ) ; pnlNorth.add( pnlVector ) ; pnlNorth.add( txtNumYears ) ; add( BorderLayout.NORTH , pnlNorth ) ; Panel pnlWest = new Panel( ) ; Panel pnlButtons = new Panel( ) ; pnlButtons.setLayout( new GridLayout( 4 , 1 ) ) ; pnlButtons.setFont( new Font( "Monospaced" , Font.PLAIN , 12 ) ) ; pnlButtons.add( lblActions ) ; Panel pnlCalculate = new Panel( ) ; pnlCalculate.add( bttnCalculate ) ; pnlButtons.add( pnlCalculate ) ; Panel pnlDefaults = new Panel( ) ; pnlDefaults.add( bttnDefaults ) ; pnlButtons.add( pnlDefaults ) ; Panel pnlClear = new Panel( ) ; pnlClear.add( bttnClear ) ; pnlButtons.add( pnlClear ) ; pnlWest.add( pnlButtons ) ; add( BorderLayout.WEST , pnlWest ) ; Panel pnlCenter = new Panel( ) ; pnlCenter.setLayout( new BorderLayout( ) ) ; pnlCenter.setFont( new Font( "Monospaced" , Font.PLAIN , 12 ) ) ; txtOutput.setBackground( Color.white ) ; pnlCenter.add( BorderLayout.NORTH , lblOutput ) ; Panel pnlOutputText = new Panel( ) ; pnlOutputText.add( txtOutput ) ; pnlCenter.add( BorderLayout.CENTER , pnlOutputText ) ; add( BorderLayout.CENTER , pnlCenter ) ; // now set initial input values ... RestoreDefaultInputs( ) ; // ... and register the buttons to listen for clicks bttnCalculate.addActionListener( this ) ; bttnDefaults.addActionListener( this ) ; bttnClear.addActionListener( this ) ; } public void actionPerformed( ActionEvent e ) { Object src = e.getSource( ) ; if ( src == bttnDefaults ) { RestoreDefaultInputs( ) ; } if ( src == bttnCalculate ) { Calculate( ) ; } if ( src == bttnClear ) { ClearOutput( ) ; } } private void RestoreDefaultInputs( ) { mat.setEntry( 0 , 0 , .70 ) ; mat.setEntry( 0 , 1 , .15 ) ; mat.setEntry( 0 , 2 , .15 ) ; mat.setEntry( 1 , 0 , .20 ) ; mat.setEntry( 1 , 1 , .80 ) ; mat.setEntry( 1 , 2 , .15 ) ; mat.setEntry( 2 , 0 , .10 ) ; mat.setEntry( 2 , 1 , .05 ) ; mat.setEntry( 2 , 2 , .70 ) ; vec.setEntry( 0 , 0 , 15000 ) ; vec.setEntry( 1 , 0 , 20000 ) ; vec.setEntry( 2 , 0 , 65000 ) ; pnlMatrix.updatePanel( mat ) ; for ( int i = 0 ; i < vec.getNumRows( ) ; i++ ) { // display state vector as ints, since these are numbers of people pnlVector.setValue( i , 0 , ( int ) vec.getEntry( i , 0 ) ) ; } txtNumYears.setText( "15" ) ; } private void Calculate( ) { mat.updateFromPanel( pnlMatrix ) ; // get values from panel into matrix vec.updateFromPanel( pnlVector ) ; // get values from panel into vector intNumYears = ( int ) txtNumYears.getValue( ) ; // get number of years from text box if ( isInputOK( ) == false ) { displayInputErrorMessage( txtOutput ) ; } else { ClearOutput( ) ; DisplayOutputLine( 0 ) ; // display year 0 (initial) system state for ( int i = 1 ; i <= intNumYears ; i++ ) { // loop to calculate and display // system state for each year CalculateNextState( ) ; DisplayOutputLine( i ) ; } } } private boolean isInputOK( ) { int n , m ; n = mat.getNumRows( ) ; m = mat.getNumCols( ) ; // check that entries are positive for ( int i = 0 ; i < n ; i++ ) { for ( int j = 0 ; j < m ; j++ ) { if ( mat.getEntry( i , j ) < 0 ) { return false ; } } } // check that column totals = 1 double x = 0 ; for( int j = 0 ; j < m ; j++ ) { x = 0 ; for( int i = 0 ; i < n ; i++ ) { x = x + mat.getEntry( i , j ) ; } if ( Math.abs( x - 1 ) > 0.0001 ) { return false ; } } // check that the total population doesn't exceed 1,000,000 n = vec.getNumRows( ) ; x = 0 ; for ( int i = 0 ; i < n ; i++ ) { x = x + vec.getEntry( i , 0 ) ; } if( x > 1000000.0 ) { return false ; } // check that number of years is <= 100 if( intNumYears > 100 ) { return false ; } // getting here means input OK return true ; } private void displayInputErrorMessage( TextArea x ) { ClearOutput( ) ; x.append( "\n*******************\n*** INPUT ERROR ***\n*******************" ) ; x.append( "\n- Matrix entries should be >= 0" ) ; x.append( "\n- Column totals should be 1" ) ; x.append( "\n- Total population should be <= 1000000" ) ; x.append( "\n- Number of years should be <= 100" ) ; } private void DisplayOutputLine( int year ) { TextArea x = txtOutput ; // just a shorter name ( x ) for the display area ( txtOutput ) int[] vals = new int[ 3 ] ; // array to hold integer values to be displayed int[] pipePositions = { 6 , 18 , 30 } ; // these are the 'tab' positions where the pipes ( | ) appear in the output display int line_num = 0 , pos = 0 , width = 0 , num_blanks = 0 ; line_num = year + 2 ; // the first two lines (i.e., lines 0 and 1 ) of output are for column headings, // so year i state appears on line i + 2 of text area width = x.getColumns( ) ; for ( int i = 0 ; i < vals.length ;i++ ) { // loop to cast values as ints ( and round ) - for display purposes vals[ i ] = ( int ) ( vec.getEntry( i , 0 ) + 0.5 ) ; } txtOutput.append( "\n" + year ) ; for ( int i = 0 ; i < vals.length ; i++ ) { // loop to display values, seperate by pipes, at proper places // calculations depend on the # of characters in each line // being equal to the width of the textarea txtOutput pos = x.getCaretPosition( ) ; pos = pos - line_num * width ; num_blanks = pipePositions[ i ] - pos ; displayBlanks( x , num_blanks ) ; x.append( "| " + vals[ i ] ) ; } // now fill rest of line with blanks, so that # chars in line = width of textarea pos = x.getCaretPosition( ) ; pos = pos - line_num * width ; num_blanks = width - pos ; displayBlanks( x , num_blanks ) ; } private void displayBlanks( TextArea x , int n ) { for( int i = 0 ; i < n ; i++ ) { x.append( " " ) ; } } private void CalculateNextState( ) { // next state vector is matrix times current state vector vec = MatrixMath.Product( mat , vec ) ; } private void ClearOutput( ) { txtOutput.setText( "Year | Company A | Company B | No cable \n-----|-----------|-----------|---------" ) ; } }