def trainSlopesSynced(gui, paramDict, userVertIs, coreVerts, bestScore):
	slopeStart, slopeStop, slopeStep = paramDict["slopeSyncedStart"], paramDict["slopeSyncedStop"], paramDict["slopeSyncedStep"]  #minSlopeOn
	rangeStart, rangeStop, rangeStep = paramDict["rangeSyncedStart"], paramDict["rangeSyncedStop"], paramDict["rangeSyncedStep"]  #slopeRangeOn

	#Tier5 = minimum on-bout slope
	for i1 in range(slopeStart, slopeStop, slopeStep):
		gui.minSlopeOnE.delete(0, "end")
		gui.minSlopeOffE.delete(0, "end")
		gui.minSlopeOnE.insert(0, (i1 * 0.01))
		gui.minSlopeOffE.insert(0, ((i1 * 0.01) * -1))
	
			
		#Tier7 = on-bout slope range
		for i2 in range(rangeStart, rangeStop, rangeStep):
			gui.slopeRangeOnE.delete(0, "end")
			gui.slopeRangeOffE.delete(0, "end")
			gui.slopeRangeOnE.insert(0, paramDict["rangeSyncedStart"])
			gui.slopeRangeOffE.insert(0, paramDict["rangeSyncedStart"])
		
			#Initialize vertex migration to off
			gui.vertMigrationCK.deselect()
			
			#Get refined vertices to expidite migration testing
			trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
			refinedVerts = coreVD.getRefinedVerts(gui, trainBlock)

			#Test vertex migration
			del trainBlock
			trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
			bestScore = testVertMigration(gui, userVertIs, refinedVerts, bestScore)
					
	return bestScore
def testVertMigration(gui, userVertIs, refinedVerts, bestScore):
	#Get score without vertex migration
	trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
	noMigScore = scoreAlgo(gui, userVertIs, trainBlock, bestScore, False, refinedVerts)
	
	#Bypasses vertex migration testing if difference from number of vertices is already greater than best score
	if noMigScore is "Void2" or noMigScore is "Void3":
		
		return bestScore

	#Turn on migration
	gui.vertMigrationCK.select()
	
	del trainBlock
	trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
	
	#Get score with vertex migration
	migScore = scoreAlgo(gui, userVertIs, trainBlock, bestScore, False, refinedVerts)
	
	
	# print("\nbestScore =", bestScore)
	# print("noMig =", noMigScore)
	# print("migScore =", migScore)
	
	#If both have valid score, take the best
	if isinstance(noMigScore, float) and isinstance(migScore, float):
		if noMigScore <= migScore:
			localBest = noMigScore
			gui.vertMigrationCK.deselect()
		else:
			localBest = migScore
	#If only noMig is valid score, take it
	elif isinstance(noMigScore, float):
		localBest = noMigScore
		gui.vertMigrationCK.deselect()
	#If only mig is valid score, take it
	elif isinstance(migScore, float):
		localBest = migScore
	#If neither are valid, return provided bestScore
	else:
		gui.vertMigrationCK.deselect()
		# print("neither valid")
		return bestScore
	
	# print("localBest =", localBest)
	
	#Update bestScore and save settings if better score encountered
	if localBest < bestScore:
		bestScore = localBest
		saveSettings(gui)

	gui.vertMigrationCK.deselect()
	return bestScore	
def trainDursSynced(gui, paramDict, userVertIs, bestScore):
	trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
	
	#Initialize range of values to be tested			
	start, stop, step = paramDict["durSyncedStart"], paramDict["durSyncedStop"], paramDict["durSyncedStep"]  #on/off-bout duration threshold
	
	#Tier1 = on/off-bout time threshold
	for i in range(start, stop, step):
		#Populate entry boxes
		gui.timeThreshAdvOnE.delete(0, "end")
		gui.timeThreshAdvOffE.delete(0, "end")
		gui.timeThreshAdvOnE.insert(0, i)
		gui.timeThreshAdvOffE.insert(0, i)

		
		#Initialize downstream parameters to 0
		gui.tempThreshAdvOnE.delete(0, "end")
		gui.tempThreshAdvOffE.delete(0, "end")
		gui.minSlopeOnE.delete(0, "end")
		gui.minSlopeOffE.delete(0, "end")
		gui.slopeRangeOnE.delete(0, "end")
		gui.slopeRangeOffE.delete(0, "end")
		gui.tempThreshAdvOnE.insert(0, 0)
		gui.tempThreshAdvOffE.insert(0, 0)
		gui.minSlopeOnE.insert(0, 0)
		gui.minSlopeOffE.insert(0, 0)
		gui.slopeRangeOnE.insert(0, paramDict["rangeSyncedStart"])
		gui.slopeRangeOffE.insert(0, paramDict["rangeSyncedStart"])
		
		#Initialize vertex migration to off
		gui.vertMigrationCK.deselect()
		
		coreVerts = coreVD.getCoreVerts(gui, trainBlock)
		
		
		#Bypasses further testing if penalty from too few vertices (too restrictive of settings) is already greater than best score
		if scoreAlgo(gui, userVertIs, trainBlock, bestScore) is not "Void2":
			if gui.decoupleTempsBV.get():
				#Test downstream parameters with this combination of duration thresholds
				bestScore = trainOnTemp(gui, paramDict, userVertIs, coreVerts, bestScore)
			else:
				del trainBlock
				trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
				
				trainTempsSynced(gui, paramDict, userVertIs, coreVerts, bestScore)
		else:
			# print("break at sync dur")
			break
				
	return bestScore
def trainTempsSynced(gui, paramDict, userVertIs, coreVerts, bestScore):
	start, stop, step = paramDict["tempSyncedStart"], paramDict["tempSyncedStop"], paramDict["tempSyncedStep"]  #tempThreshOn	
	
	trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
	
	#On/off-bout temperature threshold
	for i in range(start, stop, step):
		#Populate entry boxes
		gui.tempThreshAdvOnE.delete(0, "end")
		gui.tempThreshAdvOffE.delete(0, "end")
		gui.tempThreshAdvOnE.insert(0, (i * 0.1))
		gui.tempThreshAdvOffE.insert(0, (i * 0.1))
		
		#Initialize downstream parameters to 0
		gui.minSlopeOnE.delete(0, "end")
		gui.minSlopeOffE.delete(0, "end")
		gui.slopeRangeOnE.delete(0, "end")
		gui.slopeRangeOffE.delete(0, "end")
		gui.minSlopeOnE.insert(0, 0)
		gui.minSlopeOffE.insert(0, 0)
		gui.slopeRangeOnE.insert(0, paramDict["rangeSyncedStart"])
		gui.slopeRangeOffE.insert(0, paramDict["rangeSyncedStart"])
		
		#Initialize vertex migration to off
		gui.vertMigrationCK.deselect()
		
		
		if scoreAlgo(gui, userVertIs, trainBlock, bestScore, coreVerts) is not "Void2":
			if gui.trainSlopeBV.get():
				if gui.decoupleSlopeBV.get():
					bestScore = trainOnSlope(gui, paramDict, userVertIs, coreVerts, bestScore)
				else:
					bestScore = trainSlopesSynced(gui, paramDict, userVertIs, coreVerts, bestScore)
			else:
				#Get refined vertices to expidite migration testing
				del trainBlock
				trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
				refinedVerts = coreVD.getRefinedVerts(gui, trainBlock)

				#Test vertex migration
				del trainBlock
				trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
				bestScore = testVertMigration(gui, userVertIs, refinedVerts, bestScore)
		else:
			# print("break at onTemp")
			break
				
	return bestScore
def trainOnTemp(gui, paramDict, userVertIs, coreVerts, bestScore):
	start, stop, step = paramDict["onTempStart"], paramDict["onTempStop"], paramDict["onTempStep"]  #temperThreshOn	
	
	trainBlock = coreC.block(gui, 0, (len(gui.masterList) - 1), False)
	
	#Tier3 = on-bout temperature threshold
	for i in range(start, stop, step):
		#Populate entry boxes
		gui.tempThreshAdvOnE.delete(0, "end")
		gui.tempThreshAdvOnE.insert(0, (i * 0.1))
		
		gui.tempThreshAdvOffE.delete(0, "end")
		gui.minSlopeOnE.delete(0, "end")
		gui.minSlopeOffE.delete(0, "end")
		gui.slopeRangeOnE.delete(0, "end")
		gui.slopeRangeOffE.delete(0, "end")
		gui.tempThreshAdvOffE.insert(0, 0)
		gui.minSlopeOnE.insert(0, 0)
		gui.minSlopeOffE.insert(0, 0)
		gui.slopeRangeOnE.insert(0, paramDict["rangeSyncedStart"])
		gui.slopeRangeOffE.insert(0, paramDict["rangeSyncedStart"])
		
		#Initialize vertex migration to off
		gui.vertMigrationCK.deselect()
		
		
		if scoreAlgo(gui, userVertIs, trainBlock, bestScore, coreVerts) is not "Void2":
			#Test downstream parameters with these upstream parameters
			bestScore = trainOffTemp(gui, paramDict, userVertIs, coreVerts, bestScore)
		else:
			# print("break at onTemp")
			break
				
	return bestScore
def getPairs(gui, daysList, nightsList, pairsList):
	#Set modifier based on if day or night comes first
	if daysList[0].start > nightsList[0].start:
		modifier = 1
	else:
		modifier = 0
			
	for i in range(len(daysList)):
		#If there is a night corrisponding with the day at index i
		if (i + modifier) < (len(nightsList)):
			#Create pair if both daytime and nightime are complete
			if not daysList[i].partial and not nightsList[i + modifier].partial:
				pairsList.append(coreC.block(gui, daysList[i].start, nightsList[i + modifier].stop, False))
def splitDays(gui, daysList, nightsList, modifier = 0):
	#Increment time by modifier value
	def modTime(oriTime):	
		if modifier is 0:
			newTime = (" " + oriTime.strip())
		else:
			#Convert csv time value to datetime format
			search = re.search("((\d+):(\d+))", oriTime)
			hour = int(search.group(2))
			minute = int(search.group(3))
			time = datetime.datetime(1, 1, 1, hour, minute, 0)
			
			#Add modifer and strip unnecessary info
			newTime = (time + datetime.timedelta(minutes = modifier))
			newTime = str(newTime)[11:-3]
			if newTime[0] is "0":
				newTime = newTime[1:]		
		
		#Return modified time
		return(" " + newTime.strip())


	#Set to False when end of list is encountered
	dayValid      = True
	dayStart = modTime(gui.dayStart)
	nightStart = modTime(gui.nightStart)
	dayDur        = getDayDur(dayStart, nightStart)
	nightDur      = (1440 - dayDur)
	dayInterval   = (1440 / gui.interval)
	# cur = current
	curDay        = -1
	curNight      = -1
	
	
	
	#Look for first day or night
	for i in range(0, len(gui.masterList)):
		#If dayStart found before nightStart
		if re.search(dayStart, gui.masterList[i][gui.dateTimesCol]):
			#Set start of first day to this index
			curDay = i
			#Set start of first night to day index + duration of daytime
			curNight = (i + (dayDur / gui.interval))
			#Check if this sets curNight past length of gui.masterList
			if curNight > (len(gui.masterList) - 1):
				dayValid = False
				curNight = (len(gui.masterList) - 1)
				daysList.append(coreC.block(gui, curDay, curNight - 1, True))
				
			break
		#If nightStart found before dayStart
		elif re.search(nightStart, gui.masterList[i][gui.dateTimesCol]):
			#Set start of first night to this index
			curNight = i
			#Set start of first day to night index + duration of nighttime
			curDay = (i + (nightDur / gui.interval))
			#Check if this sets curDay past length of gui.masterList
			if curDay > (len(gui.masterList) - 1):
				dayValid = False
				curDay = (len(gui.masterList) - 1)
				
			break
	
	#Check if data starts at night and process to achieve uniformity going into following while `
	#Catch partial day at start of gui.masterList
	if curNight < curDay:
		daysList.append(coreC.block(gui, 0, curNight - 1, True))
		nightsList.append(coreC.block(gui, curNight, curDay - 1, not dayValid))
		curNight += dayInterval
	#Catch partial night at start of gui.masterList
	elif curDay < curNight:
		nightsList.append(coreC.block(gui, 0, curDay - 1, True))
	#If neither dayStart or nightStart found, append partial day or night
	elif curDay == curNight:
		dayValid = False
		if checkDaytime(dayStart, nightStart, gui.masterList[0][gui.dateTimesCol]):
			daysList.append(coreC.block(gui, 0, (len(gui.masterList) - 1), True))
		else:
			nightsList.append(coreC.block(gui, 0, (len(gui.masterList) - 1), True))
			
	#Save each day and night as object
	while dayValid:
			daysList.append(coreC.block(gui, curDay, curNight - 1, False))
			curDay += dayInterval
			
			#Make final night stop at end of gui.masterList
			if curDay > len(gui.masterList):
				curDay = (len(gui.masterList) - 1)
				nightsList.append(coreC.block(gui, curNight, curDay - 1, True))
				dayValid = False
				break
			else:
				nightsList.append(coreC.block(gui, curNight, curDay - 1, False))
				curNight += dayInterval
			
			#Make final day stop at end of gui.masterList
			if curNight > len(gui.masterList):
				curNight = (len(gui.masterList) - 1)
				daysList.append(coreC.block(gui, curDay, curNight - 1, True))
				dayValid = False
	
	#Address problem of start time skipping
	if len(daysList) is 0 or len(nightsList) is 0:
		if (modifier + 1) < gui.interval:
			#Clear lists
			daysList.clear()
			nightsList.clear()
			#Recursively call splitDays with incremented modifier
			splitDays(gui, daysList, nightsList, (modifier + 1))
		#If no days or nights still found, provide text warning
		else:
			messagebox.showwarning("Warning", 
			("If daytime or nighttime periods are not being properly detected, make sure the Data Time Interval provided on the Main tab is correct."))