program gradeval
!  Evaluates calculated vs. "true" gpa.
implicit none
real g ! Raw "true" grade. 100 point max, 90-100=A, 80 - <=90 is B etc.
real gpa ! Calculates GPA as a function of g and grading system.
real gpa_err ! Calculates rms error in GPA as a function of g and grading sys
real teacherr! Calculates the rms error in assigned gpa due to teacher errors.
external teacherr

real old ! Old(g) returns quality points for g under old system.
external old
real new ! new(g) returnes quality points for g under new system.
external new
real sd ! standard deviation of noise in grades
character*80 sd_char !character representation of sd from command line

!Argument passing declarations.  Note: getarg is not standard f90
external getarg
integer*4 iargc
external iargc

character*80 student_gpa_file,teacher_gpa_file !Output files
character*80 myname !the name of the executable of this program


call getarg(0,myname)

if (iargc()<>3) then
   write(*,*) "Syntax:"
   write(*,*) trim(myname)//" sd student_file teacher_file"
   write(*,*) "where"
   write(*,*) "sd=standard deviation of both student grades and teacher errors"
   write(*,*) "student_file is output file of student grades"
   write(*,*)  "teacher_file is output file of teacher grades."
   stop
end if

!Read command line arguments
call getarg(1,sd_char)
read(unit=sd_char,fmt=*) sd
call getarg(2,student_gpa_file)
call getarg(3,teacher_gpa_file)

!Generate the effects of new and old system on student GPA's

open(11,file=student_gpa_file)

write(11,*) "Student GPAs, for SD=", sd
write(11,90) "Raw","Old GPA","New GPA","Diff","Old RMS Error","New RMS Error"

do g=50,100,0.1
      write(11,100) g,gpa(old,g,sd),gpa(new,g,sd),gpa(new,g,sd)-gpa(old,g,sd),gpa_err(old,g,sd),gpa_err(new,g,sd)
end do

90 format(A5,3A10,2A17)
100 format(F5.1,3F10.3,2F17.3)

close(11)



!Now let's find the rms error in the assigned gpa due to errors
!in grading under the new and old systems.

open(11,file=teacher_gpa_file)

write(11,*) "Errors in QPs due to errors in teacher scores, for SD=", sd
write(11,190) "Raw","Old QP","New QP","Old Grading Err","New Grading Err"

190 format(A5,2A10,2A17)
200 format(F5.1,2F10.3,2F17.3)

do g=50,100,0.1
   write(11,200) g,gpa(old,g,sd),gpa(new,g,sd),teacherr(old,g,sd),teacherr(new,g,sd)
end do
close(11)

stop 
end


function gpa(func,g,sd)
! Calculate the gpa the student would have under system given by "func",
! given a mean grade of g and a standard deviation of sd.
! P(offset,sd) describes the probability of receiving a score that
! differs by offset from the grade g.  sd is the the standard deviation
! of the probability distribution P.

real gpa         !the calculated gpa
real:: func      ! func(g) returns quality points for g under gradings system
                 !    of interest.
external func
real,intent(in):: g  !raw grade.
real, intent(in)::sd !standard deviation of grade distribution (typical noise in grades)
real offset          !Offset of a noisy grade from "true" grade
real denom
real step
real P
external P
real true
external true


!Integrate probability P*func(grade) over +/- sd standard devations
gpa=0.0
denom=0.0
step=sd/200.0

do offset=-4.0*sd,4.0*sd,step
      gpa=gpa+P(offset,sd)*func(g+offset)
      denom=denom+P(offset,sd)
end do
gpa=gpa/denom
return
end

function gpa_err(func,g,sd)
! Calculate the gpa the student would have under A,B,C,D system.
implicit none
real gpa_err !the calculated gpa
real func ! func(g) returns quality points for g under gradings system
          ! of interest.
external func
real g  !raw grade.
real sd !standard deviation of grade distribution (typical noise in grades)
real offset !Offset of a noisy grade from "true" grade
real denom
real step
real P
external P
real true
external true


!Integrate probability P*(func(grade)- true grade)^2 
!    over +/- 4 standard devations

gpa_err=0.0
denom=0.0
step=sd/200.0

do offset=-4.0*sd,4.0*sd,step
      gpa_err=gpa_err+P(offset,sd)*(func(g+offset)-true(g+offset))**2
      denom=denom+P(offset,sd)
end do

gpa_err=sqrt(gpa_err/denom)

return
end



function old(g)
real old !quality points under old system.
real g  !raw grade

if (g >= 90) then 
      old=4.0
else if (g >= 80) then
      old=3.0
else if (g >= 70) then
      old=2.0
else if (g >= 60) then
      old=1.0
else
      old=0.0
end if

return
end

function new(g)
real new !quality points under new system
real g    !raw score

if ( g >= 93.333 ) then
      new=4.0
else if (g >= 90.0) then
      new=3.667
else if (g >= 86.667) then
      new=3.333
else if (g >=83.333 )then
      new=3.0
else if (g >= 80) then
      new=2.667
else if (g >=76.667) then
      new=2.333
else if (g>=73.333) then
      new=2.0
else if(g>=70.0) then
      new =1.667
else if (g>=66.667) then
      new=1.333
else if (g >=63.333) then
      new=1.0
else if (g>=60.0) then
      new=0.667
else
   new=0.0

end if

return
end

function true(g)
real true
real g

!calculates "true" gpa, the one you would have if there were no noise.
!Assumes linear mapping of 10 point scale, 1.0=65,2.0=75,3.0=85,4.0=95.
!But all grades over 95 are 4.0, all grades under 60 are 0.0

if (g>=95) then
      true=4.0
else if (g<60) then
      true=0.0
else 
      true=(g-55.0)/10.0
end if

return
end

function P(offset,sd)
!Calculates probability of a certain offset with standard deviation sd.
real P
real offset
real sd

P=exp(-offset**2/(2*sd**2))
return
end


function teacherr(func,g,sd)
real teacherr
!Calculates the rms error in the assigned quality points, assuming
!the teachers assigned raw grade g has an rms error sd
real g ! raw grade
real sd !rms error in raw grade assigned
real right !the quality points the student should have received.

real func  !the gpa assignment function
external func

real offset  !Error from true raw grade
real denom   !Normalization factor

teacherr=0.0
denom=0.0

step=sd/100.0
do offset=-4*sd,4*sd,step
   teacherr=teacherr+P(offset,sd)*(func(g+offset)-func(g))**2
   denom=denom+P(offset,sd)
end do

teacherr=sqrt(teacherr/denom)
if (teacherr<0) write(*,*) "Error: teacherr=",teacherr

return 
end