The Inca and a couple of other South American cultures from the Andean region used quipus 1(also known as khipus or talking knots): recording devices consisting of knotted strings. The word khipu — meaning "knot" or "to knot" — comes from the Cusco-Colloa Quechua2 language (Peru). A quipu usually consisted of colored, spun, and plied thread or strings made from cotton or camelid3 fiber. For the Inca, the system aided in collecting data and keeping records, ranging from monitoring tax obligations, properly collecting census records, calendrical information, and military organization. Only a relatively small number have survived. A quipu could have only a few or up to 2,000 cords.

quipu
An example of a quipu from the Inca Empire, currently in the Larco Museum Collection (Lima, Peru).

Quipus certainly have not yet revealed all of their secrets, but it is clear now that most recorded information consists of numbers in a decimal system. Marcia and Robert Ascher — after having analyzed several hundred quipus — succeeded in deciphering the code that allows to read the numbers.

One cord can record one or more numbers. These numbers are represented as a sequence of knot clusters to be read from the main cord: the cord to which all the other cords are attached (at the top of the figure below). Each cluster of knots is a digit, and there are three main types of knots: simple overhand knots4, figure-of-eight knots5 and long knots (consisting of an overhand knot with one or more additional turns).

knot notation
A quipu uses three main types of knots for recording numeric information: simple overhand knots, figure-of-eight knots and long knots.

For their analysis of quipus the Aschers introduced a simple notation for a cluster of knots (known as the Aschers' notation). They discovered that a natural number $$c_{n}c_{n - 1}c_{n - 2}\ldots c_{2}c_{1}c_{0}$$ — with $$c_i (i = 0, 1, \ldots, n)$$ the digits $$0, 1, \ldots, 9$$ in the decimal representation of the number — is recorded in the following way using knots. For all digits except the last one, a zero is represented by the absence of a knot in the appropriate position (represented as X) and all other digits are represented by a cluster of $$c_i$$ overhand knots (represented as $$c_i$$s). For the last digit a zero is represented by a figure-of-eight knot with an extra twist (represented as EE), a one by a figure-of-eight knot (represented as E; a long knot with a single turn is the same as an overhand knot and would not discriminate the last digit) and the other digits by a long knot with $$c_i$$ turns (represented as $$c_i$$L). The representations of the individual clusters of knots are separated by spaces in the Aschers' notation.

Because the ones digit (the last digit of a natural number) is represented in a distinctive way, it is clear where each number ends. One strand on a quipu can therefore contain several numbers. This is summarized in the diagram below.

decimal to quipu
Conversion of the decimal representation of a natural number to its knots representation on a quipu in the Aschers' notation.

For example, if 3s represents three overhand knots, 7L represents a long knot with seven turns, E represents a figure-of-eight knot and X represents a space:

This reading can be confirmed by a fortunate fact: quipus regularly contain sums in a systematic way. For instance, a cord may contain the sum of the next $$n$$ cords, and this relationship is repeated throughout the quipu. Sometimes there are sums of sums as well. Such a relationship would be very improbable if the knots were read incorrectly.

Assignment

Define the following functions that can be used to convert the decimal representation of a number $$n \in \mathbb{N}$$ (int) into the knots representation of $$n$$ on a quipu in the Aschers' notation (str), and vice versa.

Now also define a class Quipu that can be used to compute with quipus in Python. New objects of this class must either be initialized with the decimal representation of a number $$n \in \mathbb{N}$$ (int) or with the knots representation of $$n$$ on a quipu in the Aschers' notation (str). Other values passed upon initialization should result in an AssertionError being raised with the message invalid quipu.

The conversion of a quipu (an object of the class Quipu) into a string — using the built-in functions str() and repr() — always contains the knots representation on a quipu in the Aschers' notation (use the example below to derive the difference in formatting of the results returned by both built-in functions) and the conversion to an integer — using the built-in function int() — returns the decimal value.

Make sure that the binary operators for addition (+), subtraction (-), multiplication (*), integer division (//), modulo (%) and exponentiation (**) each evaluate to a new quipu (an object of the class Quipu) if both of their operands are quipus or if one operand is a quipu and the other is an integer. The result of these operations should have the same decimal value as the result obtained by applying these operators on two integer operands having the same decimal values. If the operations do not evaluate to a valid quipu, an AssertionError must be raised with the message invalid quipu.

Example

>>> decimal2ascher(327)
'3s 2s 7L'
>>> decimal2ascher(2461)
'2s 4s 6s E'
>>> decimal2ascher(107)
'1s X 7L'
>>> decimal2ascher(-1)
Traceback (most recent call last):
AssertionError: invalid quipu

>>> ascher2decimal('3s 2s 7L')
327
>>> ascher2decimal('2s 4s 6s E')
2461
>>> ascher2decimal('1s X 7L')
107
>>> ascher2decimal('5s 17s 4L')
Traceback (most recent call last):
AssertionError: invalid quipu

>>> number = Quipu('3s 2s 7L')
>>> int(number)
327
>>> str(number)
'3s 2s 7L'
>>> number
Quipu('3s 2s 7L')

>>> number = Quipu(2461)
>>> int(number)
2461
>>> str(number)
'2s 4s 6s E'
>>> number
Quipu('2s 4s 6s E')

>>> Quipu(42) + Quipu(16)
Quipu('5s 8L')
>>> Quipu(42) - Quipu(16)
Quipu('2s 6L')
>>> Quipu(42) * Quipu(16)
Quipu('6s 7s 2L')
>>> Quipu(42) // Quipu(16)
Quipu('2L')
>>> Quipu(42) % Quipu(16)
Quipu('1s EE')
>>> Quipu(12) ** Quipu(2)
Quipu('1s 4s 4L')

>>> Quipu(42) + 16
Quipu('5s 8L')
>>> Quipu(42) - 16
Quipu('2s 6L')
>>> Quipu(42) * 16
Quipu('6s 7s 2L')
>>> Quipu(42) // 16
Quipu('2L')
>>> Quipu(42) % 16
Quipu('1s EE')
>>> Quipu(12) ** 2
Quipu('1s 4s 4L')

>>> 42 + Quipu(16)
Quipu('5s 8L')
>>> 42 - Quipu(16)
Quipu('2s 6L')
>>> 42 * Quipu(16)
Quipu('6s 7s 2L')
>>> 42 // Quipu(16)
Quipu('2L')
>>> 42 % Quipu(16)
Quipu('1s EE')
>>> 12 ** Quipu(2)
Quipu('1s 4s 4L')

>>> Quipu('5s 17s 4L')
Traceback (most recent call last):
AssertionError: invalid quipu
>>> Quipu(-1)
Traceback (most recent call last):
AssertionError: invalid quipu
>>> Quipu({1, 2, 3})
Traceback (most recent call last):
AssertionError: invalid quipu
>>> Quipu(16) - Quipu(42)
Traceback (most recent call last):
AssertionError: invalid quipu

Resources