PROGRAM Academic_Standing
!-----------------------------------------------------------------------
! Program to determine academic standing of engineering students
! according to two criteria:  cumulative hours and cumulative GPA.
! It also counts the total # of students checked and the # found to
! be in good standing, and calculates the average current GPA for all
! students.  Variables used are:
!   NumStudents           : total number of students
!   Num_in_Good_Standing  : number in good standing
!   Response              : user response to more-data inquiry
!   Sum_of_GPAs           : sum of all current GPAs 
!
! Input:  Response; also several items of student information in 
!         subroutine CalculateStats 
! Output: Student report by subroutine Report and summary statistics 
!         by subroutine WrapUp  
!-----------------------------------------------------------------------

  IMPLICIT NONE
  INTEGER :: NumStudents = 0, Num_in_Good_Standing = 0
  REAL :: Sum_of_GPAs = 0.0
  CHARACTER(1) :: Response

  CALL Inform()

  ! Repeat the following until no more data
  DO
     CALL Process(NumStudents, Num_in_Good_Standing, Sum_of_GPAs)
     WRITE (*, '(// " ", A)', ADVANCE = "NO") "More (Y or N)? "
     READ *, Response
     IF (Response /= "Y") EXIT
  END DO

  CALL WrapUp(NumStudents, Num_in_Good_Standing, Sum_of_GPAs)
  STOP

CONTAINS

  !-Inform--------------------------------------------------------------
    
    SUBROUTINE Inform()
    
      PRINT *, "*********** Inform called ***********"
      RETURN

    END SUBROUTINE Inform
  
  
  !--Process------------------------------------------------------------
  ! Accepts student information, determines academic standing, and
  ! maintains counts of # processed and # in good standing, and a sum
  ! of current GPAs.  Variables used are:
  !   NumStudents          : total number of students
  !   Num_in_Good_Standing : number in good standing
  !   Sum_of_GPAs          : sum of all current GPAs
  !   StudentNumber        : student's number
  !   Class                : student's class
  !   CumulativeHours      : student's cumulative hours
  !   CumulativeGPA        : student's cumulative GPA
  !   CurrentGPA           : student's current GPA
  !   InGoodStanding       : indicates whether student is in good 
  !                          standing
  !
  ! Accepts:  NumStudents, Num_in_Good_Standing, and Sum_of_GPAs
  ! Returns:  Updated values of NumStudents, Num_in_Good_Standing, and
  !           Sum_of_GPAs
  !---------------------------------------------------------------------
  
  SUBROUTINE Process(NumStudents, Num_in_Good_Standing, Sum_of_GPAs)
  
    INTEGER, INTENT(IN OUT) :: NumStudents, Num_in_Good_Standing
    REAL, INTENT(IN OUT) :: Sum_of_GPAs
    REAL :: CumulativeHours, CumulativeGPA, CurrentGPA
    INTEGER :: StudentNumber, Class
    LOGICAL :: InGoodStanding
  
    CALL CalculateStats(StudentNumber, Class, NumStudents, &
                        CumulativeHours, CumulativeGPA, CurrentGPA,  &
                        Sum_of_GPAs)
    CALL CheckEligibility()
    CALL Report(StudentNumber, Class, CumulativeHours, CurrentGPA, &
                CumulativeGPA)
    RETURN

  END SUBROUTINE Process
  
  
  !-WrapUp--------------------------------------------------------------
  
  SUBROUTINE WrapUp(NumStudents, Num_in_Good_Standing, Sum_of_GPAs)
  
    INTEGER, INTENT(IN) ::  NumStudents, Num_in_Good_Standing
    REAL, INTENT(IN) :: Sum_of_GPAs
 
    PRINT *, "*********** WrapUp called ***********"
    PRINT *, "Number of Students =", NumStudents
    PRINT *, "Sum of GPAs =", Sum_of_GPAs
    RETURN

  END SUBROUTINE WrapUp
  
  
  !-CalculateStats------------------------------------------------------
  ! Subroutine to read a student's number, class, cumulative hours,
  ! and cumulative GPA; then read Hours and Grade for courses taken 
  ! during the current year, and calculate current GPA, update 
  ! cumulative hours, cumulative GPA, and count (NumStudents) of
  ! students processed.  Hours = 0 and Grade = 0 are used to signal the 
  ! end of data for a student.  Other local variables used are:
  !   NewHours       : total hours earned during current year
  !   NewHonorPoints : honor points earned in current year
  !   OldHonorPoints : honor points earned in past years
  !
  ! Accepts:  NumStudents and Sum_of_GPAs
  ! Input:    StudentNumber, Class, CumulativeHours, CumulativeGPA;
  !           also Hours and Grade for each of several courses
  ! Returns:  StudentNumber, Class, CumulativeHours, CumulativeGPA,
  !           CurrentGPA and updated values of NumStudents and
  !           Sum_of_GPAs
  !---------------------------------------------------------------------
  
  SUBROUTINE CalculateStats(StudentNumber, Class, NumStudents,  &
                            CumulativeHours, CumulativeGPA, &
                            CurrentGPA, Sum_of_GPAs)
  
    INTEGER, INTENT(IN OUT) ::  NumStudents
    INTEGER, INTENT(OUT) :: StudentNumber, Class
    REAL, INTENT(IN OUT) :: Sum_of_GPAs
    REAL, INTENT(OUT) :: CumulativeHours, CumulativeGPA, CurrentGPA
    REAL :: Hours, Grade, NewHours, NewHonorPoints, OldHonorPoints
 
    WRITE (*, '(" ", A)', ADVANCE = "NO") &
          "Enter student number, class, cum. hours, cum. gpa: "
    READ *, StudentNumber, Class, CumulativeHours, CumulativeGPA
    OldHonorPoints = CumulativeHours * CumulativeGPA
    NewHours = 0.0
    NewHonorPoints = 0.0

    DO
       WRITE (*, '(" ", A)', ADVANCE = "NO") "Hours and grade? "
       READ *, Hours, Grade
       IF (Hours <= 0.0) EXIT
       ! Terminate repetition if end-of-data signaled
       ! Otherwise continue with the following
  
       NewHours = NewHours + Hours
       NewHonorPoints = NewHonorPoints + Hours * Grade
    END DO

    IF (NewHours == 0.0) THEN
       CurrentGPA = 0.0
    ELSE
       CurrentGPA = NewHonorPoints / NewHours
    END IF
    Sum_of_GPAs = Sum_of_GPAs + CurrentGPA
    CumulativeHours = CumulativeHours + NewHours
    CumulativeGPA = (OldHonorPoints + NewHonorPoints) / CumulativeHours
    NumStudents = NumStudents + 1
    RETURN

  END SUBROUTINE CalculateStats
  
  
  !-CheckEligibility----------------------------------------------------
  
  SUBROUTINE CheckEligibility()
  
    PRINT *, "*********** CheckEligibility called ***********"
    RETURN

  END SUBROUTINE CheckEligibility
  
  
  !-Report--------------------------------------------------------------
  
  SUBROUTINE Report(StudentNumber, Class, CumulativeHours, &
                      CurrentGPA, CumulativeGPA)
  
    INTEGER, INTENT(IN) :: StudentNumber, Class
    REAL, INTENT(IN) :: CumulativeHours, CurrentGPA, CumulativeGPA
  
    PRINT *, "*********** Report called ***********"
  
    !----- Temporary printout -----
    PRINT *, "StudentNumber:", StudentNumber
    PRINT *, "Class:        ", Class
    PRINT *, "Cum. Hours:  ", CumulativeHours
    PRINT *, "Curr. GPA:   ", CurrentGPA
    PRINT *, "Cum. GPA:    ", CumulativeGPA
    RETURN

  END SUBROUTINE Report
  
END PROGRAM Academic_Standing
