Chapter 7 Introduction to Procedures. So far, all programs written in such way that all subtasks are...

42
Chapter 7 Introduction to Procedures

Transcript of Chapter 7 Introduction to Procedures. So far, all programs written in such way that all subtasks are...

Chapter 7

Introduction to Procedures

• So far, all programs written in such way that all subtasks are integrated in one single large program.

• There is no way to code, verify and test each subtask independently before combining them to the final program.

• Fortran has a special mechanism to do so.• Each subtask can be coded as a separate program unit called external

procedure which can be compiled and tested independently.• FORTRAN External Procedures:

– Subroutines• Can be invoked using CALL statement• They can return multiple results through calling arguments

– Functions• Can be invoked by directly using them in expression• They return single value that is then used in the expression

• Advantages of procedures:1. Independent testing of subtasks2. Reusable code3. Isolation from unintended effects

Introduction to Procedures

• Subroutine is a FORTRAN procedure that is invoked by naming it in CALL statement and that retrieves its input values and return its results through an argument list.

• It is an independent program that starts with SUBROUTINE and ends with END SUBROUTINE

• The general form of subroutine (similar to program organization)

SUBROUTINE sub_name (argument_list)…

( Declaration section)…

(Execution section)…

RETURNEND SUBROUTINE [sub_name]

Subroutines

Beginning

END

Optional

Standard FORTRAN name

Dummy arguments: variables or arrays

passed from program

Optional

If we need to return before END similar to STOP, END Program

• It is compiled independently away from the program and any other subroutine.

• When a program call the subroutine, the execution of the program will suspend and the procedure will execute till it reach RETURN or END SUBROUTINE and the program then resumes.

• Any executable program can call a subroutine including another subroutine.

Subroutines

Calling a subroutine

• Place a CALL statement in the calling program’s code.

CALL sub_name (argument_list)

• The order and type of the actual arguments match the order and type of dummy arguments in the declared subroutine.

Example-1

• Write a program that calculates the hypotenuse of right triangle. The user should enter the first and second sides (x, y) and the program calculates and print the hypotenuse (z)

x

y

z

22 yxz

PROGRAM mainProgramIMPLICIT NONEREAL :: x, y, zWRITE (*,*) 'Enter first value: 'READ (*,*) xWRITE (*,*) 'Enter second value: 'READ(*,*) yCALL calculate(x, y, z)WRITE (*,*) 'Result: ', zEND PROGRAM

SUBROUTINE calculate ( first, second, output)IMPLICIT NONEREAL, INTENT(IN) :: first, secondREAL, INTENT(OUT) :: outputoutput = SQRT(first**2 + second**2)RETURNEND SUBROUTINE

PROGRAM mainProgramIMPLICIT NONEREAL :: x, y, zWRITE (*,*) 'Enter first value: 'READ (*,*) xWRITE (*,*) 'Enter second value: 'READ(*,*) yz = SQRT(x**2 + y**2)WRITE (*,*) 'Result: ', zEND PROGRAM

You can remove RETURN

INTENT Attribute

• INTENT(IN)Dummy argument is used to pass input data to subroutine only.

• INTENT(OUT)Dummy argument is used to return result to the calling program only.

• INTENT(INOUT) or INTENT(IN OUT)Dummy argument is used to both pass input data to subroutine and to

return result to the calling program.

• You need to specify INTENT type for each argument but not for local variables of the subroutine.

• This will help the compiler to know what is expected for each argument and easily discover errors.

REAL, INTENT(IN) :: inin = 3 [ERROR]

Example-2PROGRAM mainProgramIMPLICIT NONEINTEGER, PARAMETER:: max_Size =10INTEGER :: i, jREAL, DIMENSION (max_Size) :: datREAL :: tempWrite (*,*) 'Enter DATA set (', max_Size,' values) :'WRITE (*,*)DO i=1, max_SizeREAD (*,*) dat(i)END DODO i=max_Size, 1, -1DO j=2, iIF (dat(j-1) > dat(j)) THENtemp = dat(j-1)dat(j-1) = dat(j)dat(j) = tempENDIFEND DOEND DOWRITE (*,*) ' Sorted data: 'DO i=1, max_SizeWRITE (*,*) dat(i)END DOEND PROGRAM

PROGRAM mainProgramIMPLICIT NONEINTEGER, PARAMETER:: max_Size =10INTEGER :: iREAL, DIMENSION (max_Size) :: datWrite (*,*) 'Enter DATA set (', max_Size,' values) :'WRITE (*,*)DO i=1, max_SizeREAD (*,*) dat(i)END DOCALL sortArray(dat, max_Size)WRITE (*,*) ' Sorted data: 'DO i=1, max_SizeWRITE (*,*) dat(i)END DOEND PROGRAMSUBROUTINE sortArray (array, arrSize)IMPLICIT NONEINTEGER, INTENT (IN):: arrSizeREAL, DIMENSION(arrSize), INTENT(INOUT) :: arrayINTEGER :: i, jREAL :: tempDO i=arrSize, 1, -1DO j=2, iIF (array(j-1) > array(j)) THENtemp = array(j-1)array(j-1) = array(j)array(j) = tempENDIFEND DOEND DOEND SUBROUTINE

Sort data in array

Variable passing in FORTRAN• Pass-by-Reference

– The program passes a pointer to the memory location of actual arguments in the list which are going to be used by the subroutine to get the values of the dummy arguments.

– So, pointers are sent to the subroutine not values.

– Actual arguments should match the dummy arguments in number, type, and order

PROGRAM test

SUBROUTINE adding

PROGRAM testREAL :: a, b, sumINTEGER :: c...CALL adding (a, b, c, sum)...END PROGRAM

SUBROUTINE adding (x1, x2, x3, x4)REAL, INTENT(IN) :: x1, x2INTEGER, INTENT(IN) :: x3REAL, INTENT(OUT) :: x4x4 = X1 + X2 + X3END SUBROUTINE

RE

AL

INT

EG

ER

RE

AL

RE

AL

ErrorsPROGRAM testREAL :: a, b, sumREAL :: cREAD (*,*) a, b, cCALL adding (a, b, c)WRITE (*,*) 'sum : ', sumEND PROGRAM

SUBROUTINE adding (x1, x2, x3, x4)REAL, INTENT(IN) :: x1, x2INTEGER, INTENT(IN) :: x3REAL, INTENT(OUT) :: x4WRITE (*,*) x1, ' ', x2,' ', x3x4 = X1 + X2 + X3END SUBROUTINE

________________________________Input : 12.6 16.5 14.9Output: 12.6 16.5 1097754214sum : 1.09775424E+09

___________________Wrong:Number of arguments does not match

PROGRAM testREAL :: a, b, sumREAL :: cREAD (*,*) a, b, cCALL adding (a, b, c, sum)WRITE (*,*) 'sum : ', sumEND PROGRAM

SUBROUTINE adding (x1, x2, x3, x4)REAL, INTENT(IN) :: x1, x2INTEGER, INTENT(IN) :: x3REAL, INTENT(OUT) :: x4WRITE (*,*) x1, ' ', x2,' ', x3x4 = X1 + X2 + X3END SUBROUTINE

Exercise-1

Write a subroutine that takes three real values, add them, and return the sum.

PROGRAM testREAL :: a, b, c, sumWRITE (*,*) ‘Enter three values: ’READ (*,*) a, b, cCALL adding (a, b, c, sum)WRITE (*,*) 'sum : ', sumEND PROGRAM

SUBROUTINE adding (x1, x2, x3, x4)REAL, INTENT(IN) :: x1, x2, x3REAL, INTENT(OUT) :: x4x4 = X1 + X2 + X3END SUBROUTINE

Exercise-2

Write a subroutine that accepts three real values and return the maximum value out of the three. Don’t use the function MAX. PROGRAM test

REAL :: a, b, c, maxValueWRITE (*,*) 'Enter three values: 'READ (*,*) a, b, cCALL getMax (a, b, c, maxValue)WRITE (*,*) '‘Largest Value : ', maxValueEND PROGRAM

SUBROUTINE getMax (x1, x2, x3, x4)REAL, INTENT(IN) :: x1, x2, x3REAL, INTENT(OUT) :: x4x4=x1IF(x2>x4) THENx4=x2ENDIFIF(x3>x4) THENx4=x3ENDIFEND SUBROUTINE

Exercise-3

Write a subroutine that accepts three real values and return the minimum value out of the three. Don’t use the function MIN. PROGRAM test

REAL :: a, b, c, maxValueWRITE (*,*) 'Enter three values: 'READ (*,*) a, b, cCALL getMax (a, b, c, maxValue)WRITE (*,*) '‘Largest Value : ', maxValueEND PROGRAM

SUBROUTINE getMax (x1, x2, x3, x4)REAL, INTENT(IN) :: x1, x2, x3REAL, INTENT(OUT) :: x4x4=x1IF(x2<x4) THENx4=x2ENDIFIF(x3<x4) THENx4=x3ENDIFEND SUBROUTINE

Exercise-4

Write a subroutine that swap the values of two variables.

PROGRAM testREAL :: a, bWRITE (*,*) 'Enter two values: 'READ (*,*) a, bWRITE (*,*) ' a : ', a, ' b : ', bCALL swap (a, b)WRITE (*,*) ' a : ', a, ' b : ', bEND PROGRAM

SUBROUTINE swap (a,b)REAL, INTENT(INOUT) :: a, bREAL :: temptemp = aa = bb = tempEND SUBROUTINE

Passing Arrays to subroutine

• All what is needed is to send pointer to the memory location of the first element of the array.

• However, the subroutine also needs to know the size of the array in order to perform operations and make sure indexes stay within bounds.

• There are three ways to do so:1. Explicit-Shape dummy array2. Assumed-Shape dummy array3. Assumed-size dummy array

Explicit-Shape Dummy Array

• Pass the bounds and dimensions of the array as arguments to the subroutine, then the array will be declared accordingly.

• This allow the bound checker in most of FORTRAN compiler to detect out-of-bound errors.

• In this method, since the shape and bounds are known, whole operations and array subsections can be used.

SUBROUTINE getNewData (dat1, dat2, n)INTEGER, INTENT(IN) :: n REAL, Dimension (n), INTENT(OUT) :: dat2REAL, Dimension (n), INTENT(IN) :: dat1INTEGER :: iDO i=1,ndat2(i)=dat1(i)+dat1(i)**2END DOEND SUBROUTINE

SUBROUTINE getNewData (dat1, dat2, n)INTEGER, INTENT(IN) :: n REAL, Dimension (n), INTENT(OUT) :: dat2REAL, Dimension (n), INTENT(IN) :: dat1INTEGER :: idat2=dat1+dat1**2END SUBROUTINE

SUBROUTINE getNewData (dat1, dat2, n)INTEGER, INTENT(IN) :: nREAL, Dimension (n), INTENT(OUT) :: dat2REAL, Dimension (n), INTENT(IN) :: dat1INTEGER :: idat2(1:n)=dat1(1:n)+dat1(1:n)**2END SUBROUTINE

Assumed-Shape Dummy Array

• Shape is known rather than bounds. • Declared using colon as placeholder for subscripts of the arrays• The subroutine should have an explicit interface and no need to

pass the array size or bounds to it. (uses modules)• Whole array operations and subsection can be used in this type

MODULE testCONTAINS

SUBROUTINE getNewData (dat1, dat2)REAL, Dimension (:), INTENT(OUT) :: dat2REAL, Dimension (:), INTENT(IN) :: dat1dat2=dat1+dat1**2END SUBROUTINE

END MODULE

Assumed-Size Dummy Array

• Declare the size of the array as (*) assumed size• Actual length not known by the compiler• Bounds checking, whole array operations and array sections does

not work here.• This was in old FORTRAN, it should never be used in new

programs

SUBROUTINE getNewData (dat1, dat2, n)INTEGER, INTENT(IN) :: n REAL, Dimension (*), INTENT(OUT) :: dat2REAL, Dimension (*), INTENT(IN) :: dat1DO i=1,ndat2(i)=dat1(i)+dat1(i)**2END DOEND SUBROUTINE

n is not the array size. It represent how many elements should be involved in

the operation

Exercise-5

Write a subroutine that accept array and return the sum of all its elements.

PROGRAM testIMPLICIT NONEINTEGER, Parameter :: maxSize=5REAL :: sumREAL, DIMENSION(maxSize) :: arr=(/2., -1., 5., 10., 0./)CALL sumAll (arr, maxSize, sum)WRITE(*,*) ' sum of all values: ', sumEND PROGRAM

SUBROUTINE sumAll (dat, n, total)INTEGER, INTENT(IN) :: nREAL, Dimension (n), INTENT(IN) :: datREAL, INTENT(OUT) :: totalINTEGER :: itotal=0DO i=1,ntotal=total+dat(i)END DOEND SUBROUTINE

Exercise-6

Write a subroutine that find the maximum value in an array (don’t use MAX function).

PROGRAM testIMPLICIT NONEINTEGER, Parameter :: maxSize=5REAL :: maxValueREAL, DIMENSION(maxSize) :: arr=(/2., -1., 5., 10., 0./)CALL getMax (arr, maxSize, maxValue)WRITE(*,*) ' maximum values: ', maxValueEND PROGRAM

SUBROUTINE getMax (dat, n, num)INTEGER, INTENT(IN) :: nREAL, Dimension (n), INTENT(IN) :: datREAL, INTENT(OUT) :: numINTEGER :: inum=dat(1)DO i=2,nIF (dat(i)>num) THENnum = dat(i)END IFEND DOEND SUBROUTINE

Exercise-7

Write a subroutine that find the minimum value in an array (don’t use MIN function).

PROGRAM testIMPLICIT NONEINTEGER, Parameter :: maxSize=5REAL :: maxValueREAL, DIMENSION(maxSize) :: arr=(/2., -1., 5., 10., 0./)CALL getMax (arr, maxSize, maxValue)WRITE(*,*) ' maximum values: ', maxValueEND PROGRAM

SUBROUTINE getMax (dat, n, num)INTEGER, INTENT(IN) :: nREAL, Dimension (n), INTENT(IN) :: datREAL, INTENT(OUT) :: numINTEGER :: inum=dat(1)DO i=2,nIF (dat(i)<num) THENnum = dat(i)END IFEND DOEND SUBROUTINE

Passing Character Variables to Subroutine• The length of the character variable is declared with (*).• It is not necessary to know the length in subroutine.• When the subroutine is called the length of the dummy argument

will be the length of the actual argument in the program.• However, you can tell the size of the character at run time using the

function LEN.

SUBROUTINE sampleSUB (name)CHARACTER(len=*), INTENT(IN) :: name WRITE(*,*) ‘The length of the received name is: ’, LEN(name)END SUBROUTINE

Error Handling in SubroutineSUBROUTINE getRES (a, b, res)IMPLICIT NONEREAL, INTENT(IN) :: a, bREAL, INTENT(OUT) :: resREAL :: temptemp = a -bres = SQRT(temp)END SUBROUTINE

What does this subroutine do?

What is the output if a = 1, b = 2 ?

Run time error: res = -NaN

Rewrite it to avoid this problem.

SUBROUTINE getRES (a, b, res)IMPLICIT NONEREAL, INTENT(IN) :: a, bREAL, INTENT(OUT) :: resREAL :: temptemp = a-bIF (temp>=0) THENres = SQRT(temp)ELSEWRITE(*,*) 'Square root of negative value in subroutine getRes'STOPENDIFEND SUBROUTINE

First solution: when square root of negative values is requested print an error and stop the program before returning form subroutine. The main program looses all data that are processed before calling the subroutine. Is there a better way?

SUBROUTINE getRES (a, b, res, error)IMPLICIT NONEREAL, INTENT(IN) :: a, bREAL, INTENT(OUT) :: resINTEGER, INTENT(OUT) :: errorREAL :: temptemp = a-bIF (temp>=0) THENres = SQRT(temp)error = 0ELSEres = 0error = 1ENDIFEND SUBROUTINE

Never use STOP in any subroutine. Add an error flag that indicates the situation when the subroutine return wrong values and pass it to the main program. After returning from the subroutine, the program should check the flag and display error message for the user.

Program Design

• Write a program that accepts real values from the user and store them in 1-D array. The user first should be asked to enter the size of the data set he needs to enter. Then, he will be asked to enter the values. Your program should make statistics on the data that are stored in the array. The statistics will include:

1. Find the max value and its location in the array2. Find the min value and its location in the array3. Calculate the average of all values 4. Find the median5. Calculate the standard deviation

• For each one of the above subtasks write a subroutine. The main program will just accept the user inputs, store them in an array and call these five subroutines to get the results and print them to the user.

Sharing Data Using Modules

• Programs, Subroutine and Functions can exchange data through modules in addition to argument list.

• Module is a separately compiled program unit that contains the definitions and initial values of the data we wish to share between program units.

• It provides a way to share data between program units.

• Modules are especially useful for sharing large volumes of data among many program and procedure units.

Module Construct

MODULE module_name

IMPLICIT NONE

SAVE

INTEGER, PARAMETER :: num_Vals = 5

REAL, DIMENSION(num_Vals) :: values

END MODULE

Beginning

END

Declaration of the data to be

shared

• Any program unit can make use of that share data it contains the command

USE module_name

• The USE command must be the first command after the program name or subroutine name.

• USE association is the process of accessing information in a module using the USE statement

Using Modules

Example - 3

MODULE module_nameIMPLICIT NONESAVEINTEGER, PARAMETER :: num_Vals = 5REAL, DIMENSION(num_Vals) :: valuesEND MODULE

PROGRAM testUSE module_nameIMPLICIT NONEvalues=(/1, 2, 3, 4, 5/)WRITE (*,*) valuesEND PROGRAM

SUBROUTINE displayUSE module_nameIMPLICIT NONEWRITE (*,*) valuesEND SUBROUTINE

Example - 4

MODULE module_nameIMPLICIT NONESAVEINTEGER, PARAMETER :: num_Vals = 5REAL, DIMENSION(num_Vals) :: values=(/1,2,3,4,5/)END MODULE

PROGRAM testIMPLICIT NONECALL displayEND PROGRAM

SUBROUTINE displayUSE module_nameIMPLICIT NONEWRITE (*,*) valuesEND SUBROUTINE

Random Number Generator

• Random number generator is a procedure that return different numbers that seem to be random.

• One simple random number generator uses the following equation

Ni+1 = MOD( 8121 ni + 28411, 134456)

• The random number rani = ni / 134456

• n0 is call the seed of the sequence which should be entered by the user so the sequence vary from run to another.

• Write a subroutine randomNum that generates and return a single number ran based on the above equations. Another subroutine (seed) should be called to get the seed value n0.

Random Number GeneratorPROGRAM randomGeneratorIMPLICIT NONEINTEGER :: n0REAL :: numWRITE (*,*) 'Enter the seed value: 'READ (*,*) n0CALL seed(n0)CALL randomNum(num)WRITE (*,*) numEND PROGRAM

SUBROUTINE randomNum(ran)USE ranValueIMPLICIT NONEREAL, INTENT(OUT):: rann = MOD(8121*n+28411, 134456)ran = REAL(n)/134456END SUBROUTINE

SUBROUTINE seed(inSeed)USE ranValueIMPLICIT NONEINTEGER, INTENT(IN):: inSeedn = ABS(inSeed)END SUBROUTINE

MODULE ranValueIMPLICIT NONESAVEINTEGER :: nEND MODULE

PROGRAM randomGeneratorIMPLICIT NONEINTEGER :: n0REAL :: numWRITE (*,*) 'Enter the seed value: 'READ (*,*) n0CALL seed(n0)CALL randomNum(num)WRITE (*,*) numEND PROGRAM

SUBROUTINE randomNum(ran)USE ranValueIMPLICIT NONEREAL, INTENT(OUT):: rann = MOD(8121*n+28411, 134456)ran = REAL(n)/134456END SUBROUTINE

SUBROUTINE seed(inSeed)USE ranValueIMPLICIT NONEINTEGER, INTENT(IN):: inSeedn = ABS(inSeed)END SUBROUTINE

MODULE ranValueIMPLICIT NONESAVEINTEGER :: nEND MODULE

Full Program• Now, modify the main program so

that it prints 10 random numbers.

• Then, make it generates 1000 numbers and calculate their average.

PROGRAM randomGeneratorIMPLICIT NONEINTEGER :: n0, iREAL :: num, sum=0, AvgWRITE (*,*) 'Enter the seed value: 'READ (*,*) n0CALL seed(n0)WRITE (*,*) ' 10 random numbers : 'DO i=1, 10CALL randomNum(num)WRITE (*,*) numEND DODO i=1, 1000CALL randomNum(num)sum = sum +numEND DOAvg = sum / 1000WRITE (*,*) ' The average of 1000 random numbers is: ', AvgEND PROGRAM

FORTRAN FUNCTION

• FORTRAN Function is a procedure that returns single output which can be, number, logical value, character string or an array.

• In contrast to routines, functions can be used and combined with expressions.

• Function types:

– Intrinsic functionsBuilt into the FORTRAN language (e.g. SIN(x), MAX(a,b))

– User-defined functions (function subprograms)Defined by a programmer to meet specific needs not addressed by

intrinsic functions and they are used in expressions like intrinsic function.

Function Construct

FUNCTION functionName (argument_List)…(Declaration Section must declare type of functionName)…(Execution section)functionName = exprRETURNEND FUNCTION [ functionName ]

FUNCTION myMax (a , b)IMPLICIT NONEREAL :: myMaxREAL, INTENT(IN) :: a, bIF(a>=b) THENmyMax = aELSEmyMax=bEND IFEND FUNCTION

Beginning

END

Optional

Standard FORTRAN nameDummy arguments: variables or arrays

passed from program. Can be blank but with

parenthesis

Optional

If we need to return before END

The return value

Declaring Function Type

FUNCTION myMax(a,b)

REAL :: myMax

FUNCTION myMax (a , b)IMPLICIT NONEREAL :: myMaxREAL, INTENT(IN) :: a, bIF(a>=b) THENmyMax = aELSEmyMax=bEND IFEND FUNCTION

REAL FUNCTION myMax (a , b)IMPLICIT NONEREAL, INTENT(IN) :: a, bIF(a>=b) THENmyMax = aELSEmyMax=bEND IFEND FUNCTION

ORREAL FUNCTION myMax(a,b)

Exercise-8Write a program that call the function quadf that calculates and returns

the value of the quadratic equation. In the main program the user will be asked to enter the three coefficients a, b, c and the evaluation value x. All of these four values will be sent as arguments to the function quadf. Finally, the program will print the result returned by the function.

cbxaxxcbaquadf 2),,,(

FUNCTION quadf(x,a,b,c)IMPLICIT NONEREAL :: quadfREAL, INTENT(IN) :: x,a,b,cquadf=a*x**2+b*x+cEND FUNCTION

REAL FUNCTION quadf(x,a,b,c)

IMPLICIT NONE

REAL, INTENT(IN) :: x,a,b,c

quadf=a*x**2+b*x+c

END FUNCTION

PROGRAM quadraticFunctionIMPLICIT NONEREAL :: quadfREAL :: a, b, c, x, fWRITE (*,*) 'Enter the three coefficients a,b,c : 'READ (*,*) a,b,cWRITE (*,*) 'Enter the evaluation value x: 'READ (*,*) xf = quadf(x, a, b, c)WRITE (*,*) 'The quadratic result: ', fEND PROGRAM

Unintended Side Effects in Functions

• Side effect happen when the function change the values of the input arguments in the program.

• The function should produce single output and it should have no side effects.

• It should never modify its input arguments.• If you need a function that produce more than

one result then, use subroutine instead.• Always declare input arguments with

INTENT(IN) to avoid side effects.

Exercise-9• The sinc function is defined by the equation:

sinc(x)=sin(x)/x

• It is easy to implement but consider x equal or very close to zero.

• Modify the equation to be:

IF |x|> Epsilon sinc(x)=sin(x)/xElse sinc=1

• Epsilon is very small real number that is chosen to ensure that the division does not cause divide by zero. A good value of Epsilon is 1.0E-30

PROGRAM getSINCIMPLICIT NONEREAL :: sincREAL :: xWRITE (*,*) 'Enter a number to evaluate with SINC : 'READ (*,*) xWRITE(*,*) 'THe value is: ', sinc(x)END PROGRAM

REAL FUNCTION sinc(x)IMPLICIT NONEREAL, PARAMETER :: eps=1.0E-30REAL, INTENT(IN) :: xIF (ABS(x)>eps) THENsinc=SIN(x)/xELSEsinc=1END IFEND FUNCTION

Tutorial

Write a subroutine to compute the average of all the elements in a matrix. Use a SUBROUTINE AND A MODULE within a program to ask the user for a matrix, and compute its average.

PROGRAM tutorial_on_subroutinesUSE tutIMPLICIT NONEINTEGER :: i, jWRITE(*,*) 'Enter the elements of the matrix A (3x3)'READ (*,*) ACALL find_meanWRITE (*,*) 'The mean of your matrix = ', A_meanEND PROGRAM

MODULE tutIMPLICIT NONEREAL, DIMENSION (3,3) :: AREAL:: A_meanEND MODULE

SUBROUTINE find_meanUSE tutIMPLICIT NONEINTEGER :: i, jREAL :: total=0.0DO i=1,3DO j=1,3total = total + A(i,j)END DOEND DOA_mean = total / 9.0END SUBROUTINE

Add one more subroutine to find the maximum value in the matrix and its location