[Question]
  [
[Quipus](https://en.wikipedia.org/wiki/Quipu) are an ancient device used by the Inca in the Precolumbian era to record numbers in a base ten positional system of knots on a cord, which works as follows:
> 
> Each cluster of knots is a digit, and there are three main types of knots: simple overhand knots; "long knots", consisting of an overhand knot with one or more additional turns; and figure-eight knots.
> 
> 
> * Powers of ten are shown by position along the string, and this position is aligned between successive strands.
> * Digits in positions for 10 and higher powers are represented by clusters of simple knots (e.g., 40 is four simple knots in a row in the "tens" position).
> * Digits in the "ones" position are represented by long knots (e.g., 4 is a knot with four turns). Because of the way the knots are tied, the digit 1 cannot be shown this way and is represented in this position by a figure-of-eight knot.
> * Zero is represented by the absence of a knot in the appropriate position.
> 
> 
> 
## Details
For this challenge, each strand of a quipu represents a **single number** (though, as the Wikipedia article states, you *can* represent many numbers on one strand, in this challenge, we shall not).
**Knots**
Each knot will be represented by a single ASCII character.
* `.` represents a simple knot
* `:` represents a one turn of a long knot
* `8` represents a figure-eight knot
* `|` represents the absence of a knot as well as a delimiter between the digits.
**Constructing Quipus**
Quipu are constructed following these rules.
1. Strands run top to bottom in descending order of position (as in, the units digit will be at the bottom end of a strand). Digits along a strand are separated by the character (`|`).
2. The power of 10 a digit represents is determined by its position along the strand in the same way a digit's power of 10 would be calculated using its index in a number with our number system. That is, `24` with a `2` in the tens place and a `4` in the units place, will be represented by two knots, a delimiter (`|`), then four knots.
3. Digits in the same position are aligned towards the bottom of the strand. If one digit in a position will have less knots than other digits of other numbers in the same position, the absence of those knots is represented by (`|`).
4. Consecutive simple knots (`.`) represent a value in their position.
5. Every digit is represented by at least 1 character. When a digits value is 0 for all numbers in a quipu, it is represented by the absence of a knot (`|`).
6. The units place is treated specially. A one in the units place is represented by a figure-eight knot (`8`). A value of two or more in the units place is represented by consecutive long knots (`:`).
7. When the units digit is 0 for all numbers in a quipu the absence of a knot is not printed but, the trailing delimiter for the tens digit is preserved.
8. There is no delimiter following the units digit.
## Rules
* Input will consist of a non-empty list of non-negative integers that may be received through any of the [default input methods](http://meta.codegolf.stackexchange.com/q/2447/31441). You may assume that these integers are all less than or equal to `2147483647` or `2^31-1`. While the test cases are space-delimited, your input format can separate inputs in any way that is convenient for your language, whether that be comma-separated, newline-separated, in an array, and so on.
* Output consists of a single Quipu constructed according to the rules described above. Output may be given through any of the [default output methods](http://meta.codegolf.stackexchange.com/q/2447/31441).
* Your code should be a program or a function, though it does not need to be a named function.
* Knots take some time to tie so to save time, your code as short as possible.
As always, if the problem is unclear, please let me know. Good luck and good golfing!
## Examples
Input:
```
5 3 1 0
```
Output:
```
:|||
:|||
::||
::||
::8|
```
Input:
```
50 30 10 0
```
Output:
```
.|||
.|||
..||
..||
...|
||||
```
Input:
```
330
```
Output:
```
.
.
.
|
.
.
.
|
```
Input:
```
204 1
```
Output:
```
.|
.|
||
||
||
:|
:|
:|
:8
```
Input:
```
201 0 100 222
```
Output:
```
.||.
.|..
||||
|||.
|||.
||||
|||:
8||:
```
Input:
```
1073741823 2147483647
```
Output:
```
|.
..
||
|.
||
.|
.|
.|
..
..
..
..
||
|.
|.
|.
|.
..
..
..
||
.|
.|
.|
..
..
..
..
||
|.
|.
|.
|.
..
..
..
..
||
|.
|.
..
||
.|
.|
..
..
..
..
..
..
||
|.
|.
..
..
||
|:
|:
|:
|:
::
::
::
```
Input:
```
0
```
Output:
```
|
```
[Longer test cases](https://gist.github.com/anonymous/eb36d73023e7d8dcc53a)
# Further Reading
* [Ancientscripts: Quipu](http://www.ancientscripts.com/quipu.html)
* [The Quipu, Pre-Inca Data Structure](http://agutie.homestead.com/files/quipu_b.htm)
  
[Answer]
# [Unreadable](https://esolangs.org/wiki/Unreadable), ~~3183~~ 3001 bytes
This was a fun challenge to work on, on and off, in between Christmas celebrations. Thank you for posting! Golfing this was interesting because the specification is full of exceptions and special cases, which required lots of if conditions. Also, while I didn’t need to convert to and from decimal this time, I *did* need a “max” function of sorts to determine the largest number of digits in each number and the largest value of the digits in each place.
**The first version of this was 4844 bytes,** just to give you an idea of how much I golfed this.
The program expects the input as a *comma-separated* list of integers. No spaces or newlines. Using those will produce undefined behavior.
> 
> '""""""'""'""'""'""'""'"""'""""""'"""'""'""'""'""'""'""'""'""'""'""'""""""'""'""'""'""'""'""'"""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""'""""""'""""""""'"""'""'""'""'""'""'""""""'""'"""'""'""'""'""'""'""'""'""'"""'"""""'""""""'"""'""""""""'"""""""'"""'""""""'""""""""'"""'""'""'"""""""'""""""""'"""'"""""'""'""""""'""'""'""'"""'""""""""""'""""'""""""'""'""'"""'"""""""'""'""'""'""'""'""'"""'"""""""""'""""""'""""""'""'"""'""'"""""""'""'"""'""'"""""'""""""'""'""'"""'""""""""'"""""""'""'""'"""'""""""'""'""'""'"""'""""""""'"""""""'""'""'""'"""'""""""'""""""'"""'""""""""'"""""""'"""'"""'""""""'"""'""""""""'"""'""""""'""'""'"""'"""""'"""""""'"""""""'"""'""""""'"""'""""""""'"""""""'"""'"""""'""""""'"""'""'"""""""'"""'""""'""""""'""'""'""'"""'""""""""'"""'"""""'""""""""'""""""'"""""""'"""'""'"""""""'"""""""'"""'"""""""""'"""""""""'"""""""'"""""""'"""'"""'"""""""""'""'"""""""'"""'"""'""""""""'"""'""""'""""'""""""'""'"""'""'""'""'""'""'""'""'""'"""'"""""'"""""""'""""""'""'"""'""'"""""""'""'"""'""""'""""'"""""'""""""'""'""'""'""'""'""'""'"""'""""""'""'""'""'""'"""'"""""""'""""""'""'"""'""'"""""""'""'"""'"""'"""""'"""""""'""""""'""'"""'""""""""'"""""""'""'"""'"""""""""'""""""""'"""""""'""""""'""'""'""'""'""'""'""'"""'""""""""'"""""""'""'""'""'""'""'""'""'"""'"""""""""'""""""""'""""""""'"""""""'"""""""'"""'"""""""""'"""""""'""'""'""'"""'""""""'""'""'""'""'"""'"'"""""""""'""'"""""""'"""""""'""'"""'"""""""""'""'""'"""""""'"""""""'""'"""'""""'""""""'"""""""'""'"""'"""""""""'"""""""""'""'"""""""'"""'"""'"""""""""'""'""'"""""""'"""""""'"""'"""'"""""""""'""'""'""'"""""""'"""""""'""'"""'"""'""""""""'"""'""'"""""""'"""""""'""'"""'""""""""'""""""""'"""'"""""""'""""""""'"""'"""""""""'"""""""'"""""""'"""'"""""""""'""'"""""""'"""'"""""""'""'""'""'""'""'""'"""'""'""'"""""""'""'""'""'""'""'"""'"""""""'""""""""'"""'"""""""'""'""'""'""'""'"""'""""'"""""""""'"""""""""'""'"""""'"""""""'""""""'""'""'"""'""""""""'"""""""'""'""'"""'""""""'"""""""'""'"""'""""""""'"""""""'"""""""'""'"""'"""'"""""""""'"""""""'""'""'""'""'""'"""""""'""'""'"""'"""'"""""""""'""'"""""""'"""'"""'""""""""'"""'""""""'"""""""'""'"""'""""""""'"""""""'"""""""'""'"""'"""'"""""'"""""""'""""""'""'""'"""'""'"""""""'""'""'"""'"""'""""'""""'"""""'""""""'"""""""'""'"""'""""""""'"""""""'"""""""'""'"""'""""""'""""""'""'""'"""'""""""""'"""""""'""'""'"""'"""'"""""'"""""""'""""""'""'""'"""'""'"""""""'""'""'"""'""""""'"""""""'""'"""'""'"""""""'"""""""'""'"""'"""""""""'""'"""""""'"""'""""""'""""""""'""""""""'""""""""'""""""""'"""""""'""'""'"""'"""'"""'"""'"""""""""'"""""'"""""""'""""""'""'"""'""'"""""""'""'"""'"""""""'""'""'""'""'"""'"""'"""""""""'"""""""'""'""'""'"""'"'"""""""'""""""""'"""'"""'"""""""""'""""""""'""""""""'""""""""'"""""""'"""""""'"""'"""""""""'"""""""'""'""'""'"""'"'""'""'""'""'""'""'""'""'""'"""'"""'""""""'"""""""'"""'""""""""'""""""'""'""'""'"""'"""""'"""""""'""""""'""'""'"""'""""""""'"""""""'""'""'"""'""""""'"""""""'"""'""""""""'"""""""'"""""""'"""'"""
> 
> 
> 
## Explanation
I’m going to walk you through how the program works by showing you how it processes the specific input `202,100,1`.
In the beginning, we construct a few values that we will need later — mostly the ASCII codes of the characters we will output.
[![enter image description here](https://i.stack.imgur.com/WQ7p7.png)](https://i.stack.imgur.com/WQ7p7.png)
As you can see, `'8'` and `'.'` are already available. `'|'`, however, is really 124, not 14. We use a while loop to add twice the temporary value in slot #1 onto it to get 124 (which is 14 + 55×2, because the while loop runs for 56−1 = 55 iterations). This saves some bytes because large integer literals like 124 are really long. In the following diagram, I show the location of every variable the program uses.
[![enter image description here](https://i.stack.imgur.com/pGDGL.png)](https://i.stack.imgur.com/pGDGL.png)
Next, we want to input all the characters and store them on the tape starting at cell #12 (*p* is the running pointer for this). At the same time, we want to know how long the longest number is (how many digits). To achieve this, we keep a running total *in unary* going leftwards starting at cell #−1 (we use *q* as the running pointer). After the first input number (`202`), the tape now looks like this:
[![enter image description here](https://i.stack.imgur.com/cKNBv.png)](https://i.stack.imgur.com/cKNBv.png)
You will have noticed that the numbers are off by 4. Well, when we first input them, they are their ASCII values, so they’re “off” by 48 and the comma is 44. For each character, we copy the 46 from `'.'` into *r* and then subtract it with a while loop (which subtracts 45) and then we add 1. We do that so that the comma (our separator) is 0, so we can use a conditional to recognize it.
Also, you will have noticed that we leave cell #11 at 0. We need that to recognize the boundary of the first number.
The next character will be a comma, so we store a 0 in #15, but of course this time we don’t advance *q*. Instead, we set *q* back to 0 and start “overwriting” the 1s we’ve already placed.
After all the remaining characters are processed, we get this:
[![enter image description here](https://i.stack.imgur.com/u9OYA.png)](https://i.stack.imgur.com/u9OYA.png)
As you can see, the 1s written by *q* now indicate (in unary) the length of the longest number.
We now use a while loop to move *q* to the very left, and then place another pointer there which I will call *r2*. The purpose of *r2* will become clear later.
[![enter image description here](https://i.stack.imgur.com/YZS0y.png)](https://i.stack.imgur.com/YZS0y.png)
At this point, let me clarify the terminology that I will use throughout this.
* By **number**, I mean one of the input numbers that are separated by commas. In our example, they are 202, 100 and 1.
* By **digit**, I mean a single digit in a specific one of the numbers. The first number has 3 digits.
* By **place**, I mean the ones place, tens place, hundreds place, etc. So if I say “the digits in the current place”, and the current place is the ones place, those digits are 2, 0, and 1 in that order.
Now back to our regular programming. The entire rest of the program is a big loop that moves *q* forward until it reaches cell #0. Each of the cells along the way represents a place, with the ones place on the far right, and *q* will start at the most significant. In our example, that is the hundreds place.
We proceed by incrementing the cell *q* points at (that is, *\*q*).
[![enter image description here](https://i.stack.imgur.com/nlQJc.png)](https://i.stack.imgur.com/nlQJc.png)
We are now at “stage 2” for the hundreds place. In this stage, we will find out what the largest digit is among all the digits in the hundreds place. We use the same unary counting trick for this, except this time the pointer is called *r* and the pointer *r2* marks its starting position to which we need to reset it every time we move on to the next number.
Let’s start with the first number. We begin by setting *p* to 11 (the hard-coded starting position of all the numbers). We then use a while loop to find the end of the number and set *p2* there to mark the position. At the same time, we also set *q2* to 0:
[![enter image description here](https://i.stack.imgur.com/Lz8nD.png)](https://i.stack.imgur.com/Lz8nD.png)
Don’t be distracted by the fact that *q2* is pointing into the vars. We don’t have a padding of a blank cell there because we can detect cell #0 simply because it’s number zero.
Next, we go through the current number by decrementing *p* and *q2* together until *\*p* is zero. At each place, the value of *\*q2* tells us what we need to do. 1 means “do nothing”, so we keep going. Eventually we encounter the 2 in cell #−3. Every time *\*q2* is not equal to 1, *q2* is always equal to *q*.
[![enter image description here](https://i.stack.imgur.com/9pnYV.png)](https://i.stack.imgur.com/9pnYV.png)
As I already stated, stage 2 is “determine the largest digit in this place”. So we set *r* to *r2*, use a while loop to decrement *\*p* and move *r* leftwards and fill the tape with 1s, and then use another while loop to move *r* back to the right and increment *\*p* again to restore the value. Remember that every while loop runs for one fewer iteration than the value we use it on; because of this, the number of 1s written will be 3 more (rather than 4 more) than the digit value, and the final value stored back in *\*p* will be 2 more. Thus, this has effectively decremented *\*p* by 2.
After that, we set *p* to the value of *p2* and then we do all that again. For the second time, set *q2* to 0, find the end of the number by moving *p* to the right, and then go through the digits of this number by decrementing *p* and *q2* together. Once again we will encounter the 2 in cell #−3 and write that many 1s left of *\*r*.
In the case of the third number, we end up doing nothing because it doesn’t have a hundreds place (so *q2* never reaches *q*), but that’s OK because that doesn’t affect the calculation of the maximum digit value.
[![enter image description here](https://i.stack.imgur.com/BgM3D.png)](https://i.stack.imgur.com/BgM3D.png)
We also set the cell *\*(r − 4)*, which I’ve marked with an unlabeled arrow here, to 1 (even though it’s already at 1). I’m not going to tell you why yet, but maybe you’ve already guessed?
The next increment of *\*q* takes us to stage 3, which is “subtract the maximum digit from all digits in the current place”. As before, we reset *p* to 11 and *q2* to 0 and then go through all the numbers just like we did in the previous stage; except this time, *\*q* = 3 instead of 2. Every time *q2* meets *q* and *p* is in a hundreds place, we use a while loop to decrement *\*p* as many times as there are 1s in the block left of *\*r2* (5 in our example) by using *r* as a running pointer. We actually decrement it once more than that so that the largest digit ends up at −2, for a reason that will become clear later:
[![enter image description here](https://i.stack.imgur.com/N6Gdu.png)](https://i.stack.imgur.com/N6Gdu.png)
After we’ve processed all the numbers, we are now at the end of stage 3. Here we perform two singular things.
* First, we *also* subtract the size of the *r*-block (plus 1) from *\*q*, but using the *r2* pointer, which leaves it on the left. *\*q* becomes negative this way. In our case, the *r*-block has five 1s, so *\*q* becomes −3.
* Second, we set a variable *out* to a non-zero value to indicate that we are now entering the output stage. (Technically, the fact that *\*q* is negative already indicates the output stage, but this is too difficult to check for, hence the extra variable.)
You now understand that we keep going through the numbers, find the current place (indicated by the non-1 value of *\*q*) within each number, and do something depending on the value of *\*q*. We see that *\*q* is first incremented to 2 (= calculate maximum digit value), then 3 (subtract maximum digit value from every digit in this place) and then we subtract from it to make it negative. From there, it will continue to go up until it reaches 1, thus restoring the value that means “do nothing”. At that point, we move on to the next place.
Now, when *\*q* is negative, we’re outputting. *\*q* is at exactly the right value so that we will output the right number of rows of characters before it reaches 1; if the largest digit is 2, we need to output 3 rows. Let’s see what happens at each value of *\*q*:
* ***\*q* = −2:**
	+ For the first number, *\*p* is −2, which indicates that we need to output a `'.'` (dot) or a `':'` (colon). We decide which by looking at *q*: if it’s −1, we’re in the ones place, so output a `':'` (which we calculate as `'8'`+2), otherwise a `'.'`.
	+ For the second number, *\*p* is −3. Anything that is not −2 means we output a `'|'` (pipe) and then increment the value. This way it will reach −2 in the right place and then we output `'.'`s/`':'`s for the rest of that digit.
	+ In each case, we also set a variable *pd* to 0 *before* we process the number, and set *pd* (= “printed”) to a non-zero value to indicate that we have printed a character.
	+ For the third number, no processing occurs because the third number does not have a hundreds place. In this case, *pd* will still be 0 after processing the number, indicating that we still need to output a `'|'` (but only if *out* is non-zero, because otherwise we’re still in stage 2 or 3).
	+ After processing all numbers, if *out* is non-zero, output a newline. Note that we need the *out* variable so that we don’t output the newline in stage 2 or 3.
* ***\*q* = −1:** Same as before, except that *\*p* is −2 for both the first two numbers, so both output a `'.'` (and the third outputs a `'|'` as before).
* ***\*q* = 0:** When *\*q* is 0, this means “do nothing if we’re in the ones place, otherwise output a row of `'|'`s regardless of *\*p*”. This way we get the padding between the digits.
Now we increment *q* to move on to the next place, the tens place, and increment *\*q* there. At the beginning of Stage 2, the tape looks like this:
[![enter image description here](https://i.stack.imgur.com/XCcjx.png)](https://i.stack.imgur.com/XCcjx.png)
Then we perform Stage 2 just as before. Remember this effectively subtracts 2 from every digit in this place and also leaves a unary number left of *\*r2* indicating the maximum digit. We leave the previous unary number alone and just keep extending the tape to the left; it would only cost unnecessary extra code to “clean up”. When we’re done and we increment *\*q*, at the start of Stage 3 the tape is now:
[![enter image description here](https://i.stack.imgur.com/bdtD5.png)](https://i.stack.imgur.com/bdtD5.png)
Actually, this is a lie. Remember earlier where I said we set *\*(r − 4)* to 1 and I didn’t tell you why? Now I’ll tell you why. It’s for cases like this one, where the largest digit is in fact 0, meaning *all* the digits in this place are 0. Setting *\*(r − 4)*, indicated by the unlabeled arrow above, to 1 extends the unary number by 1, but only in this special case. This way we pretend as if the largest digit was 1, which means we will output one extra row.
After Stage 3 (subtract maximum digit from all digits in the current place), including the extra step that makes *\*q* negative, the tape looks like this. Last time the biggest digit was represented by −2 in the *\*p* block, but this time they’re all −3 because they’re all actually zeroes but we’re pretending as if the maximum digit was a 1.
[![enter image description here](https://i.stack.imgur.com/f8JYS.png)](https://i.stack.imgur.com/f8JYS.png)
Now let’s see what happens as *\*q* progresses towards 1:
* When *\*q* = −1, the *\*p* values are all −3, which means we output `'|'`s and increment them.
* When *\*q* = 0, we output `'|'` because that’s what we always do when *\*q* = 0, regardless of *\*p*.
Thus, we get two rows of pipes.
Finally, we move *\*q* to the one’s place. This one gets interesting because we need to output `':'`s if the actual digit is anything but 1, but an `'8'` if it’s 1. Let’s see how the program proceeds. First, we increment *\*q* to initiate Stage 2:
[![enter image description here](https://i.stack.imgur.com/YYktx.png)](https://i.stack.imgur.com/YYktx.png)
After Stage 2 (“calculate maximum digit value”), we’re left with this:
[![enter image description here](https://i.stack.imgur.com/ObtMX.png)](https://i.stack.imgur.com/ObtMX.png)
After Stage 3 (“subtract maximum digit value from all digits in current place”) the tape looks like this:
[![enter image description here](https://i.stack.imgur.com/29VzO.png)](https://i.stack.imgur.com/29VzO.png)
Now let’s go through each iteration of *\*q* in turn:
* ***\*q* = −2:**
	+ First number: already at −2, so output a `':'` (rather than a `'.'` because *q* = −1).
	+ Second number: at −4, so output a `'|'` and increment.
	+ Third number: at −3, so output a `'|'`. However, this time, instead of incrementing, a special case triggers. *Only* if we’re outputting the last place (*q* = −1), *and* we’re in the second-last row for that (*\*q* = −2), *and* the digit is actually a 1 (*\*p* = −3), *then* instead of incrementing it to −2, we set it to −1. In other words, we use −1 as a special value to indicate that in the next iteration, we will need to output `'8'` instead of `':'`.
* ***\*q* = −1:**
	+ First number: already at −2, so output a `':'`.
	+ Second number: at −3, so output a `'|'`. The special condition does not trigger because *\*q* is no longer −2. Therefore, increment.
	+ Third number: at −1, so output `'8'`.
* ***\*q* = 0:** Ordinarily, we would output the padding row of `'|'`s here, but in the special case where we’re in the ones place (*q* = −1), we skip that.
After this, *q* is incremented to 0 and the big while loop ends.
Now you know how an input like `202,100,1` works. However, there’s one more special case that we still haven’t covered. You may remember that while we were processing the last place, when *\*p* was −3 we set it to −1 for the `1` (instead of incrementing it to −2) so that the next iteration would output an `'8'` instead. This only works because we have an iteration in which *\*p* is −3 and we make a decision as to whether to increment it or set it to −1. We *do not* have such an iteration if *all* of the digits in the ones place are 0 or 1. In such a case all the *\*p* values for the 1s would *start out* at −2; there is no opportunity to decide to set it to −1 *rather than to increment it from −3*. Because of this, there is another special-casing condition inside Stage 3 (“subtract maximum digit from every digit in the current place”). I claimed that after subtracting the maximum digit value from every digit (at which point the maximum digit is at −1), we just decrement it once more, but actually there’s a condition on that which goes as follows:
If the digit we’re looking at is equal to the maximum digit in this place (*\*p* = −1), *and* this place is the ones place (*q* = −1), *and* the maximum digit is 1 (*\*(r + 5)* = 0, i.e. the unary block at the very left is only 5 cells long), *only then* we leave *\*p* at −1 to indicate that the only iteration of the output must output an `'8'`. In all other cases we decrement it once more.
Done. Happy new year!
* **Edit 1 (3183 → 3001):** Some Happy New Year golfing! I managed to get rid of the variables *p2* and *r2* entirely! *p* now races back and forth to keep finding the beginning and end of numbers, but it seems to be shorter in code. I tried to get rid of *q2* as well, but I couldn’t make the code shorter that way.
I also found a few more places where I could apply typical Unreadable golfing tricks like re-using the last value of a while loop. To give you an example, instead of
```
while *(++p) { 1 }         // just increment p until *p is 0; the 1 is a noop
if (pd) { x } else { y }   // where pd is a variable
```
I can save the `'""""` (do the first, then the second) and the `'"""` (constant 1) by writing it in a way that is kind of like
```
if (while *(++p) { pd }) { x } else { y }
```
Of course, this only works if I know that the while loop will run for at least one iteration, but if it does, its return value is *pd* so I can use that as the condition for the if.
[Answer]
# Javascript (ES6) ~~750~~ ~~744~~ ~~690~~ ~~604~~ ~~498~~ ~~346~~ ~~245~~ 234 bytes
I'm new to PPCG and thought I might give this one a try thinking it was rather simple. Boy was I wrong!! I've been working on it for a while and I have **a lot of golfing** to do...  
Suggestions are encouraged! - although making sense of this won't be an easy task. 
Outputs ropes when the input is an array of numbers (eg: `[204, 1]` ).
```
a=>(o=m=s="",l=a.map(n=>(s+="|",l=(n+"").length)>m?m=l:l),h=[],a=a.map((n,i)=>[..."0".repeat(m-l[i])+n].map((d,j)=>d<h[j]?d:h[j]=d)),h.map((n,i)=>{i?o+=s+`
`:0;for(j=n;j--;o+=`
`)a.map(d=>o+="|.:8"[d[i]-j<1?0:i<m-1?1:d[i]-1?2:3])}),o)
```
## Explanation
```
a=>(
  o=m=s="",                      // m = max number of digits in a number, s = separator string         
  l=a.map(n=>(                   // l = lengths of each number
      s+="|",                    // set the separator string
      l=(n+"").length                 // convert each number to a string
    )>m?m=l:l                    // get max length
  ),
  h=[],
  a=a.map((n,i)=>
    [..."0".repeat(m-l[i])+n]    // add leading 0s to make all same length
    .map((d,j)=>d<h[j]?d:h[j]=d) // set each digit of h to max
  ),
  h.map((n,i)=>{
    i?o+=s+`
`:0;
    for(j=n;j--;o+=`
`)
      a.map(d=>
        o+=
          "|.:8"[
            d[i]-j<1?0
            :i<m-1?1
            :d[i]-1?2:
            3
          ]
      )
  }),
  o
)
```
## Example
**Input:** Array of numbers: `[4,8,15,16,23,42]`  
**Output:**
```
|||||.
|||||.
||||..
||....
||||||
|:||||
|:||||
|:|:||
|:::||
::::||
:::::|
::::::
::::::
```
[Answer]
# Python 3, ~~624~~ ~~598~~ ~~595~~ ~~574~~ ~~561~~ ~~535~~ ~~532~~ ~~527~~ ~~525~~ ~~426~~ ~~345~~ ~~328~~ ~~324~~ ~~294~~ ~~288~~ ~~286~~ ~~283~~ ~~280~~ ~~267~~ ~~265~~ ~~255~~ ~~251~~ ~~245~~ ~~238~~ ~~235~~ ~~234~~ ~~230~~ ~~228~~ ~~236~~ 244 bytes
```
z=input().split();v=max(map(len,z));d=''.join(i.zfill(v)for i in z);x=['']*len(z)
for k,c in enumerate(d):j=k%v;m=int(max(d[j::v]));c=int(c);x[k//v]+="|"*(m-c+(2>v)+(j>0)+0**(m+(j>v-2)))+":8."[(c<2)|-(j<v-1)]*c
for r in zip(*x):print(*r,sep='')
```
[Try it online!](https://tio.run/##HY/daoQwEIXv@xRBKM7Enx21haKmLyJeSHTZ@BOD6wYr@@42ejXMOcN3zpi/9THr7Dh2obR5rYDx04zKzcKKqdlgagyMnQ53xKIVvh/3s9Kg4v2uxhEs3ueFKaY027HYROX7NXfnsOPH6QyhPL1Ov6ZuadYOWsx7MXzaYnJ5K5wJbdXnua0dX16adKBquN1sHQjv7XGYIhlA/0sYEHdbAGlpkTvFRikiBl7@E3sVyDLFdwR9aaMEay6vAstVTRngG@ZmOfl8CZ@dca/gcXxTQiyjlFhKGbGEvojRPw)
Well, since this question needed an answer, I have provided one here, where the input should be a space-separated string of numbers, such as `"204 1"`. Boy, is it a long one. Any golfing suggestions (or better answers) are welcome.
**Edit:** Bytes saved by mixing tabs and spaces.
**Edit:** I saved a *lot* of bytes by changing how I obtained the digits of a number (make a list from the zero-padded string of the number, then in the body of the code, transpose to get hundreds digits, ten digits, etc.)
**Edit:** And I saved some more by incorporating that last `:8` loop into the main quipu loop. ~~Now if only I could figure out why `b=d[j*v+i]==m(d[i::v])` won't work.~~ Figured it out and the solution takes too many bytes. (Also the byte count dropped because somehow the tabs turned back into four spaces. It was probably the code block formatting on this site)
**Edit:** I reorganized how made the quipus. Now it creates one strand at a time, then transposes for printing.
**Edit:** Turned my answer back into a Python 3 program to save some more bytes.
**Edit:** I found a bug in my code that made it so that it was not printing zeroes in the middle of numbers correctly (see test case `204 1` above). In fixing this, I managed to golf it :)
**Edit:** I changed the printing to save 10 bytes. And I put all of the old byte counts back, just because.
**Edit:** Golfed the assignment of `v` using `map` for four bytes. Credit to [CarpetPython](https://codegolf.stackexchange.com/users/31785/carpetpython), as I got the idea from their answer [here](https://codegolf.stackexchange.com/a/69615/47581).
**Edit:** Turned the middle "for loop inside a for loop", into just one for loop for six bytes.
**Edit:** Now using `enumerate`. No longer using `l=len(z)`. Turned the ternary `if-else` into a list ternary. See below for details.
**Edit:** Sp3000 suggested an edit to `print` and an edit to the ternary condition that saved a byte each.
**Edit:** +6 bytes fixing a bug where the code did not follow rule 7. +8 more bytes in continuing to fix this bug. O bytes from more bug fixes.
**Ungolfed:**
```
s = input()
z = s.split()
v = max(map(len, z))                # the amount of digits of the largest number
d = ''.join(i.zfill(v) for i in z)  # pad zeroes until every number is the same length
                                     # then join the numbers into one string
x = ['']*len(z)                     # a list of strings for the output, one for each number
for k,c in enumerate(d):          # for every digit in every number
    i,j = divmod(k, v)            # i is the index of the number we're on
                                   # j is the index of the digit of the number we're on
    m = int(max(d[j::v]))         # the largest of all the digits in the j-th place
    c = int(c)                    # the digit in j-th place of the i-th number
    x[i] += "|"*(m-c+(j>0)+0**(m+(2>v)*(j>v-2)))
                                  # pad | to size m-c, until the knots are correctly spaced
                                  # add a | if all inputs are single-digit numbers
                                  # add a | if j>0, if it's not at the start, as delimiters
                                  # add a | if the maximum is 0 and it's *NOT* the ones digit
    x[i] += ":8."[(c<2)|-(j<v-1)] * c
    # this is essentially the following code
    # if j<v-1:
    #     x[i] += "."*c      # . knots if not in the units place
    # else:
    #     if c == 1:
    #         x[i] += "8"*c  # 8 knots for ones in the units place
    #     else:
    #         x[i] += ":"*c  # : knots for something else is in the units place
for r in zip(*x):       # transpose so all the rows (the quipu strings) now hang down
    print(*r, sep='')    # join the strings together at each knot
                         # and print each on a separate line
```
[Answer]
# C, 238 235 bytes
Relying heavily on the C preprocessor to make the code as short as possible. As a side effect, also makes it pretty much unreadable.
```
#define l strlen(a[i])
#define d l<k||a[i][l-k]-48
#define m(e) for(i=1;a[i];e<=r?i++:r++);
#define p(e) {m(!putchar(e?'|':k>1?46:d<2?56:58))puts("");}
k,r;main(int i,char**a){m(l)for(k=r,r=1;k;r=k>1){m(d)for(;r;r--)p(d<r)if(--k)p(1)}}
```
On Ubuntu 14.04, you can compile the code with a straightforward `gcc quipu.c` (please ignore the warnings). An example of running the executable:
```
$ ./a.out 1 2 3 2 1
||:||
|:::|
8:::8
```
Tested against all OP's test cases.
Ungolfed source code:
```
// Standard library; leaving out the includes still gives executable code despite the warnings.
#include <stdio.h>
#include <string.h>
// 4 preprocessor macros.
// Note: some of these actually make use of the fact that parentheses have been left out
// l: length of argument i
#define l     strlen(a[i])
// d: shorthand for a digit
#define d     l<k || a[i][l-k]-'0'
// m: loop across all arguments; calculates r as the maximum of expression e
#define m(e)  for (i=1; a[i]; e<=r ? i++ : r++);
// p: prints one line of output
// note: intentionally does not use the r++ code branch of m;
//       putchar always returns a non-zero number here, so !putchar is zero,
//       which is always <=r (because r is never negative)
// note: the semicolon after m(...) is redundant;
//       the definition of m already contains a semicolon
// note: puts("") outputs a newline
#define p(e)  { m(!putchar(e ? '|' : k > 1 ? '.' : d < 2 ? '8' : ':')); puts(""); }
// k: knot position; 1 for units, 2 for tens, 3 for hundreds...
int k;
// r: input and output value for m
// note: the first time we call m, we need r to be zero;
//       by defining it outside main, it is automatically initialized as such
int r;
// function main
// note: parameter i (normally named argc by convention) is not needed
//       (the last element of argv is known; it is followed by a NULL pointer)
//       but we cannot leave it out (otherwise we cannot access argv)
//       so it serves as a local variable (to loop through arguments; see m)
// note: parameter a (normally named argv by convention)
//       is the array of arguments (starting from index 1)
int main(int i, char **a)
{
    // Determine the longest argument; store its length in r.
    // This is the number of knot positions to consider.
    m(l)
    // Iterate k through the knot positions from top to bottom.
    // Note: k > 0 has been abbreviated to k.
    // Note: for each iteration, we also initialize r with either 0 or 1.
    //       0 = suppress printing when all knots are zero
    //       1 = always print, even when all knots are zero
    for (k = r, r = 1; k > 0; r = k > 1)
    {
        // Determine the highest digit at this knot position.
        // Note: due to the absence of parentheses, d mixes up with <=r into:
        // (l < k) || (a[i][l-k]-'0' <= r)
        m(d)
        // Count the digits down.
        for (; r; r--)
        {
            // Print a single line of output.
            // When d (the digit in the current strand) is less than the counter,
            // then print a '|', otherwise print a knot.
            p(d < r)
        }
        // Decrement k (go to next knot position).
        // If this was not the last iteration...
        if (--k > 0)
        {
            // Print separator line.
            p(1)
        }
    }
    // Return exit code zero; redundant.
    return 0;
}
```
[Answer]
# Mathematica ~~436 453 357 352~~ 347 bytes
```
t=Transpose;y=ConstantArray;a=Table;
g@j_:=(s=t[PadLeft[#,Max[Length/@i]]&/@(i=IntegerDigits@#)]&;p_~u~o_:=(m=Max@p;c=If[o==-2,":","."];w=If[o==-2,"8","."];p//.{0->a["|",Max@{1,m}],1->Join[a["|",{m-1}],{w}],n_/;MemberQ[2~Range~9,n]:>Join[y["|",m-n ],c~y~n]});t[Join@@@t@Join[u[#,0]&/@Most@#,u[#,-2]&/@{#[[-1]]}]]&[Riffle[(s@j),{a[0,Length@j]}]]//Grid)
```
The above
* Breaks up each integer into a list of digits, using `IntegerDigits`; pads each number with zeros to the left (to ensure equal spacing); each input number, now decomposed into digits, corresponds to a row of an array; each column represents a place value. Array is transposed.
* Replaces digits with a list of knots with padding. A slightly different pattern-matching routine is used for the units.
---
**Example**
```
g[Range[0, 50]]
```
[![fifty](https://i.stack.imgur.com/0OSPi.png)](https://i.stack.imgur.com/0OSPi.png)
[Answer]
# Pyth, 64 bytes
```
=QjRTQjCmj\|+:R"[8:]"\.PdedCmm+*\|-h.MZdk*?tk\:\8kdC.[L0h.MZlMQQ
```
[Try it online!](http://pyth.herokuapp.com/?code=%3DQjRTQjCmj%5C%7C%2B%3AR%22%5B8%3A%5D%22%5C.PdedCmm%2B%2A%5C%7C-h.MZdk%2A%3Ftk%5C%3A%5C8kdC.%5BL0h.MZlMQQ&input=1%2C12%2C21%2C32%2C41%2C132%2C221%2C312%2C401%2C312%2C221%2C132%2C41%2C32%2C21%2C12%2C1&debug=0)
## How it works
```
=QjRTQ   Converts each number in input to decimal (as a list)
         123 becomes [1,2,3]
----
jCmj\|+:R"[8:]"\.PdedCmm+*\|-h.MZdk*?tk\:\8kdC.[L0h.MZlMQQ
                                              .[L0       Q  0-leftpad each #
                                                  h.MZlMQ   (max length) times
                                             C              transpose
                      mm                    d    for each digit:
                        +                        [convert to] sum of
                         *\|                     "|" repeated
                            -                    the difference of
                             h.MZd               maximum digit in same row
                                  k              and itself.. that many times
                                   *?tk\:\8      and (digit > 1 then ":" or "8") repeated
                                           k     itself many times
the list:
[11,23,52]
->[[1,1],[2,3],[5,2]]
->[[1,2,5],[1,3,2]]
->[["||||8","|||::",":::::"],["||8",":::","|::"]]
                     C      transpose
->[["||||8","||8"],["|||::",":::"],[":::::","|::"]]
  m                          for each number
      +                      [convert to] sum of
                 Pd          every element but the last
       :R"[8:]"\.            with "8" and ":" replaced by "."
                   ed        and the last element
   j\|                       joined with "|"
  C                          transpose
 j                           join (with newlines)
```
[Answer]
# R - 446 444
I see there is no R solution yet, so here is one. The function takes a vector with integers.
```
function(x){r=nchar(max(x));c=length(x);m=matrix(0,r,c);for(i in 1:c){t=as.numeric(strsplit(as.character(x[i]),"")[[1]]);m[(r+1-length(t)):r,i]=t};Q=c();for(i in 1:r){d=m[i,];z=ifelse(max(d)>0,max(d),1);q=matrix("|",z,c);for(j in 1:c){v=m[i,j];if(i==r){if(v==1)q[z,j]=8;if(v>1)q[(z-v+1):z,j]=rep(":",v)};if(i<r){if(v>0)q[(z-v+1):z,j]=rep(".",v)}};if(i!=1&sum(d)>0)q=rbind(rep("|",c),q);Q=rbind(Q,q)};for(i in 1:nrow(Q))cat(Q[i,],sep="",fill=T)}
```
## Ungolfed
```
# Some test data
test <- c(201, 0, 100, 222, 53)
# Define function
quipu <- function (x) {
    # Create matrix with a row for each digit and a column for each number
    r=nchar(max(x));c=length(x);m <- matrix(0,r,c)
    for(i in 1:c) {
        t=as.numeric(strsplit(as.character(x[i]),"")[[1]])
        m[(r+1-length(t)):r,i]=t
    }
    # Loop through each row (digit) starting at the top of the quipu
    Q=c() # Empty matrix to store quipu 
    for(i in 1:r){
        d=m[i,]
        z=ifelse(max(d)>0,max(d),1)
        q=matrix("|",z,c)
        # Loop through each column (number in the vector) starting at the leftmost quipu
        for(j in 1:c){
            # The digit
            v=m[i,j]
            # If it is the last segment of the quipu
            if(i==r){
                if(v==1){q[z,j]=8} # If unit digit =1
                if(v>1){q[(z-v+1):z,j]=rep(":",v)} # If unit digit >1               
            }
            # If it is not the last segment of the quipu
            if(i<r){
                if(v>0){q[(z-v+1):z,j]=rep(".",v)} # If non-unit digit >0   
            }
        }
        # Add segment to Q
        if(i!=1 & sum(d)>0){q=rbind(rep("|",c),q)}
        Q=rbind(Q,q)    
    }
    # Print quipu
    for(i in 1:nrow(Q)) {cat(Q[i,], sep="", fill=T)}
}
# Test
quipu(test)
```
[Answer]
# [APL (Dyalog Unicode)](https://www.dyalog.com/), ~~160~~ ~~137~~ ~~132~~ ~~128~~ ~~123~~ ~~117~~ ~~126~~ ~~119~~ 116 bytes
I sort of wrote this in a frenzy while unable to sleep tonight, so please forgive me if I skip the explanation, and miss any bugs or golfing opportunities. I'll take another look at this after I've gotten some sleep.
**Edit:** ~~Turns out this doesn't for an input of just `0`. Will fix soonest.~~ -23 bytes from bug fixing! -5 bytes from removing no-longer-used variables. -4 bytes from using parts of the approach used in [Leaky Nun's excellent Pyth answer](https://codegolf.stackexchange.com/a/79589/47581) (Sidenote: this answer is now exactly twice as long as that answer :D). -5 bytes from rearranging the result matrix mid-computation. -6 bytes from slimming down the generation of the result matrix even further. +9 bytes bug fixing. 0 bytes from more bug fixing. -7 bytes from rearranging how to handle the ones digit special case. -3 bytes from continued rearranging.
```
{⎕IO←0⋄m←⊖↓10⊥⍣¯1⊢1,⍵⋄1↓⊃⍪/⌽{w←1↓⊃m[⍵]⋄M+←1+0*((1≠≢m)∧⍵=0)+M←⌈/w⋄⊃,/⍵{M 1⍴((M-⍵)/'|'),⍵/'.'(':8'[1=⍵])[0=⍺]}¨w}¨⍳≢m}
```
[Try it online!](https://tio.run/##ZY@/SgNBEMb7PMV2e@cl3szexjuEVDamOARjF1IE5GxyaBckpoocyZkLimgtEVFsUqiNYJNHmRc5Z9TmYrF/5pvf981u/2zQOD7vD05PynJEi7v2AWXXQFeXKZ@U31N2i0D5ExWP6xVSvsQ6FR/cR@5QPqHi1af512jI@J@UdpnoMRJ7Inqw5ThIsweaLVOXps/cbYHrxTJgPvWHTLKr7rM@ihVS8e44cYMr19cX2pV5vt7Wjt6NdBdbEu52gc/P3nj9MuRFxZuEj8tEMovF7z/yyXoVUHbDVedwj/ej/XanTBTU@M6g1rVENVWgjMKqiBUCVADKgELYoFQksrEKm6ppqx4xiUts/3ymyqLAkgSB4HaDN2BlVlXiF/8kG1MJQwiD0GJk@FdoQxsFOzb8Bg "APL (Dyalog Unicode) – Try It Online")
**Explanation**
```
quipu←{
    ⎕IO←0         ⍝ Set to 0-indexing
    nums ← 1,⍵    ⍝ Append a 1 so that when converting in the next line,
                  ⍝ we always get a matrix, not a vector
    digit_list ← ⊖ ↓ 10⊥⍣¯1⊢ nums    ⍝ All the digits in each digit place, reversed
    ⍝ For each digit in each digit place, create knots
    1↓⊃⍪/⌽{    ⍝ After generating knots, these instructions clean up the result
        q ← ⍵
        w ← 1↓ ⊃ digit_list[⍵]    ⍝ 1↓ drops the 1 we appended before
        M +← 1+ 0* ((1 = ≢digit_list) ∧ (⍵ ≠ 0)) + M ← ⌈/w
        ⍝ M is the number of knot spaces (including delimiters) needed
        ⊃,/{     ⍝ ,/ concatenates the knots
            padding ← ((M-⍵) 1)⍴'|'
            knots ← ⍵ / ('.' (':8'[1=⍵]))[q=0]
            (M 1) ⍴ knots ⍪ padding   ⍝ Turns the knots into a column vector 
                                      ⍝ of shape M by 1
        }¨w
    }¨⍳≢digit_list
}
```
]