"""
Account is a program to generate a devcoin receiver file from a bitcoinshare, bounty, devcoinshare and peer file.

This is meant to be used by devcoin accountants and auditors to create and check the receiver files.  The account file has a list of addresses and shares.  Anything after a dash is a comment.


==Commands==
===Help===
The -h option, the -help option, will print the help, which is this document.  The example follows:
python account.py -h

===Input===
Default is https://raw.github.com/Unthinkingbit/charity/master/account_3.csv

The -input option sets the input file name.  The example follows:
python account.py -input https://raw.github.com/Unthinkingbit/charity/master/account_3.csv

An example of an account information input file is at:
https://raw.github.com/Unthinkingbit/charity/master/account_3.csv

===Output===
Default is test_receiver.csv

The -output option sets the output.  If the output ends with stderr, the output will be sent to stderr  If the output ends with stdout, the output will be sent to stdout.  If the output does not end with stderr or stdout, the output will be written to a file by that name, with whatever suffix the input file has.  The example follows:
python genereceiver.py -output test_receiver.csv

An example of an genereceiver output file is at:
https://raw.github.com/Unthinkingbit/charity/master/test_receiver_3.csv


==Install==
For genereceiver to run, you need Python 2.x, almoner will probably not run with python 3.x.  To check if it is on your machine, in a terminal type:
python

If python 2.x is not on your machine, download the latest python 2.x, which is available from:
http://www.python.org/download/
"""

import almoner
import base58
import cStringIO
import hashlib
import math
import sys


__license__ = 'MIT'


globalGoldenRatio = math.sqrt(1.25) + 0.5


def addAdministratorBonus(accountLines):
	'Add the administrator bonus, up to a maximum of 15%.'
	originalReceiverLines = getReceiverLinesByAccountLines(accountLines)
	originalNumberOfLinesFloat = float(len(originalReceiverLines))
	administrators = []
	for accountLine in accountLines:
		if 'Administrator' in accountLine:
			administrators.append(Administrator(accountLine))
	administratorPay = 0.0
	generalAdministrators = []
	factotumCount = 0
	for administrator in administrators:
		administratorPay += administrator.pay
		if administrator.isGeneralAdministrator:
			generalAdministrators.append(administrator)
		if administrator.isFactotum:
			factotumCount += 1
	for bonusMultiplier in xrange(7, 0, -1):
		bonusPay = bonusMultiplier * float(len(generalAdministrators) + factotumCount)
		totalAdministratorPay = bonusPay + administratorPay
		totalShares = originalNumberOfLinesFloat + bonusPay
		percentPay = 0.1 * round(1000.0 * totalAdministratorPay / totalShares)
		if percentPay < 15.0:
			accountLines.append('Administrator Bonus' + ': %s Shares' % int(round(bonusPay)))
			for generalAdministrator in generalAdministrators:
				accountLines.append(generalAdministrator.getAccountLine(bonusMultiplier))
			accountLines.append('')
			return

def addReceiverLines(coinAddresses, receiverLines):
	'Get the receiver format line.'
	if len(coinAddresses) == 0:
		return
	addressQuantityDictionary = getQuantityDictionary(coinAddresses)
	firstQuantity = addressQuantityDictionary.values()[0]
	for addressQuantity in addressQuantityDictionary.values():
		if addressQuantity != firstQuantity:
			receiverLines.append(','.join(coinAddresses))
			return
	receiverLines.append(','.join(addressQuantityDictionary.keys()))

def carryCoinAddresses(denominatorSequences):
	"""
	Sometimes there number of identical coin addresses in the denominator sequence is as high as the denominator. To condense the receiver file,
	if the number of copies of the coin address is as high as the denominator, coin addresses are carried to the sequence with the smaller
	denominator.
	
	To do this, the sequences to be carried into, which are sorted in ascending value of the denominator, are iterated downward from
	the length minus two, to zero. The quantity of each coin address in the sequence with the big denominator is entered into a
	dictionary. For	each coin address, the carry is calculated by dividing quantity of the coin address by the denominator ratio between the
	sequence with the big denominator and the denominator of the sequence to be carried into. The carry is then added to the sequence to be
	carried into, and the carry times the denominator ratio is subtracted from the sequence with the big denominator. For example, if the
	sequence has six copies of a coin address, and the denominator is five, and the sequence to be carried into has a denominator of one, the
	denominator ratio is five, and the carry integer is int(6 / 5) = 1. So one coin address is added to the list of coin addresses in the
	sequence to be carried into, and 1 * 5 = 5 is subtracted from the quantity of the coin address in the sequence with the big	denominator.

	After the quantities of each coin address are modified, the coin address lists in the sequence with the big denominator are generated from
	the quantity dictionary.
	"""
	for denominatorSequenceIndex in xrange(len(denominatorSequences) - 2, -1, -1):
		denominatorSequence = denominatorSequences[denominatorSequenceIndex]
		denominatorSequenceBigDenominator = denominatorSequences[denominatorSequenceIndex + 1]
		denominatorRatio = denominatorSequenceBigDenominator.denominator / denominatorSequence.denominator
		bigDenominatorAddressDictionary = getQuantityDictionary(denominatorSequenceBigDenominator.coinAddresses)
		for bigDenominatorAddressKey in bigDenominatorAddressDictionary:
			bigDenominatorAddressValue = bigDenominatorAddressDictionary[bigDenominatorAddressKey]
			carry = bigDenominatorAddressValue / denominatorRatio
			if carry > 0:
				bigDenominatorAddressDictionary[bigDenominatorAddressKey] -= carry * denominatorRatio
				denominatorSequence.coinAddresses += [bigDenominatorAddressKey] * carry
		denominatorSequenceBigDenominator.coinAddresses = []
		for bigDenominatorAddressKey in bigDenominatorAddressDictionary:
			bigDenominatorAddressValue = bigDenominatorAddressDictionary[bigDenominatorAddressKey]
			denominatorSequenceBigDenominator.coinAddresses += [bigDenominatorAddressKey] * bigDenominatorAddressValue

def getAccountLines(arguments, suffixNumberString):
	'Get the lines according to the arguments.'
	linkFileName = almoner.getParameter(arguments, 'account_location.csv', 'location')
	linkLines = almoner.getTextLines(almoner.getLocationText(linkFileName))[1 :]
	accountLines = ['']
	nameSet = set([])
	for linkLine in linkLines:
		linkLineSplit = linkLine.split(',')
		name = linkLineSplit[0]
		location = linkLineSplit[1]
		extraLines = []
		if '_xx' in location:
			location = location.replace('_xx', '_' + suffixNumberString)
			locationText = almoner.getLocationText(location)
			if '<title>404 Not Found</title>' in locationText:
				print('Warning, could not download page: %s' % location)
			else:
				extraLines = almoner.getTextLines(locationText.replace('coinzen.org/index.php/topic,', 'coinzen.org/index.php/topic='))
		else:
			extraLines = getNameAddressLines(location, nameSet)
		for extraLineIndex in xrange(len(extraLines) - 1, -1, -1):
			extraWords = extraLines[extraLineIndex].split(',')
			if len(extraWords) < 3:
				print('Warning, less than 3 words in:')
				print(linkLine)
				print(extraWords)
				print('')
				del extraLines[extraLineIndex]
			else:
				secondWord = extraWords[1]
				if '-' in secondWord or '(' in secondWord or ':' in secondWord:
					print('Coin address is invalid in:')
					print(extraWords)
					del extraLines[extraLineIndex]
		numberOfShares = len(getReceiverLinesByAccountLines(extraLines))
		accountLines.append(name + ': %s Shares' % numberOfShares)
		accountLines += extraLines
		accountLines.append('')
	addAdministratorBonus(accountLines)
	return accountLines

def getAddressDictionary(round):
	'Get the address dictionary.'
	addressDictionary = {}
	for accountLine in getAccountLines([], str(round)):
		accountLineSplit = accountLine.split(',')
		if len(accountLineSplit) > 1:
			name = accountLineSplit[0].strip()
			if name != '':
				addressDictionary[accountLineSplit[1]] = name
	return addressDictionary

def getAddressFractions(lines):
	'Get the AddressFractions by text.'
	addressFractions = []
	for line in lines:
		addressFractions.append(AddressFraction(line))
	return addressFractions

def getCutLines(cutLines, suffixNumber):
	"""
	The lines are cut at a different part of the list, so that a developer whose key starts with 1A does not get more on average over multiple
	rounds than a developer whose key starts with 1Z. This is done by cutting the list at an index which is the golden ratio times the round
	number, then modulo is used to keep it within the list bounds. It also reverses the list at every even round number, in case cutting is
	not enough to average pay over multiple rounds.
	"""
	rotation = (float(suffixNumber) * globalGoldenRatio) % 1.0
	rotationIndex = int(math.floor(rotation * float(len(cutLines))))
	if suffixNumber % 2 == 0:
		cutLines.reverse()
	cutLines = cutLines[rotationIndex :] + cutLines[: rotationIndex]
	return cutLines

def getDenominatorSequences(addressFractions):
	'Get the DenominatorSequences from the addressFractions.'
	denominators = []
	for addressFraction in addressFractions:
		for fraction in addressFraction.fractions:
			if fraction.denominator not in denominators:
				denominators.append(fraction.denominator)
	denominators.sort()
	denominatorSequences = []
	for denominator in denominators:
		denominatorSequence = DenominatorSequence(addressFractions, denominator)
		denominatorSequence.coinAddresses = getShuffledElements(denominatorSequence.coinAddresses)
		denominatorSequences.append(denominatorSequence)
	return denominatorSequences

def getDenominatorSequencesByAccountLines(accountLines):
	'Get the lines according to the arguments.'
	addressFractions = getAddressFractions(accountLines)
	denominatorSequences = getDenominatorSequences(addressFractions)
	carryCoinAddresses(denominatorSequences)
	return denominatorSequences

def getGroupedReceiverLines(denominatorMultiplier, denominatorSequences):
	'Get grouped receiver lines.'
	print('Receiver lines will be grouped by a factor of %s.' % denominatorMultiplier)
	for denominatorSequence in denominatorSequences:
		denominatorSequence.denominator *= denominatorMultiplier
	return getReceiverLinesByDenominatorSequences(denominatorSequences)

def getNameAddressLines(fileName, nameSet):
	'Get the name and address lines by the file name.'
	if fileName == '':
		return []
	listName = 'Share List'
	shareIndex = fileName.find('share')
	if shareIndex > -1:
		listName = fileName[: shareIndex].capitalize() + ' ' + listName
	addressLines = []
	for contributor in almoner.getContributors(fileName):
		name = contributor.name
		leftBracketIndex = name.find('[')
		if leftBracketIndex >= 0:
			name = name[leftBracketIndex + 1 :]
			spaceIndex = name.find(' ')
			if spaceIndex >= 0:
				name = name[spaceIndex + 1 :]
		rightBracketIndex = name.find(']')
		if rightBracketIndex >= 0:
			name = name[: rightBracketIndex]
		dotIndex = name.find('.')
		if dotIndex > 0:
			name = name[: dotIndex]
		lowerName = name.lower()
		if lowerName not in nameSet:
			linkName = 'https://raw.github.com/Unthinkingbit/charity/master/' + fileName
			addressLine = '%s,%s,1-%s(%s)' % (name.replace(' ', '_'), contributor.bitcoinAddress, listName, linkName)
			addressLines.append(addressLine)
			nameSet.add(lowerName)
		else:
			print('Duplicate contributor, which will not be added a second time.')
			print(name)
			print('')
	return addressLines

def getPackedReceiverLines(denominatorSequences, originalReceiverLines, suffixNumber):
	"""
	A devcoin round has 4,000 blocks, if there are more than 4,000 receiver lines, the lines after the four thousandth row will not get
	generation devcoins. To get devcoins to every line, the receiver lines are packed by the denominatorMultiplier:
	denominatorMultiplier = (len(originalReceiverLines) + maximumReceivers) / (maximumReceivers + 1 - len(denominatorSequences))

	The denominator in each denominator sequence is multiplied by the denominatorMultiplier. When the receiver lines are then generated by the
	denominator sequences, the column width of the line will be up to the denominator, in effect reducing the number of rows by the
	denominatorMultiplier and increasing the number of columns by the denominatorMultiplier.

	Because this changes the number of rows, the 'Average devcoins per share' is calculated from the original number of lines.
	"""
	maximumReceivers = 4000
	originalReceiverLineLength = len(originalReceiverLines)
	denominatorMultiplier = 1
	if len(originalReceiverLines) > maximumReceivers:
		denominatorMultiplier = (len(originalReceiverLines) + maximumReceivers) / (maximumReceivers + 1 - len(denominatorSequences))
		originalReceiverLines = getGroupedReceiverLines(denominatorMultiplier, denominatorSequences)
		if len(originalReceiverLines) > maximumReceivers:
			print('Warning, denominatorMultiplier math is wrong, the receiver lines will be grouped by another factor of two.')
			originalReceiverLines = getGroupedReceiverLines(2, denominatorSequences)
	originalDevcoinBlocksPerShareFloat = float(maximumReceivers) / originalReceiverLineLength
	averageDevcoinsPerShare = int(round(originalDevcoinBlocksPerShareFloat * 45000.0))
	print('Average devcoins per share: %s' % almoner.getCommaNumberString(averageDevcoinsPerShare))
	print('Number of original receiver lines lines: %s' % originalReceiverLineLength)
	print('Number of receiver lines lines: %s' % len(originalReceiverLines))
	print('')
	return getCutLines(originalReceiverLines, suffixNumber)

def getPeerLines(arguments):
	'Get the inner peer text according to the arguments.'
	peerFileName = almoner.getParameter(arguments, 'peer.csv', 'inputpeer')
	peerLines = almoner.getTextLines(almoner.getLocationText(peerFileName))
	print('Number of peers: %s' % len(peerLines))
	print('')
	return peerLines

def getPluribusunumText(peerText, receiverLines):
	'Get the pluribusunum text according to the arguments.'
	return 'Format,pluribusunum\n%s_begincoins\n%s_endcoins\n' % (peerText, almoner.getTextByLines(receiverLines))

def getQuantityDictionary(elements):
	'Get the quantity dictionary.'
	quantityDictionary = {}
	for element in elements:
		if element in quantityDictionary:
			quantityDictionary[element] += 1
		else:
			quantityDictionary[element] = 1
	return quantityDictionary

def getReceiverLinesByAccountLines(accountLines):
	'Write output.'
	denominatorSequences = getDenominatorSequencesByAccountLines(accountLines)
	return getReceiverLinesByDenominatorSequences(denominatorSequences)

def getReceiverLinesByDenominatorSequences(denominatorSequences):
	'Concatenate the receiver lines from all the denominator sequences.'
	receiverLines = []
	for denominatorSequence in denominatorSequences:
		receiverLines += denominatorSequence.getReceiverLines()
	return receiverLines

def getRecipientDictionary(round):
	'Get the recipient dictionary.'
	recipientDictionary = {}
	for accountLine in getAccountLines([], str(round)):
		accountLineSplit = accountLine.split(',')
		if len(accountLineSplit) > 1:
			name = accountLineSplit[0].lower()
			if name != '':
				recipientDictionary[name] = accountLineSplit[1]
	return recipientDictionary

def getShareListSet(round):
	'Get the names of the developers on the share list set.'
	isShareName = False
	shareListSet = set([])
	for accountLine in getAccountLines([], str(round)):
		accountLineSplit = accountLine.split(',')
		if len(accountLineSplit) == 0:
			isShareName = False
		elif len(accountLineSplit[0]) == 0:
			isShareName = False
		if isShareName:
			shareListSet.add(accountLineSplit[0].lower())
		if len(accountLineSplit) == 1:
			if ' Share List: ' in accountLineSplit[0]:
				isShareName = True
	return shareListSet

def getShuffledElements(elements):
	"""
	Because the number of lines is usually not perfectly divisible into 4,000, the addresses of each developer are spread out, so that in each
	round the amount that the developer receives is close to the average. This is done by inserting them at an index within the shuffledLines
	list which is increased by the golden ratio, then modulo is used to keep it within the list bounds.
	"""
	shuffledElements = []
	for element in elements:
		shuffledLengthFloat = float(len(shuffledElements))
		index = int(shuffledLengthFloat * ((shuffledLengthFloat * globalGoldenRatio) % 1.0))
		shuffledElements.insert(min(index, len(shuffledElements) - 1), element)
	return shuffledElements

def getSuffixNumber(fileName):
	'Determine the round number, returning 0 if there is not one.'
	underscoreIndex = fileName.rfind('_')
	if underscoreIndex == -1:
		return 0
	afterUnderscore = fileName[underscoreIndex + 1 :]
	dotIndex = afterUnderscore.rfind('.')
	if dotIndex == -1:
		return 0
	afterUnderscore = afterUnderscore[: dotIndex]
	if not afterUnderscore.isdigit():
		return 0
	return int(afterUnderscore)

def getSummaryText(accountLines, originalReceiverLines, peerLines, suffixNumber):
	'Get the summary text.'
	cString = cStringIO.StringIO()
	suffixNumberPlusOne = suffixNumber + 1
	numberOfLines = len(originalReceiverLines)
	numberOfLinesFloat = float(numberOfLines)
	administratorPay = 0.0
	for accountLine in accountLines:
		if 'Administrator' in accountLine:
			administratorPay += Administrator(accountLine).pay
	percentPay = 0.1 * round(1000.0 * administratorPay / numberOfLinesFloat)
	cString.write('The round %s receiver files have been uploaded to:\n' % suffixNumber)
	for peerLine in peerLines:
		suffixedPeerLine = peerLine[: -len('.csv')] + ('_%s.csv' % suffixNumber)
		cString.write('%s\n' % suffixedPeerLine)
	cString.write('\nThe account file is at:\n')
	cString.write('http://galaxies.mygamesonline.org/account_%s.csv\n' % suffixNumber)
	devcoins = int(round(180000000.0 / numberOfLinesFloat))
	linesCommaString = almoner.getCommaNumberString(numberOfLines)
	cString.write('\nThere were %s original receiver lines, so the average number of devcoins per share is ' % linesCommaString)
	cString.write('180,000,000 dvc / %s = %s dvc.' % (linesCommaString, almoner.getCommaNumberString(devcoins)))
	cString.write(' Administrator pay is %s shares, %s percent of the total.\n' % (administratorPay, percentPay))
	cString.write('\nPeople on that list will start getting those coins in round %s, starting at block %s,000.' % (suffixNumber, 4 * suffixNumber))
	cString.write(' The procedure for generating the receiver files is at:\n')
	cString.write('http://devtome.com/doku.php?id=devcoin#generating_the_files\n')
	cString.write('\nThe next bounties will go into round %s:\n' % suffixNumberPlusOne)
	cString.write('http://devticker.pw/business_bounty/business_bounty_%s.csv\n' % suffixNumberPlusOne)
	cString.write('\nThe next ongoing payments will go into round %s:\n' % suffixNumberPlusOne)
	cString.write('http://dvccountdown.blisteringdevelopers.com/ongoing_%s.csv\n' % suffixNumberPlusOne)
	return cString.getvalue()

def writeOutput(arguments):
	'Write output.'
	if '-h' in arguments or '-help' in arguments:
		print(__doc__)
		return
	suffixNumberString = almoner.getParameter(arguments, '24', 'round')
	suffixNumber = int(suffixNumberString)
	outputAccountTo = almoner.getSuffixedFileName(almoner.getParameter(arguments, 'account.csv', 'account'), suffixNumberString)
	accountLines = getAccountLines(arguments, suffixNumberString)
	peerLines = getPeerLines(arguments)
	peerText = '_beginpeers\n%s_endpeers\n' % almoner.getTextByLines(peerLines)
	accountText = getPluribusunumText(peerText, accountLines)
	if almoner.sendOutputTo(outputAccountTo, accountText):
		print('The account file has been written to:\n%s\n' % outputAccountTo)
	outputReceiverTo = almoner.getSuffixedFileName(almoner.getParameter(arguments, 'receiver.csv', 'receiver'), suffixNumberString)
	outputSummaryTo = almoner.getParameter(arguments, 'receiver_summary.txt', 'summary')
	denominatorSequences = getDenominatorSequencesByAccountLines(accountLines)
	originalReceiverLines = getReceiverLinesByDenominatorSequences(denominatorSequences)
	receiverLines = getPackedReceiverLines(denominatorSequences, originalReceiverLines, suffixNumber)
	receiverText = getPluribusunumText(peerText, receiverLines)
	if almoner.sendOutputTo(outputReceiverTo, receiverText):
		print('The receiver file has been written to:\n%s\n' % outputReceiverTo)
		shaOutputPrefix = almoner.getParameter(arguments, '', 'sha')
		if len(shaOutputPrefix) != 0:
			sha256FileName = almoner.getSuffixedFileName(outputReceiverTo, shaOutputPrefix)
			almoner.writeFileText(sha256FileName, hashlib.sha256(receiverText).hexdigest())
			print('The sha256 receiver file has been written to:\n%s\n' % sha256FileName)
	if almoner.sendOutputTo(outputSummaryTo, getSummaryText(accountLines, originalReceiverLines, peerLines, suffixNumber)):
		print('The summary file has been written to:\n%s\n' % outputSummaryTo)


class AddressFraction:
	'A class to handle an address and associated fractions.'
	def __init__(self, line=''):
		'Initialize.'
		self.coinAddress = ''
		self.fractions = []
		words = line.split(',')
		if len(words) < 2:
			return
		self.coinAddress = words[1].strip()
		if len(words) < 3:
			self.fractions.append(Fraction())
			return
		for word in words[2 :]:
			dashIndex = word.find('-')
			if dashIndex != -1:
				word = word[: dashIndex]
			wordStripped = word.replace('/', '').strip()
			if wordStripped.isdigit() or len(wordStripped) == 0:
				self.fractions.append(Fraction(word))

	def __repr__(self):
		"Get the string representation of this class."
		return '%s, %s' % (self.coinAddress, self.fractions)


class Administrator:
	'A class to handle an administrator.'
	def __init__(self, line):
		'Initialize.'
		self.administratorDescription = ''
		self.factotumDescription = ''
		self.isFactotum = False
		self.isFileAdministrator = False
		self.isGeneralAdministrator = False
		self.pay = 0.0
		lineSplit = line.split(',')
		if len(lineSplit) < 3:
			return
		self.name = lineSplit[0].strip()
		self.coinAddress = lineSplit[1].strip()
		for word in lineSplit:
			wordUntilBracket = word
			bracketIndex = word.find('(')
			if bracketIndex > -1:
				wordUntilBracket = word[: bracketIndex]
			if wordUntilBracket.endswith('-File Custodian'):
				self.isFileAdministrator = True
				numberStrings = wordUntilBracket[: wordUntilBracket.find('-')].split('/')
				firstFloat = float(numberStrings[0])
				if len(numberStrings) == 2:
					self.pay += firstFloat / float(numberStrings[1])
				else:
					self.pay += firstFloat
			elif wordUntilBracket.endswith(' Administrator'):
				dashIndex = wordUntilBracket.find('-')
				if dashIndex != -1:
					self.pay += float(wordUntilBracket[: dashIndex])
					self.isGeneralAdministrator = True
					self.administratorDescription = word[dashIndex + 1 :]
			elif wordUntilBracket.endswith(' Factotum'):
				dashIndex = wordUntilBracket.find('-')
				if dashIndex != -1:
					self.pay += float(wordUntilBracket[: dashIndex])
					self.isFactotum = True
					self.factotumDescription = word[dashIndex + 1 :]

	def getAccountLine(self, bonusMultiplier):
		'Get account line.'
		accountLine = '%s,%s,%s-%s' % (self.name, self.coinAddress, bonusMultiplier, self.administratorDescription)
		if self.isFactotum:
			accountLine += ',%s-%s' % (bonusMultiplier, self.factotumDescription)
		return accountLine


class DenominatorSequence:
	'A class to handle a DenominatorSequence.'
	def __init__(self, addressFractions, denominator):
		'Initialize.'
		self.coinAddresses = []
		self.denominator = denominator
		for addressFraction in addressFractions:
			for fraction in addressFraction.fractions:
				if fraction.denominator == denominator:
					for addressIndex in xrange(fraction.numerator):
						if base58.get_bcaddress_version(addressFraction.coinAddress) == 0:
							self.coinAddresses.append(addressFraction.coinAddress)
						else:
							print('Warning, the address %s is invalid and will not get paid.' % addressFraction.coinAddress)
		self.coinAddresses.sort()

	def __repr__(self):
		"Get the string representation of this class."
		return '%s, %s\n' % (self.denominator, self.coinAddresses)

	def getReceiverLines(self):
		'Get the receiver lines.'
		numberOfSlots = int(math.ceil(float(len(self.coinAddresses)) / float(self.denominator)))
		floatWidth = float(len(self.coinAddresses)) / float(numberOfSlots)
		maximumSlotWidth = int(math.ceil(floatWidth))
		minimumSlotWidth = int(math.floor(floatWidth))
		numberOfCells = numberOfSlots * maximumSlotWidth
		remainingNumberOfNarrows = numberOfCells - len(self.coinAddresses)
		remainingNumberOfWides = numberOfSlots - remainingNumberOfNarrows
		receiverLines = []
		coinAddressIndex = 0
		for wideIndex in xrange(remainingNumberOfWides):
			endIndex = coinAddressIndex + maximumSlotWidth
			addReceiverLines(self.coinAddresses[coinAddressIndex : endIndex], receiverLines)
			coinAddressIndex = endIndex
		for narrowIndex in xrange(remainingNumberOfNarrows):
			endIndex = coinAddressIndex + minimumSlotWidth
			addReceiverLines(self.coinAddresses[coinAddressIndex : endIndex], receiverLines)
			coinAddressIndex = endIndex
		return receiverLines


class Fraction:
	'A class to handle a fraction.'
	def __init__(self, line=''):
		'Initialize.'
		self.denominator = 1
		self.numerator = 1
		lineStripped = line.strip()
		if len(lineStripped) < 1:
			return
		if lineStripped[0] == '/':
			lineStripped = '1' + lineStripped
		words = lineStripped.replace('/', ' ').split()
		if len(words) == 0:
			return
		self.numerator = int(words[0])
		if len(words) == 2:
			self.denominator = int(words[1])

	def __repr__(self):
		"Get the string representation of this class."
		return '%s/%s' % (self.numerator, self.denominator)


def main():
	'Write output.'
	writeOutput(sys.argv)

if __name__ == '__main__':
	main()