#!/usr/bin/python
################################################################
#
#              Check Proofs
#
################################################################
import pickle,os,re,argparse,itertools,copy
import pattern, synt, prop, search
#
from urllib.request import urlretrieve
#
from simplify import simplify as simplify_trace
from getpath import getpath
from unify import main_unifier
#
parser = argparse.ArgumentParser()

parser.add_argument("file",
		help = "name of the file with proof(s) to be checked")

parser.add_argument("num",
		help = "number or regular expression for items to be checked") 

parser.add_argument("-c", "--caseflags", action = "store_true",
		help = "produce caseflags output from unification:")

parser.add_argument("-i", "--inferencerules", action = "store_true",
		help = "display each rule found")

parser.add_argument("-l", "--linenum", type=int,
		help = "line number of rule to test")

parser.add_argument("-m", "--multiline", action = "store_true",
		help = "display multiline transitivity checks")

parser.add_argument("-r", "--rules", nargs = '?', type=str,
		help = "rules of inference file")

parser.add_argument("-t", "--tracing", action = "store_true",
		help = "trace premises for relevant logic")

parser.add_argument("-T", "--Transitives", action = "store_true",
		help = "show parsed transitive attempts")

parser.add_argument("-v", "--verbose", action = "store_true",
		help = "produce verbose output from the unifier")

args = parser.parse_args()

if args.file.endswith('.tex'):
	Arg_1 = args.file[-4:]
else:
	Arg_1 = args.file

filename = Arg_1 + '.tex'
if not os.path.isfile(filename):
	raise SystemExit(filename + ' not found.')


############################################################
#
# Load proof(s) from .pfs file
#
############################################################

if not os.path.isfile(Arg_1 + ".pfs"):
	raise SystemExit("File " + Arg_1 + ".pfs not found.")

if os.stat(filename).st_mtime > os.stat(Arg_1 + ".pfs").st_mtime:
#
#     If .TeX file is modified then reparse, halt only on error
#
		if os.name == 'nt':
			read_parse =  os.popen("parse.py -q " + filename).read()
		else:
			read_parse =  os.popen("parse -q " + filename).read()
		if "numbered propositions" in read_parse:  # Parse successful
			pass
		else:
			raise SystemExit(read_parse)

try:
	f = open(Arg_1 + ".pfs" ,"rb")
	doc = pickle.load(f)
	f.close()
except:
	raise SystemExit("Error loading " + Arg_1 + ".pfs")

proof_list = doc.proofnumlist 
proof_list = [x.strip() for x in proof_list]


selectm = None
if args.num in proof_list:
	if args.num.isdigit():
		Line_num = int(args.num)
		Theorem_ref = None
	else:
		Theorem_ref = args.num
		Line_num = None
else:
	try:
		selectm = re.compile(args.num)
	except:
		print("Invalid pattern: " + args.num)
		raise SystemExit


############################################################
#
# Load math data from .dfs file
#
############################################################

if not os.path.isfile(Arg_1 + ".dfs"):
	raise SystemExit("File " + Arg_1 + ".dfs not found.")

try:
	f = open(Arg_1 + ".dfs" ,"rb")
	syntdb = pickle.load(f)
	f.close()
except:
	raise SystemExit("Error loading " + Arg_1 + ".dfs")

if len(syntdb) < synt.MD_LEN:
	raise SystemExit("File, " + Arg_1 + ".dfs, obsolete. ")
	
synt.mathdb = syntdb

############################################################
#
# Load tracing functions
#
############################################################

if args.tracing:
	if not os.path.isfile(Arg_1 + ".trc"):
		raise SystemExit("tracing requires a file `"+ Arg_1 + ".trc' which was not found. ")
	try:
		f = open(Arg_1 + ".trc","rb")
		premise_trace_function = pickle.load(f)
		f.close()
	except:
		raise SystemExit("Error reading file: " + Arg_1 + ".trc")

############################################################
#
# Load Reductions
#
############################################################

nreductionlist =  prop.getrules(syntdb[synt.MD_NREDFILE],raw = True)

prop.nreductionlist = nreductionlist 

reductionfilelist = syntdb[synt.MD_REDFILE]

reductionlist = prop.getrules(reductionfilelist) 

############################################################
#
# Load Rules of Inference 
#
############################################################


if args.rules:
	if args.rules.endswith('.tex'):
		rulefilelist = [args.rules]
	else:
		rulefilelist = [args.rules + '.tex']
elif syntdb[synt.MD_RFILE]:  #This is a list!
	rulefilelist = syntdb[synt.MD_RFILE]
else:
	rulefilelist = [filename]
	
rulelist = prop.getrules(rulefilelist, args.linenum)

if rulelist==[]:
	raise SystemExit("No rules found.")


######################################################################################
	

if selectm:
	selected_list = [x for x in doc.proofnumlist if selectm.match(x)]
	if not selected_list:
		print("No such proof or derivation")
		raise SystemExit


#
############################################################
#
# Define all the functions  
#
############################################################
#
def fetchrefs(file_refkey, filerefnums):
	extfile = syntdb[synt.MD_REFD] 
	if extfile[file_refkey].startswith("http:"):
		try:
			(tempfilename, headers) = urlretrieve(extfile[file_refkey])
			f = open(tempfilename,"r")
			line_list = f.readlines()
			f.close() 
		except:
			print("Accessing", extfile[file_refkey], "failed.")
			raise SystemExit
	else:
		try:
			f = open(getpath(extfile[file_refkey]),"r")
			line_list = f.readlines()
			f.close()
		except:
			print(extfile[file_refkey], "not found.")
			raise SystemExit
			
	filerefnums_copy = filerefnums[:] 
	linetail = [line_list[0], 0 , 1 , line_list]
	r = linetail[0]
	while r:                   # Begin external file pass
		propnumm = pattern.propnum.match(r)
		if propnumm:
			thm_ref = propnumm.group('refnum')
			thm_reflist = thm_ref.split('.')
			sreference = thm_reflist[0] 
			reference = thm_reflist[1] 
			if file_refkey == '0':
				current_ref = file_refkey + sreference + '.' + reference
				current_key = current_ref
			else:
				current_ref = sreference + '.' + reference
				current_key = current_ref + file_refkey
			if current_ref in filerefnums:
				beginmaths = pattern.beginmath.search(r)
				linetail[0] = r[beginmaths.end(1):]
				reffed_item = synt.getformula(linetail)
				if reffed_item:
					wp = reffed_item[2]
				else:
					print(current_key, "failed to parse.")
					raise SystemExit
				doc.basic_ref[current_key] = [wp, synt.varlist(wp)]
				if current_ref in filerefnums_copy:
					filerefnums_copy.remove(current_ref)
				else:
					print(current_ref,"repeated in", extfile[file_refkey])
					raise SystemExit
		synt.getline(linetail)
		r = linetail[0] 
	
	
	if filerefnums_copy:
		if len(filerefnums_copy) == 1:
			print("Reference not found: ", filerefnums_copy[0])
		else:
			print("References not found: " + ", ".join(filerefnums_copy))
		raise SystemExit	


class Blackhole:
	def write(self, string):
		pass

def search_transitive_rules(tran_inst, takenvars, localvars):
#	print("search_transitive_rules:")
	pr_form = prop.deep(tran_inst[0])
	pq_form = prop.deep(tran_inst[1])
	qr_form = prop.deep(tran_inst[2])
#	print("pq_form ==========", pq_form)
#	print("qr_form ==========", qr_form)
#	print("pr_form ==========", pr_form)
	instance = [pq_form, qr_form]

#	conclusion = pr_form
	result = search.search_rules(pr_form, instance, ['$','-','$'], 
									takenvars, localvars,rules=rulelist)
	if args.Transitives:
		print("pq_form  ====", pq_form)
		print("qr_form  ====", qr_form)
		print("pr_form  ====", pr_form)
		if result == 0:
			print("Transitivity step not checked.")
		else:
			print("This Transitivity step ok")
	return result

def ancestors(proof, c, choice):
	deduction_rule = proof.deductionrule
	single_line_flag = proof.notelist[c-1].single_line
	if single_line_flag : 
		parsed_reference =  proof.notelist[c-1].parsed_reference
		if parsed_reference == ['G']:
			return ["{'" + proof.numlist[c-1] + "'}"]
		elif 'P' in parsed_reference :
			if len(parsed_reference) > 1 and choice:
#				print("choice ====", choice)
				z = ancestors(proof, proof.note_num_consec[choice[proof.numlist[c-1]]], choice)
				return z
			elif len(parsed_reference) > 1:
				stringlist = []
				for p in parsed_reference[1:]:
					if p == ',':
						stringlist.append('&')
					elif p.startswith('.'):
						z = ancestors(proof, proof.note_num_consec[p], choice)
						if type(z) is str:
								return z
						else:
							stringlist.extend(z)
				return stringlist
			if deduction_rule:
				assert False
			else:
				return ["{'" + proof.numlist[c-1] + "'}"]
		try:
			inference_rule =  proof.notelist[c-1].inference_rule	
		except:
			error_message = \
			"Tracing requires a proof that checks.  Note " + str(c-1) + " doesn't check."
			return error_message
		if not inference_rule.tracefun:
			y  =  premise_trace_function.get((inference_rule.rulefile,
			 			inference_rule.linenum), -1)
			if y != -1:
				inference_rule.tracefun = y
			else:
				error_message = "No premise tracing function established for: " +\
												inference_rule.rulefile + " " + str(inference_rule.linenum)
				return error_message
		arglist = []
		H_spot_past = False
		for x in parsed_reference:
			if x[0].isdigit():
				arglist.append(["set()"]) # Presume any theorem has empty set of ancestors.
			elif x.startswith('.') and H_spot_past:
				arglist.append(["{'" + x + "'}"])
			elif x.startswith('.'):
				z = ancestors(proof, proof.note_num_consec[x],choice)
				if type(z) is str:
						return z
				else:
					arglist.append(z)
			elif x == 'H' and deduction_rule:
				H_spot_past = True
				arglist.append(x)
			else:
				arglist.append(x)
		tracefun = inference_rule.tracefun
		assert len(parsed_reference) == len(arglist)

		if 'H' in parsed_reference:
			H_spot_past = False
			stringlist = []
			for x in arglist:
				if x == 'H':
					H_spot_past = True
					stringlist.append('-')
				elif H_spot_past and x == ',':
					stringlist.append('-')
				elif x == ',':
					stringlist.append('&')
				else:
					stringlist.extend(x)
			return ['('] + stringlist + [ ')']
		stringlist = []
		for x in tracefun:
			if type(x) is int:
				stringlist.extend(arglist[x])
			else:
				stringlist.append(x)

		return stringlist 
	else:
		print("Not ready for multi-lined notes.")
		raise SystemExit
		chain = proof.notelist[c-1]
		for link in chain:
			parsed_reference = link[4]
			for x in parsed_reference:
				if x.startswith('.'):
					y = proof.note_num_consec[x]
					if y not in alreadyfound:
						alreadyfound.append(y)
						ancestors(proof, y,choice)

def check_premise_with_ref(proof,parsed_item,parsed_reference,infrule,takenvars):
	assert 'H' not in parsed_reference
	assert 'P' in parsed_reference
	premise = infrule.premise_list()
	h_spot = premise.index('H')
	givenlist = premise[h_spot +1:]
	hencelist = premise[:h_spot]
			
	instance_list = []
	p_spot = parsed_reference.index('P')
	for t in parsed_reference[p_spot + 1:]:
		if t[0].isdigit():
			instance_list.append(revar(doc.prop_ref[t][0], doc.prop_ref[t][1],takenvars))
		elif t[0] == '.':
			refnum = proof.note_num_consec[t]
			note = proof.notelist[refnum -1]
			instance_list.append(revar(proof.note_reflist[refnum-1],
										note.varlist,
					  				takenvars, proof.given_varlist[refnum]))
		else:
			instance_list.append(t)

	return parsed_item in hencelist and givenlist == instance_list


def search_with_ref(proof, goal,parsed_reference,
								takenvars,localvars,releasedvars=[]):
	if 'H' in parsed_reference:
		block_sub = synt.freshsub(releasedvars,takenvars)
	else:
		block_sub = []
	premises = []
	for t in parsed_reference: 
		if t[0] == '0' and t[1].isdigit() or t[-1].islower():
			premises.append(revar(prop.deep(doc.basic_ref[t][0]), doc.basic_ref[t][1], takenvars))
		elif t[0].isdigit():
			premises.append(revar(prop.deep(doc.prop_ref[t][0]), doc.prop_ref[t][1], takenvars))
		elif t[0] == '.':
			refnum = proof.note_num_consec[t]
			note_vars = proof.notelist[refnum-1].varlist 
			if block_sub:
				premise =  synt.subst(block_sub[0],
												block_sub[1], proof.note_reflist[refnum-1])
				premises.append(revar(premise, note_vars,
                            takenvars, proof.given_varlist[refnum]+ block_sub[0]))
			else:
				try:
					premise = proof.note_reflist[refnum -1]
				except:
					print("refnum - 1 ==============", refnum -1)
					raise SystemExit
				premises.append(revar(premise, note_vars,
                            takenvars, proof.given_varlist[refnum]))
	if proof.ref.isdigit():
		return search.search_rules(goal,premises,parsed_reference,takenvars,localvars,
										linemax=proof.linenum - 1, rules=rulelist, debug_flag=args.verbose)
	else:
		return search.search_rules(goal,premises,parsed_reference,takenvars,localvars,rules=rulelist,
							         debug_flag=args.verbose)

def revar(pexp,vars_pexp,takenvars,fixedvars = []):	
	s = synt.freshsub(vars_pexp,takenvars,fixedvars)
	return synt.subst(s[0],s[1],pexp)


def makeflags(linenumber):
	hasprop = syntdb[synt.MD_PROPGET]
#	syntdb[synt.MD_NRYPROP] = {}
	flags = {}
	for op in hasprop: 
		flag = 0
		if 'associative' in hasprop[op]:
			if hasprop[op]['associative'] == True or hasprop[op]['associative'] < linenumber:
				flag = flag | 16
		if 'commutative' in hasprop[op]:
			if hasprop[op]['commutative'] == True or hasprop[op]['commutative'] < linenumber:
				flag = flag | 8 
#		syntdb[synt.MD_NRYPROP][op] = flag
		prop.propflags[op] = flag
		flags[op] = flag
	return flags

def reportcheck(match, outfile):
	print("Checked with" , end = ' ', file = outfile)
	print(match.rulefile,":",  match.linenum, file = outfile)
	if args.inferencerules:
		print("\n",match.inferencerule.body, file = outfile)
	if args.caseflags:
		print("caseflags:",match.caseflags,"\n", file = outfile)
	else:
		print("\n", file = outfile)

#
################################################################
# 
#     Create the given-hence blocks 
#
################################################################
#

def prep(proof):

	proof.propflags = makeflags(proof.linenum)
	proof.given_vars = {}
	proof.given_vars[0] = [[],[]]
#
# given_vars[Consecutive Note Number of some Given Note] =
#        [ List of Non-bound vars contained in the Given Note,
#          List of variables Set in the scope of the Given Note]
# The following two lists work with the Consecutive Note Number c
# as an index and yield the variables and note numbers
# which are "given" for Note number c
# The given note numbers are also "consecutive" values of c.
	proof.given_varlist = ['placeholder']
	proof.given_numlist = ['placeholder']

	proof.used_notes = []
# used_notes is just an accumulator for all the note numbers that are
# referred to somewhere in the proof.


	if proof.shortproof:
		current_givennums = []
	else:
		current_givennums = [0]

	global_givens = []

	if proof.ref.isdigit():
		Line_num = int(proof.ref)
		Theorem_ref = None
	else:
		Theorem_ref = proof.ref 
		Line_num = None

	if Line_num: 
#
		raw_infrule = doc.infruledict[Line_num]
		infrule = prop.Rule(raw_infrule)
		for prem in infrule.body:
			if	prem in synt.reference_punctuator_list:
				continue
			for x in synt.nblist(prem):
				if x not in global_givens and x not in search.fixed_variables:
					global_givens.append(x)

	current_givenvars = global_givens[:]

	Note_list = proof.notelist
	c = 0
	for note in Note_list:
		c = c+1
#	note = Note_list[c-1]

		if note.single_line:
			if Line_num:
				if c == len(Note_list):
					note.parsed_item = prop.deep(infrule.body[-1])
				else:	
					parsed_item = prop.deep(note.parsed_item)
			else:	
					parsed_item = prop.deep(note.parsed_item)
			try:
				nb_varlist = synt.nblist(note.parsed_item)
			except:
				print("c ===", c)
				print(note.text_fragments)
				print(note.parsed_item)
				raise SystemExit
			if note.parsed_reference == 0:
				print("Bad reference in ", end = ' ')
				print("Note ", proof.note_nums[c-1][1:], end=' ')
				print("By ", note.raw_justification)
				raise SystemExit
	
			check_return = proof.checknoterefs(c,note.parsed_reference,current_givennums)

			noterefserror = ''
			parsed_reference = note.parsed_reference
			if 'D' in parsed_reference:
				noterefserror = "D not allowed in proofs"
			if 'A' in parsed_reference:
				noterefserror = "A not allowed in proofs"
			if c == len(Note_list) :
				current_givennums.remove(0)
				for z in proof.given_vars[0][1]:
					if z in nb_varlist:
						noterefserror = z +  " cannot be exported."
			if 'G' in parsed_reference:
				current_givennums.append(c)
#			nb = synt.nblist(parsed_item)
#				print("parse_item ====", parsed_item)
#				print("nb_varlist ====", nb_varlist)
				newvars = []
				for x in nb_varlist:
					if x not in current_givenvars:
						newvars.append(x)
				proof.given_vars[c] = [newvars,[]]
			elif 'P' in parsed_reference:
				if Theorem_ref:
					noterefserror = "P not allowed in proofs"
				elif parsed_reference == ['P']:
					pass
				elif 'H' not in infrule.premise_list():
					noterefserror = "Premise has no H!" 
				else:
					pass
			elif 'S' in parsed_reference:
				localtakenvars  = synt.varlist(parsed_item)
				for x in current_givenvars:
					if x not in localtakenvars:
						localtakenvars.append(x)
				wp = prop.deep(parsed_item)
				rule_match = search.search_rules(wp,[],['S'],
												localtakenvars,current_givenvars,rules=rulelist)
				if not rule_match:
					noterefserror = "Set statement not justified."
				else:
					if rule_match.rulesubst:
						rule_fixed_variables = []
						for var in rule_match.inferencerule.allvars:
							if var in search.fixed_variables:
								rule_fixed_variables.append(
									synt.subst(rule_match.rulesubst[0],
                             rule_match.rulesubst[1],var))
					else:
						rule_fixed_variables = search.fixed_variables
					for var in rule_match.subst:
						if rule_match.subst[var] in rule_fixed_variables:
							newvar = var
							lastnum = current_givennums[-1]
							proof.given_vars[lastnum][1].append(newvar)
							break
					else:
						noterefserror = "No fixed variable matched!"

			if 'H' in parsed_reference and \
										not noterefserror and not check_return: 
		# It is required that the conclusion
		# contains no occurrences of any variables
		# set subject to a given statement 
		# closed out by this hence statement.
				i = parsed_reference.index('H')

#			nb = synt.nblist(parsed_item)

				deleted_givennums = []
				for x in parsed_reference[i:]:
					if len(x) < 2 or x[0] != '.':
						pass
					else:
						y = proof.note_num_consec[x]
						if y in current_givennums:
							deleted_givennums.append(y)
							for z in proof.given_vars[y][1]:
								if z in nb_varlist:
									noterefserror =  z +  " cannot be exported"
						else:
							noterefserror = x + " does not refer to a given."
	
				while deleted_givennums:
					y = current_givennums[-1]
					if y in deleted_givennums:
						deleted_givennums.remove(y)	
						current_givennums.pop()
					else:
						noterefserror = " overlooked given note:" + proof.numlist[y-1][1:]
						break

			if not noterefserror:
				noterefserror = check_return
			note.error_message = noterefserror

			workparse = prop.deep(note.parsed_item)
			note.parsed_item = workparse
			proof.note_reflist.append(workparse)
#			proof.note_reflist.append(prop.negdeep(workparse))

		else:
			for link in note.chain:
				delta_goal = link.delta_goal
				sigma_goals = link.sigma_goals
				textfragments = link.textfragments
				raw_justification = link.raw_justification
				parsed_reference = link.parsed_reference
				if parsed_reference == 0:
					print("Bad reference")
					print("Note ", proof.numlist[c-1][1:], end=' ')
					print("By ", raw_justification)
					raise SystemExit
				for letter in ['H', 'G', 'S']:
					if letter in parsed_reference:
						noterefserror = letter + " not allowed in multi-line note"
						break
				else:
					noterefserror = proof.checknoterefs(c, parsed_reference,current_givennums)

				link.error_message = noterefserror

			if not link.sigma_goals :
				print(proof.ref)
				for link in note.chain:
					print(link.delta_goal)
			last_goal = link.sigma_goals[-1]	
			new_last_goal = prop.reduce(prop.deep(last_goal[0]),reductionlist)
			proof.note_reflist.append(new_last_goal)
			
		proof.given_numlist.append(current_givennums)

#  Make current_givenvars:

		current_givenvars = global_givens[:]
		for x in current_givennums:
			for y in proof.given_vars[x][0]:
				if y not in current_givenvars:
					current_givenvars.append(y)
			for y in proof.given_vars[x][1]:
				if y not in current_givenvars:
					current_givenvars.append(y)

		proof.given_varlist.append(current_givenvars)
		current_givennums = current_givennums[:]

	proof.outstanding_givens = current_givennums


#
################################################################
# 
# Check a proof 
#
################################################################
#

def check(proof, outfile = None):

	prop.propflags = proof.propflags

	if proof.ref.isdigit():
		Line_num = int(proof.ref)
		Theorem_ref = None
	else:
		Theorem_ref = proof.ref 
		Line_num = None

#
#  First just print the Theorem
#

	if Theorem_ref:
		print('',file = outfile)
		print("Theorem",Theorem_ref, end=' ',file=outfile)
	elif Line_num:
		print("Inference rule line", Line_num, ":",file = outfile)


	if Theorem_ref:
		reffed_item = doc.propdict[proof.ref] 
		if reffed_item:
			theorem = reffed_item[0]
			parsed_theorem = reffed_item[2]
		else:
			print(" failed to parse.",file = outfile)
			raise SystemExit
		print(theorem, file = outfile) 
		print('', file = outfile)
	elif Line_num:
		raw_infrule = doc.infruledict[Line_num]
		print(raw_infrule, file = outfile)
		try:
			infrule = prop.Rule(raw_infrule)
		except ValueError as e:
			print(" failed to parse: ",e, file = outfile)
			print("Error in ", filename, ", line: ", linenum, file = outfile)
			raise SystemExit
		
	Problem_found = False  # This variable records the final judgement on the proof.
#
# First check for outstanding givens
#
	if proof.outstanding_givens:
		if len(proof.outstanding_givens) == 1:
			print("Outstanding given: ", end=' ', file = outfile)
			print(proof.numlist[proof.outstanding_givens[0] - 1], file = outfile)
		else:
			print("Outstanding givens: ", end=' ', file = outfile)
			for x in proof.outstanding_givens:
				print(proof.numlist[x-1], end=' ', file = outfile)
			print('', file = outfile)
		print("Not ready for QED.", file = outfile)
		Problem_found = True

# 
#   Check a short proof 
#

	if proof.shortproof:  # In this case Note_list remains empty
		parsed_reference = proof.parsed_reference
		wkpth = prop.deep(parsed_theorem)
#		localvars = synt.varlist(wkpth)
		localvars = synt.nblist(wkpth)
		takenvars = localvars[:]
		print("By ", proof.raw_justification, file = outfile)
		if parsed_reference == 0:
			print("Bad reference", end=' ', file = outfile) 
			raise SystemExit
		elif 'D' in parsed_reference:
			print("Definitions not checked", file = outfile)
			raise SystemExit
		elif 'A' in parsed_reference:
			print("Axioms not checked", file = outfile)
			raise SystemExit
		elif 'P' in parsed_reference:
			print("P used in derivations only", file = outfile)
			raise SystemExit
		else:
			for letter in ['G','H','S']:
				if letter in parsed_reference:
					print(letter,"not allowed in a short proof", file = outfile)
					raise SystemExit

		match_found = search_with_ref(proof, wkpth,parsed_reference,takenvars,localvars)
		if match_found:
			reportcheck(match_found, outfile)
		else:
			print("Not checked.\n", file = outfile)
			Problem_found = True

#
#	   Check the steps of a proof with notes
#

	Note_list = proof.notelist
	c = 0
	for note in Note_list:
		c = c+1
#		note = Note_list[c-1]

		if note.single_line:  
			parsed_item = note.parsed_item
			if type(parsed_item) is list and parsed_item[0]in[10,11]:
				parsed_item = parsed_item[1]
			if c == len(Note_list) :
				print("QED By ", note.raw_justification, file = outfile)
			else:
				print("Note ", proof.numlist[c-1][1:], end=' ', file = outfile)
				for x in note.textfragments:
					print(x, end=' ', file = outfile)
				print('', file = outfile)
				print("By ", note.raw_justification, file = outfile)
	
			if note.error_message:
				print(note.error_message, file = outfile)
				print("Not checked.\n", file = outfile)
				Problem_found = True
			elif 'G' in note.parsed_reference:
				print('', file = outfile)
			elif 'P' in note.parsed_reference:
				deduction_thmp = 'H' in infrule.premise_list()
				if note.parsed_reference == ['P']:
					if deduction_thmp:
						print("P requires justified premises.", file = outfile)
						print("Not checked.\n", file = outfile)
						Problem_found = True
					elif parsed_item in infrule.premise_list(): 
						print("Checked \n", file = outfile)
					else:
						print("Is not a premise", file = outfile)
						print("Not checked.\n", file = outfile)
						Problem_found = True
				elif not deduction_thmp:
					print("P must be standalone here.", file = outfile)
					print("Not checked.\n", file = outfile)
					Problem_found = True
				elif check_premise_with_ref(proof,parsed_item,note.parsed_reference,
									 infrule,proof.given_varlist[c-1]):
					print("Checked ", file = outfile)
					print('', file = outfile)
				else:
					print("Premise does not justify the inference", file = outfile)
					print("Not checked.\n", file = outfile)
					Problem_found = True
			elif 'S' in note.parsed_reference:
				print('', file = outfile)
			else:
				released_vars = []	
				if 'H' in note.parsed_reference:
					for x in proof.given_varlist[c - 1]:
						if x not in proof.given_varlist[c] and x not in released_vars:
							released_vars.append(x)
	
				local_goal = parsed_item 
				nb_varlist = synt.nblist(local_goal)
#				nb_varlist = nb_local = synt.nblist(local_goal)
#				local_givens = nblocal + proof.given_varlist[c] 
				local_givens = []
				for y in nb_varlist + proof.given_varlist[c]: 
					if y not in local_givens:
						local_givens.append(y)
				localvars = note.varlist[:]
				for x in proof.given_varlist[c]:
					if x not in localvars:
						localvars.append(x)	
#				takenvars = localvars
				match_found = search_with_ref(proof, local_goal,
											note.parsed_reference,localvars,local_givens,released_vars)
				if match_found:
					reportcheck(match_found, outfile)
					if args.tracing:
						note.inference_rule = match_found
				else:
					print("Not checked.\n", file = outfile)
					Problem_found = True
		else:
			varlist = note.varlist
			chain = note.chain
			print("Note ", proof.numlist[c-1][1:], file = outfile)
			assert chain != []
	
			linenum = 0
			for link in chain:
				delta_goal = link.delta_goal
				sigma_goals = link.sigma_goals
				textfragments = link.textfragments
				raw_justification = link.raw_justification
				parsed_reference = link.parsed_reference
				error_message = link.error_message
			
				linenum = linenum + 1
				print("Line ", linenum ,'   ', end=' ', file = outfile)
				for j in range(len(textfragments)):
					if j != 0: print('                  ', end=' ', file = outfile)
					print(textfragments[j], end=' ', file = outfile)
				print('', file = outfile)
				print("By  ", raw_justification, file = outfile)

				if error_message:
					print(error_message, file = outfile)
					print("Not checked.", file = outfile)
					Problem_found = True
				else:
					deepstep = prop.reduce(prop.deep(delta_goal),reductionlist) 
#					deepstep = deep(delta_goal) 
					takenvars = varlist[:]
					local_givens = takenvars[:]
					for x in proof.given_varlist[c]:
						if x not in local_givens:
							local_givens.append(x)
						if x not in takenvars:
							takenvars.append(x)
				
					match_found = search_with_ref(proof, deepstep,
												parsed_reference,takenvars,local_givens)
					match_list = []
					for tran_inst in sigma_goals:
						transitivity_match = search_transitive_rules(tran_inst,
													      takenvars,local_givens)
						if not transitivity_match:
							transitivity_ok = False
							break
						match_list.append(transitivity_match)
					else:
						transitivity_ok = True

					if match_found and transitivity_ok:
						reportcheck(match_found, outfile)
						if args.multiline and transitivity_ok and match_list:
							print("Transitivity:", file = outfile)
							for match in match_list:
								reportcheck(match, outfile)
					elif match_found:
						reportcheck(match_found, outfile)
						print("But transitivity not checked.\n", file = outfile)
						Problem_found = True
					else:
						print("Not checked.", file = outfile)
						Problem_found = True
						print('', file = outfile) 

#	end-for note in Note_list!
	
	if args.tracing:
		trace_options = []
		c = 0
		for note in Note_list:
			c = c+1
			parsed_reference = note.parsed_reference
			if 'P' in parsed_reference:
				choice_item = []
				for p in parsed_reference[1::2]:
					choice_item.append([proof.numlist[c-1], p])
				if not choice_item:
					choice_item.append([proof.numlist[c-1], proof.numlist[c-1]])
				trace_options.append(choice_item)
		for trace_option in itertools.product(*trace_options):
			trace_choice = dict(trace_option)
			c = 0
			for note in Note_list:
				c = c+1
			#	note = Note_list[c-1]
				Relevance_check = True
				parsed_reference = note.parsed_reference
				if 'H' in parsed_reference:
					i = parsed_reference.index('H')
					premise_set = set(parsed_reference[i+1:])
					check_list = []
					for p in parsed_reference: 
						if p == 'H':
							break
						elif p == ',':
							continue
						elif type(p) is str and p[0] == '.':
							a = ancestors(proof, proof.note_num_consec[p], trace_choice)
							if type(a) is str:
								print(a, file = outfile)
								raise SystemExit
							check_list.extend(a)
							ancestor_set = eval("".join(a))
							used_premises = ancestor_set & premise_set
							if not used_premises:
								for v in premise_set:
									print("Premise: ", v, "not used by", p, file = outfile)
								Relevance_check = False
								if c == len(Note_list):
									print("Relevance check failed for QED.", file = outfile) 
								else:
									print("Relevance check failed for note", proof.numlist[c-1][1:], file = outfile)
								Problem_found = True
						else:  
							assert False
							pass
						
				if Relevance_check :
					print("Relevance check passed.", file = outfile)
				print('', file = outfile)

#	   end-for note in Note_list!

		if Line_num:
			y = ancestors(proof,c,[])
			if type(y) is list:
				ancestor_string_list = y 
			else:
				print(y, file = outfile) 
				ancestor_string_list = []
				Problem_found = True
			trace_list = []
			if 'H' in infrule.body:
				i = infrule.body.index('H')
				j = infrule.body.index('\\C')
				trace_list = ['(']
				for k in range(i):
					if infrule.body[k] == ',': 
						trace_list.append('&')
					else:
						trace_list.append(k)
				trace_list.append('-')
				for k in range(i + 1, j):
					if infrule.body[k] == ',': 
						trace_list.append('-')
					else:
						trace_list.append(k)
				trace_list.append(')')
				pass
			else:
				for x in ancestor_string_list:
					prancenotem = pattern.prancenote.match(x)
					if prancenotem: 
						notenum = prancenotem.group(1)
						y = proof.note_num_consec[notenum]
						try:
							trace_list.append(infrule.premise_list().index(Note_list[y-1].parsed_item))
						except:
							if 'G' in Note_list[y-1].parsed_reference:
								trace_list.append("set()")
							else:
								print(Note_list[y-1].parsed_item)
								print(Note_list[y-1].parsed_reference)
								raise SystemExit
					else:
						trace_list.append(x)
			if trace_list == []:
				print("No tracing function", file = outfile)
				raise SystemExit
			simplified_list = simplify_trace(trace_list)
			premise_trace_function[(filename, Line_num)] = simplified_list 

	if Problem_found:
		print("Proof Not checked.\n", file = outfile)
	else:
		print("Proof checked.\n", file = outfile)

	for t in proof.unused_notes:
			print("Unused note: ", t, file = outfile)

	return not Problem_found
#
# End of check()
#
#
# Fetch needed references
#
file_refkeys = set() 
needed_basic_refnums = set()
needed_prop_refnums = set()
if selectm:
	for ref in selected_list:
		needed_basic_refnums = needed_basic_refnums | doc.proofdict[ref].basic_refnums
		needed_prop_refnums = needed_prop_refnums | doc.proofdict[ref].prop_refnums
else:
	needed_basic_refnums = doc.proofdict[args.num].basic_refnums
	needed_prop_refnums = doc.proofdict[args.num].prop_refnums

for r in needed_basic_refnums:
	outfilerefm = pattern.outfileref.match(r)
	if r[0] == '0':
		file_refkeys.add('0')
	elif outfilerefm: 
		file_refkeys.add(outfilerefm.group(4))

missing_refnums = []
for r in needed_prop_refnums:
	p = doc.propdict.get(r,-1)
	if p == -1:
		missing_refnums.append(r)
	else:
		wp = p[2]
		doc.prop_ref[r] = [wp, synt.varlist(wp)]

if len(missing_refnums) == 1:
		print("Missing reference: ", missing_refnums[0])
		raise SystemExit
elif len(missing_refnums) > 1:
		print("Missing references: "+", ".join(missing_refnums)) 
		raise SystemExit

############################################################
#
#  Get references from external files
#
############################################################
#

for fk in file_refkeys:
	refnums = []
	for gk in needed_basic_refnums: 
		if fk == '0':
			if gk.startswith('0'):
				refnums.append(gk)
		else:
			outfilerefm = pattern.outfileref.match(gk)
			if outfilerefm and fk == outfilerefm.group(4):
				refnums.append(outfilerefm.group(1))
	fetchrefs(fk,refnums)

blackhole = Blackhole()

if selectm:
	n_proofs_checked = 0
	n_derivs_checked = 0
	for ref in selected_list:
		prep(doc.proofdict[ref])
	for ref in selected_list:
		thumbs = check(doc.proofdict[ref],outfile = blackhole)
		if thumbs:
			print(ref," checked")
			if ref.isdigit():
				n_derivs_checked = n_derivs_checked + 1
			else:
				n_proofs_checked = n_proofs_checked + 1
		else:
			print(ref)
	if n_derivs_checked > 1:
		print()
		print(n_derivs_checked, "derivations checked")
	elif n_derivs_checked == 1:
		print()
		print(n_derivs_checked, "derivation checked")
	if n_proofs_checked > 1:
		print()
		print(n_proofs_checked, "proofs checked")
	elif n_proofs_checked == 1:
		print()
		print(n_proofs_checked, "proof checked")
elif Theorem_ref:
	prep(doc.proofdict[Theorem_ref])
	check(doc.proofdict[Theorem_ref])

else:
	prep(doc.proofdict[str(Line_num)])
	check(doc.proofdict[str(Line_num)])

if args.tracing:
	f = open(Arg_1 + ".trc","wb")
	pickle.dump(premise_trace_function,f)
	f.close()
print()
