def streamMethod3(self):
		chunkIndex = 1
		manifest = Manifest("BunnyManifest_1.xml")
		bitrates = manifest.listOfBitrates()

                # Compute switch-up epsilon
                epsilon = self.computeEpsilon(bitrates)
                
		#start with lowest bitrate
		bitrateIndex = 0
		numChunks = 99 #manifest.numberOfChunks(bitrates[bitrateIndex])
		idleThreadIDs = Set()
		startNewRound = False

		#threadBandwidth = {}
		for thread in self.threads:
			if thread.successful:
				idleThreadIDs.add(thread.threadID)
				#threadBandwidth[thread.threadID] = 0

		if len(idleThreadIDs) == NUM_THREADS:
			startNewRound = True
		finished = False

		bitrateFreq = {}
		for bitrate in bitrates:
			bitrateFreq[bitrate] = 0

		# Start Player
		p = Player(self.buffer)
		p.start()

		while not finished:
			for thread in self.threads:
				if not thread.isAlive():
					# Check if we have gotten all the chunks
					# If so, we are done

					if chunkIndex == numChunks + 1:
						finished = True
						break

					# if thread finishes downloading, look for more work
					if not startNewRound and thread.successful:
						minProgress = None
						work = None
						videoURL = None
						worstThread = None
						canHelpNotHelping = False
						for currThread in self.threads:
							if (currThread.downloadList[0][1] is 0 or currThread.downloadList[0][1] is None) and currThread.isAlive():
								canHelpNotHelping = True
								break
						for currThread in self.threads:
							if currThread.isAlive() and (not canHelpNotHelping or (canHelpNotHelping and (currThread.downloadList[0][1] is 0 or currThread.downloadList[0][1] is None))):
								size = self.buffer.getFileSize(currThread.bufferIndices[0], currThread.downloadList[0][1], currThread.downloadList[0][2])
							
								# If -1 returned, don't want to help out; just skip
								if size == -1:
								    continue	    

								progress = float(size) / currThread.downloadSize
								if minProgress is None or progress < minProgress:
									minProgress = progress
									work = (currThread.downloadSize - size) / 2
									videoURL = currThread.downloadList[0][0]
									worstThread = currThread


						# if all threads finish or further work is unnecessary, start new round of downloads
						if minProgress is None or work < 15000:
							idleThreadIDs.add(thread.threadID)
							if len(idleThreadIDs) == NUM_THREADS:
								startNewRound = True
						else:
							#split work
							ID = thread.threadID
							print "splitting: " + str(ID)
							# range request for new thread
							videoURL = serverURLs[thread.threadID] + re.findall("[0-9]/.*", videoURL)[0][1:]
							size = self.buffer.getFileSize(worstThread.bufferIndices[0], worstThread.downloadList[0][1], worstThread.downloadList[0][2])
							# calculate new thread's range
							if worstThread.downloadList[0][1] is None:
								endByte = worstThread.downloadSize - 1
								beginByte = size + ((endByte - size) / 2)
								#print "None Case"
								#print "(begin, end)", beginByte, endByte

							else:	
								endByte = worstThread.downloadList[0][2]
								beginByte = worstThread.downloadList[0][1] + size + ((endByte - (worstThread.downloadList[0][1] + size)) / 2)
								#print "Other Case"
								#print "(begin, end)", beginByte, endByte
							downloadSize = endByte - beginByte + 1
							# reduce byte range of helped thread
							worstThread.downloadSize -= downloadSize
							oldBegin = worstThread.downloadList[0][1]
							oldEnd = worstThread.downloadList[0][2]
							if oldBegin == None:
								newBegin = 0
							else:
								newBegin = oldBegin
							newEnd = beginByte - 1
							worstThread.downloadList[0][1] = newBegin
							worstThread.downloadList[0][2] = newEnd
							index = worstThread.bufferIndices[0]
							self.buffer.rewriteBeginAndEnd(index, self.buffer.read(index, oldBegin, oldEnd), oldBegin, oldEnd, newBegin, newEnd)
							# notify helped thread that its byte range is reduced
							worstThread.reduced = True
							if ID in idleThreadIDs:
								idleThreadIDs.remove(ID)
							self.threads[ID] = Downloader(ID, [[videoURL, beginByte, endByte]], self.buffer, [index], thread.bandwidth, downloadSize, thread.connectionBeginTime)
							self.threads[ID].start()
							while self.buffer.read(index, beginByte, endByte) is "":
								pass

					# start new round
					if startNewRound and self.buffer.size - self.buffer.length >= NUM_THREADS:
						# bitrate adaptation based on bandwidth
						# do not do for first round
						if chunkIndex != 1:
							time = 0
							min = None
							max = None
							for currThread in self.threads:
								if min == None or currThread.connectionBeginTime < min:
									min = currThread.connectionBeginTime
								if max == None or currThread.endTime > max:
									max = currThread.endTime
							deltaT = max - min
							time = deltaT.microseconds + deltaT.seconds * 1000000

							u = float(CHUNK_DURATION * NUM_THREADS) / time
							print time				      
							print u
							if u > 1 + epsilon:
								if bitrateIndex != len(bitrates) - 1:
									bitrateIndex += 1
							elif u < 1:
								if bitrateIndex != 0:
									bitrateIndex -= 1

							#threadBandwidth[thread.threadID] += u

						# threads have been assigned to download
						idleThreadIDs = Set()
						# starting of new round finishes when all threads are downloading
						startNewRound = False
						# Get threadID's and their threadInfo

						threadInfo = []
						for currThread in self.threads:
							threadInfo.append([currThread.threadID, currThread.bandwidth])

						# sort threadInfo so that higher bandwidth connections get assigned
						# to earlier byte ranges
						threadInfo = sorted(threadInfo, key=lambda list: list[1], reverse = True)
						for info in threadInfo:
							if chunkIndex == numChunks + 1:
								break
							ID = info[0]
							currThread = self.threads[ID]
							# Get url from manifest file based on bitrate and chunk index							   
							URLend, chunkSize = manifest.read(bitrates[bitrateIndex], chunkIndex)
							videoURL = serverURLs[ID] + "/" + URLend 
							self.threads[ID] = Downloader(ID, [[videoURL, None, None]], self.buffer, [chunkIndex], currThread.bandwidth, chunkSize)
							self.threads[ID].start()
							self.buffer.setSize(chunkIndex, chunkSize)

							chunkIndex += 1
							bitrateFreq[bitrates[bitrateIndex]] += 1
							self.buffer.length += 1

						# at every round, plot figure using existing information
						statBitrateFreq = {}
						statThreadBandwidth = {}

						#print "Chunk Index", chunkIndex
						#print bitrateFreq
						#print threadBandwidth
						for key in bitrateFreq.keys():
							value = bitrateFreq[key]
							statBitrateFreq[key] = (float(value)/chunkIndex) * 100


						#for key in threadBandwidth.keys():
						#    value = threadBandwidth[key]
						#    statThreadBandwidth[key] = value/chunkIndex

						self.plot(statBitrateFreq, "Our Method")
	def streamMethod1(self):
		chunkIndex = 1
		manifest = Manifest("BunnyManifest_1.xml")
		bitrates = manifest.listOfBitrates()

		# Compute switch-up epsilon
		epsilon = self.computeEpsilon(bitrates)
		
		#start with lowest bitrate
		bitrateIndex = 0
		numChunks = 99 #manifest.numberOfChunks(bitrates[bitrateIndex])
		idleThreadIDs = Set()
		startNewRound = False

		#threadBandwidth = {}
		for thread in self.threads:
			if thread.successful:
				idleThreadIDs.add(thread.threadID)
				#threadBandwidth[thread.threadID] = 0

		if len(idleThreadIDs) == NUM_THREADS:
			startNewRound = True
		finished = False

		# Used for recording bitrate frequences for graphing
		bitrateFreq = {}
		for bitrate in bitrates:
			bitrateFreq[bitrate] = 0

		# Start Player
		p = Player(self.buffer)
		p.start()

		while not finished:
			for thread in self.threads:
				if not thread.isAlive():
					# Check if we have gotten all the chunks
					# If so, we are done

					if chunkIndex == numChunks + 1:
						finished = True
						break

					# if threads finish after round of downloads started, record their IDs
					if not startNewRound and thread.successful:
						idleThreadIDs.add(thread.threadID)

						# if all threads finish, start new round of downloads
						if len(idleThreadIDs) == NUM_THREADS:
							startNewRound = True

					# start new round
					if startNewRound and self.buffer.size - self.buffer.length >= NUM_THREADS:
						# bitrate adaptation based on bandwidth
						# do not do for first round
						if chunkIndex != 1:
							time = 0
							min = None
							max = None
							for currThread in self.threads:
								if min == None or currThread.beginTime < min:
									min = currThread.beginTime
								if max == None or currThread.endTime > max:
									max = currThread.endTime
							deltaT = max - min
							time = deltaT.microseconds + deltaT.seconds * 1000000

							u = float(CHUNK_DURATION * NUM_THREADS) / time
							print time				      
							print u
							if u > 1 + epsilon:
								if bitrateIndex != len(bitrates) - 1:
									bitrateIndex += 1
							elif u < 1:
								if bitrateIndex != 0:
									bitrateIndex -= 1

							#threadBandwidth[thread.threadID] += u

						# threads have been assigned to download
						idleThreadIDs = Set()
						# starting of new round finishes when all threads are downloading
						startNewRound = False

						# Request urls from server
						for currThread in self.threads:
							ID = currThread.threadID
							# Get url from manifest file based on bitrate and chunk index							   
							videoURL, chunkSize = manifest.read(bitrates[bitrateIndex], chunkIndex)
							videoURL = serverURLs[ID] + "/" + videoURL
							bitrateFreq[bitrates[bitrateIndex]] += 1

							self.buffer.setSize(chunkIndex, chunkSize)
							self.threads[ID] = Downloader(ID, [[videoURL, None, None]], self.buffer, [chunkIndex])
							self.threads[ID].start()

							chunkIndex += 1
							self.buffer.length += 1
						# at every round, plot figure using existing information
						statBitrateFreq = {}
						statThreadBandwidth = {}

						# Convert to percentage and graph 
						for key in bitrateFreq.keys():
							value = bitrateFreq[key]
							statBitrateFreq[key] = (float(value)/chunkIndex) * 100


						self.plot(statBitrateFreq, "Naive Parallel")
	def streamMethod2(self):
		chunkIndex = 1
		redoTime = 0
		manifest = Manifest("BunnyManifest_1.xml")
		bitrates = manifest.listOfBitrates()

                # Compute switch-up epsilon
                epsilon = self.computeEpsilon(bitrates)
                

		#start with lowest bitrate
		bitrateIndex = 0
		numChunks = 99 #manifest.numberOfChunks(bitrates[bitrateIndex])
		idleThreadIDs = Set()
		startNewRound = False

		#threadBandwidth = {}
		for thread in self.threads:
			if thread.successful:
				idleThreadIDs.add(thread.threadID)
				#threadBandwidth[thread.threadID] = 0

		if len(idleThreadIDs) == NUM_THREADS:
			startNewRound = True
		finished = False

		bitrateFreq = {}
		for bitrate in bitrates:
			bitrateFreq[bitrate] = 0

		# Start Player
		#p = Player(self.buffer)
		#p.start()

		while not finished:
			for thread in self.threads:
				if not thread.isAlive():
					# Check if we have gotten all the chunks
					# If so, we are done

					if chunkIndex == numChunks + 1:
						finished = True
						break

					# if threads finish after round of downloads started, record their IDs
					if not startNewRound and thread.successful:
						idleThreadIDs.add(thread.threadID)

						# if all threads finish, start new round of downloads
						if len(idleThreadIDs) == NUM_THREADS:
							startNewRound = True
						else:
							# if finished downloading file first, stop duplicate downloads
							for currThread in self.threads:
								if currThread.isAlive():
									# just check if first URL is the same
									videoURL = currThread.downloadList[0][0]
									videoURL = serverURLs[thread.threadID] + re.findall("[0-9]/.*", videoURL)[0][1:]
									if [videoURL, currThread.downloadList[0][1], currThread.downloadList[0][2]] == thread.downloadList[0]:
										currThread.downloadSize = -1
							# check if its time to start redoing work
							timePassed = datetime.now() - thread.beginTime
							if timePassed.seconds >= redoTime:
								for currThread in self.threads:
									totalDownloaded = 0
									totalSize = 0
									if currThread.isAlive() and not currThread.downloadSize is not -1:
										# check progress to see if thread's work is worth redoing
										for index, download in zip(currThread.bufferIndices, currThread.downloadList):
											# check full file size
											if download[1] is None:
												size = manifest.readSize(bitrates[bitrateIndex], index)
											else:
												size = download[2] - download[1] + 1
											totalSize += size
											# check downloaded size
											downloaded = self.buffer.getFileSize(index, download[1], download[2])
											if downloaded == -1:
												downloaded = size
											totalDownloaded += downloaded
										work = totalSize - totalDownloaded	
										if work > 500:
											# make a modified copy of downloads for redoing work
											downloadList = []
											for download in currThread.downloadList:
												videoURL = download[0]
												videoURL = serverURLs[thread.threadID] + re.findall("[0-9]/.*", videoURL)[0][1:]
												downloadList.append([videoURL, download[1], download[2]])
											self.threads[thread.threadID] = Downloader(thread.threadID, downloadList, self.buffer, 
													currThread.bufferIndices, thread.bandwidth, None, thread.connectionBeginTime, True)
											self.threads[thread.threadID].start()
											idleThreadIDs.remove(thread.threadID)
											while self.threads[thread.threadID].beginTime is None:
												pass
											break
					# start new round
					if startNewRound and self.buffer.size - self.buffer.length >= NUM_THREADS:
						# bitrate adaptation based on bandwidth
						if chunkIndex != 1:
							time = 0
							min = None
							max = None
							for currThread in self.threads:
								if min == None or currThread.connectionBeginTime < min:
									min = currThread.connectionBeginTime
								if max == None or currThread.endTime > max:
									max = currThread.endTime
							deltaT = max - min
							time = deltaT.microseconds + deltaT.seconds * 1000000

							u = float(CHUNK_DURATION * NUM_THREADS) / time
							print time				      
							print u
							if u > 1 + epsilon:
								if bitrateIndex != len(bitrates) - 1:
									bitrateIndex += 1
							elif u < 1:
								if bitrateIndex != 0:
									bitrateIndex -= 1

							#threadBandwidth[thread.threadID] += u

						# Get URLS and chunk sizes from manifest file based on bitrate and chunk index
						chunks = []
						totalSize = 0
						for i in range(NUM_THREADS):
							if chunkIndex == numChunks + 1:
								break
							URLend, chunkSize = manifest.read(bitrates[bitrateIndex], chunkIndex + i)
							URLend = "/" + URLend
							self.buffer.setSize(chunkIndex + i, chunkSize)
							chunks.append([URLend, 0, chunkSize - 1, chunkIndex + i])
							totalSize += chunkSize

						# Get threadID's and their threadInfo
						threadInfo = []
						totalBandwidth = 0
						for currThread in self.threads:
							totalBandwidth += currThread.bandwidth
							threadInfo.append([currThread.threadID, currThread.bandwidth])

						# sort threadInfo so that higher bandwidth connections get assigned
						# to earlier byte ranges
						threadInfo = sorted(threadInfo, key=lambda list: list[1], reverse = True)
						for info in threadInfo:
							currThread = self.threads[info[0]]
							currThread.downloadList = []
							currThread.bufferIndices = []
							fraction = float(currThread.bandwidth) / totalBandwidth
							allocation = int(totalSize * fraction)
							print str(info[0]) + " downloading: " + str(allocation) + " " + str(fraction)
							for chunk in chunks:
								if chunk[1] == chunk[2] + 1:
									continue
								elif allocation == 0:
									break
								# due to possible imprecision errors with calculating allocation, 
								# check to have last thread download remains of last chunk 
								if info == threadInfo[len(threadInfo) - 1] and chunk == chunks[len(chunks) - 1]:
									if chunk[1] == 0:
										currThread.downloadList.append((serverURLs[currThread.threadID] + chunk[0],
											None, None))
									else:
										currThread.downloadList.append((serverURLs[currThread.threadID] + chunk[0],
											chunk[1], chunk[2]))
									allocation = 0
									chunk[1] = chunk[2] + 1
									currThread.bufferIndices.append(chunk[3]) 
								elif allocation - (chunk[2] - chunk[1] + 1) >= 0:
									# assign thread to download rest of chunk
									if chunk[1] == 0:
										currThread.downloadList.append((serverURLs[currThread.threadID] + chunk[0],
											None, None))
									else:
										currThread.downloadList.append((serverURLs[currThread.threadID] + chunk[0],
											chunk[1], chunk[2]))
									allocation -= (chunk[2] - chunk[1] + 1)
									chunk[1] = chunk[2] + 1
									currThread.bufferIndices.append(chunk[3])
								else:
									# assign thread to download part of chunk
									# to finish its allocation
									currThread.downloadList.append((serverURLs[currThread.threadID] + chunk[0],
										chunk[1], chunk[1] + allocation - 1))
									chunk[1] += allocation
									allocation = 0
									currThread.bufferIndices.append(chunk[3])
						
						# Keep track of bitrate for graphing purposes
						bitrateFreq[bitrates[bitrateIndex]] += NUM_THREADS

						# all threads have been assigned to download
						idleThreadIDs = Set() 
						startNewRound = False

						# Request urls from server
						for currThread in self.threads:
							ID = currThread.threadID
							self.threads[ID] = Downloader(ID, currThread.downloadList, self.buffer, currThread.bufferIndices, currThread.bandwidth)
							self.threads[ID].start()

						self.buffer.length += NUM_THREADS 
						chunkIndex += NUM_THREADS

						# at every round, plot figure using existing information"
						statBitrateFreq = {}
						statThreadBandwidth = {}

						#print "Chunk Index", chunkIndex
						#print bitrateFreq
						#print threadBandwidth
						for key in bitrateFreq.keys():
							value = bitrateFreq[key]
							statBitrateFreq[key] = (float(value)/chunkIndex) * 100


						#for key in threadBandwidth.keys():
						#    value = threadBandwidth[key]
						#    statThreadBandwidth[key] = value/chunkIndex

						self.plot(statBitrateFreq, "Pure Byte Range Method")