Maple grading function

# File: GradeAntiderivative.mpl 
# Original version thanks to Albert Rich emailed on 03/21/2017 
 
#Nasser 03/22/2017  Use Maple leaf count instead since buildin 
#Nasser 03/23/2017  missing 'ln' for ElementaryFunctionQ added 
#Nasser 03/24/2017  corrected the check for complex result 
#Nasser 10/27/2017  check for leafsize and do not call ExpnType() 
#                   if leaf size is "too large". Set at 500,000 
#Nasser 12/22/2019  Added debug flag, added 'dilog' to special functions 
#                   see problem 156, file Apostol_Problems 
#Nasser 4/07/2022   add second output which gives reason for the grade 
 
GradeAntiderivative := proc(result,optimal) 
local leaf_count_result, 
        leaf_count_optimal, 
        ExpnType_result, 
        ExpnType_optimal, 
        debug:=false; 
 
        leaf_count_result:=leafcount(result); 
        #do NOT call ExpnType() if leaf size is too large. Recursion problem 
        if leaf_count_result > 500000 then 
            return "B","result has leaf size over 500,000. Avoiding possible recursion issues."; 
        fi; 
 
        leaf_count_optimal := leafcount(optimal); 
        ExpnType_result  := ExpnType(result); 
        ExpnType_optimal := ExpnType(optimal); 
 
        if debug then 
            print("ExpnType_result",ExpnType_result," ExpnType_optimal=",ExpnType_optimal); 
        fi; 
 
# If result and optimal are mathematical expressions, 
#  GradeAntiderivative[result,optimal] returns 
#   "F" if the result fails to integrate an expression that 
#       is integrable 
#   "C" if result involves higher level functions than necessary 
#   "B" if result is more than twice the size of the optimal 
#       antiderivative 
#   "A" if result can be considered optimal 
 
    #This check below actually is not needed, since I only 
    #call this grading only for passed integrals. i.e. I check 
    #for "F" before calling this. But no harm of keeping it here. 
    #just in case. 
 
 
    if not type(result,freeof('int')) then 
        return "F","Result contains unresolved integral"; 
    fi; 
 
 
    if ExpnType_result<=ExpnType_optimal then 
        if debug then 
             print("ExpnType_result<=ExpnType_optimal"); 
        fi; 
        if is_contains_complex(result) then 
            if is_contains_complex(optimal) then 
                if debug then 
                        print("both result and optimal complex"); 
                fi; 
                if leaf_count_result<=2*leaf_count_optimal then 
                    return "A"," "; 
                else 
                   return "B",cat("Both result and optimal contain complex but leaf count of result is larger than twice the leaf count of optimal. ", 
                                   convert(leaf_count_result,string)," vs. $2 (", 
                                   convert(leaf_count_optimal,string)," ) = ",convert(2*leaf_count_optimal,string),"$."); 
                end if 
            else #result contains complex but optimal is not 
                if debug then 
                        print("result contains complex but optimal is not"); 
                fi; 
                return "C","Result contains complex when optimal does not."; 
            fi; 
        else # result do not contain complex 
             # this assumes optimal do not as well. No check is needed here. 
            if debug then 
                   print("result do not contain complex, this assumes optimal do not as well"); 
            fi; 
            if leaf_count_result<=2*leaf_count_optimal then 
                if debug then 
                    print("leaf_count_result<=2*leaf_count_optimal"); 
                fi; 
                return "A"," "; 
            else 
                if debug then 
                    print("leaf_count_result>2*leaf_count_optimal"); 
                fi; 
                return "B",cat("Leaf count of result is larger than twice the leaf count of optimal. $", 
                                   convert(leaf_count_result,string),"$ vs. $2(", 
                                   convert(leaf_count_optimal,string),")=",convert(2*leaf_count_optimal,string),"$."); 
            fi; 
        fi; 
    else #ExpnType(result) > ExpnType(optimal) 
        if debug then 
            print("ExpnType(result) > ExpnType(optimal)"); 
        fi; 
        return "C",cat("Result contains higher order function than in optimal. Order ", 
                       convert(ExpnType_result,string)," vs. order ", 
                       convert(ExpnType_optimal,string),"."); 
    fi; 
 
end proc: 
 
# 
# is_contains_complex(result) 
# takes expressions and returns true if it contains "I" else false 
# 
#Nasser 032417 
is_contains_complex:= proc(expression) 
  return (has(expression,I)); 
end proc: 
 
# The following summarizes the type number assigned an expression 
# based on the functions it involves 
# 1 = rational function 
# 2 = algebraic function 
# 3 = elementary function 
# 4 = special function 
# 5 = hyperpergeometric function 
# 6 = appell function 
# 7 = rootsum function 
# 8 = integrate function 
# 9 = unknown function 
 
ExpnType := proc(expn) 
  if type(expn,'atomic') then 
    1 
  elif type(expn,'list') then 
    apply(max,map(ExpnType,expn)) 
  elif type(expn,'sqrt') then 
    if type(op(1,expn),'rational') then 
       1 
    else 
       max(2,ExpnType(op(1,expn))) 
    end if 
  elif type(expn,'`^`') then 
    if type(op(2,expn),'integer') then 
      ExpnType(op(1,expn)) 
    elif type(op(2,expn),'rational') then 
      if type(op(1,expn),'rational') then 
         1 
      else 
         max(2,ExpnType(op(1,expn))) 
      end if 
    else 
         max(3,ExpnType(op(1,expn)),ExpnType(op(2,expn))) 
    end if 
  elif type(expn,'`+`') or type(expn,'`*`') then 
    max(ExpnType(op(1,expn)),max(ExpnType(rest(expn)))) 
  elif ElementaryFunctionQ(op(0,expn)) then 
    max(3,ExpnType(op(1,expn))) 
  elif SpecialFunctionQ(op(0,expn)) then 
    max(4,apply(max,map(ExpnType,[op(expn)]))) 
  elif HypergeometricFunctionQ(op(0,expn)) then 
    max(5,apply(max,map(ExpnType,[op(expn)]))) 
  elif AppellFunctionQ(op(0,expn)) then 
    max(6,apply(max,map(ExpnType,[op(expn)]))) 
  elif op(0,expn)='int' then 
    max(8,apply(max,map(ExpnType,[op(expn)]))) else 
  9 
  end if 
end proc: 
 
 
ElementaryFunctionQ := proc(func) 
  member(func,[ 
        exp,log,ln, 
        sin,cos,tan,cot,sec,csc, 
        arcsin,arccos,arctan,arccot,arcsec,arccsc, 
        sinh,cosh,tanh,coth,sech,csch, 
        arcsinh,arccosh,arctanh,arccoth,arcsech,arccsch]) 
end proc: 
 
SpecialFunctionQ := proc(func) 
  member(func,[ 
        erf,erfc,erfi, 
        FresnelS,FresnelC, 
        Ei,Ei,Li,Si,Ci,Shi,Chi, 
        GAMMA,lnGAMMA,Psi,Zeta,polylog,dilog,LambertW, 
        EllipticF,EllipticE,EllipticPi]) 
end proc: 
 
HypergeometricFunctionQ := proc(func) 
  member(func,[Hypergeometric1F1,hypergeom,HypergeometricPFQ]) 
end proc: 
 
AppellFunctionQ := proc(func) 
  member(func,[AppellF1]) 
end proc: 
 
# u is a sum or product.  rest(u) returns all but the 
# first term or factor of u. 
rest := proc(u) local v; 
  if nops(u)=2 then 
     op(2,u) 
  else 
     apply(op(0,u),op(2..nops(u),u)) 
  end if 
end proc: 
 
#leafcount(u) returns the number of nodes in u. 
#Nasser 3/23/17  Replaced by build-in leafCount from package in Maple 
leafcount := proc(u) 
   MmaTranslator[Mma][LeafCount](u); 
end proc: