# ASCII Maya Numbers

Write a program or function which, given a positive integer as input, outputs the representation of that integer in Maya numerals.

# Maya numerals

Maya numerals is a vigesimal system (base 20) using only 3 symbols :

• < > for Zero (The correct symbol is some sort of shell that can't easily be represented using ASCII).
• . for One
• ---- for Five

Numbers are written vertically in powers of 20, and numbers between 0 and 19 are written as stacks of fives and ones. You shall refer to the Wikipedia article for more details.

As an example, here are the numbers between 0 and 25, separated by commas:

                                                                                 .    ..  ...  ....
.    ..  ...  .... ---- ---- ---- ---- ----  .    .    .    .    .    .
.    ..  ...  .... ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
<  >, .  , .. ,... ,....,----,----,----,----,----,----,----,----,----,----,----,----,----,----,----,<  >, .  , .. ,... ,....,----


# Inputs

• Inputs always are positive integers between 0 and 2147483647 (2^31 - 1).
• You may take the input from STDIN, as a command line argument, function parameter or anything similar.

# Outputs

• Each line is at most 4 characters long. < > and ---- should always be printed as given here (4 characters long each).
• Ones(.) must be centered on the line. If there are 1 or 3 ., since perfect horizontal alignment is impossible, it does not matter whether they are one column to the left or one column to the right or the center.
• There must be exactly one empty line between different powers of 20, regardless of the height of the stacks in the power of 20s. For example, the correct output for 25 and 30 are :

        .
.
----
----   ----

• No leading or trailing lines allowed.

• Outputs must be printed exactly like they are in the examples given.

### Test cases

• Each individual number between 0 and 25 given as example above.

• Input: 42

Output:

 ..

..

• Input: 8000

Output:

 .

<  >

<  >

<  >

• Input: 8080

Output:

 .

<  >

....

<  >

• input: 123456789

Output:

 .

...
----
----
----

.
----
----

..
----
----

.

....
----
----
----

....
----

• Input: 31415

Output:

...

...
----
----
----

----
----

----
----
----

• Input: 2147483647

Output:

 .

...
----
----

.
----
----

.

----
----
----

....
----

..

..
----


# Scoring

This is , so the shortest code in bytes wins.

• 15 and 20 appear to be identical. Aug 4, 2015 at 16:16
• @isaacg Thanks, I actually had 15 appearing both at the right spot and also between 19 and 20. Fixed. Aug 4, 2015 at 16:19
• @Fatalize Does the output need to be printed (i.e. STDOUT) or can my function just return the output? Aug 4, 2015 at 17:52
• @rink.attendant.6 Must be printed exactly like they are in the post. Aug 4, 2015 at 18:23
• Is it ok if 1 is one column to the right, but 3 is one column to the left? Aug 6, 2015 at 16:45

# Perl, 125 117 bytes

$-=<>;{$_=($-%20?(""," . "," .. ","... ",".... ")[$-%5]."----
"x($-/5&3):"< > ").$_;($-/=20)&&($_=$/.$_,redo)}print


Thanks to Dom Hastings for helping me save 8 bytes.

• Hey samgak, a couple more byte saving changes, rather than redo,if(int($i/=20)) you can use ~~($i/=20)&&redo. ~~ converts to int - you could also use 0| at the beginning (or |0 at the end). Also replacing substr(".... ... .. . ",20-$i%5*5,5) with (' .', ' ..','...','.'x4)[$i%5-1].$/ seems to work ok, but I haven't tested all the test cases... If those work, you're down to 114... If I think of anything else to share I'll let you know! Aug 5, 2015 at 10:59 • Thinking about it, if you only ever want to have an integer and ignore the remainder, you can use the magic variable $- which will always truncate to an int... might save a few more! Aug 5, 2015 at 11:47
• @DomHastings thanks, the int conversion trick saves a couple of bytes, and getting rid of substr saves another 4. The newlines need to go inside the string constants because there has to be no newline in the case where there are no dots. Aug 5, 2015 at 11:47
• @DomHastings indeed it does, thanks again! I guess that means I have to get rid of my end of the world joke Aug 5, 2015 at 11:48

# JavaScript ES6, 143 bytes

Loads of bytes added because need to console.log, could save another 23 bytes without it.

n=>console.log((d=(x,s='',l=1,j=x/l|0)=>s+(j>19?d(x,s,l*20)+

:'')+((j=j%20)?(' '+..repeat(j%5)).slice(-4)+
----.repeat(j/5):'<  >'))(n))


# Mathematica 185 182 171 153

With 18 bytes saved thanks to Arcinde's suggestion to use anonymous functions,

c=Column;c[If[#>0,{q,r}=#~QuotientRemainder~5;c@{{""," ."," .."," ...","...."}[[r+1]],c@{{""},{d="----"},{d,d},{d,d,d}}[[q+1]]},"< >"]&/@#~IntegerDigits~20]&


Example

c[If[# > 0, {q, r} = #~QuotientRemainder~5; c@{{"", " .", " ..", " ...", "...."}[[r + 1]], c@{{""}, {d = "----"}, {d, d}, {d, d, d}}[[q + 1]]}, "< >"] & /@ #~IntegerDigits~20] &[31415]


Checking

The decimal number, 31415, expressed in base 20. Mathematica employs lower case for this.

BaseForm[31415, 20]


The decimal digits corresponding to the above base 20 number.

IntegerDigits[31415,20]


{3, 18, 10, 15}

Another example

IntegerDigits[2147483607, 20]


{1, 13, 11, 1, 15, 9, 0, 7}

c[If[# > 0, {q, r} = #~QuotientRemainder~5;c@{{"", " .", " ..", " ...","...."}[[r + 1]], c@{{""}, {d = "----"}, {d, d}, {d, d, d}}[[q + 1]]},"< >"] & /@ #~IntegerDigits~20] &[2147483607]


• c=Column;c[If[#>0,{q,r}=#~QuotientRemainder~5;c@{{""," ."," .."," ...","...."}[[r+1]],c@{{""},{d="----"},{d,d},{d,d,d}}[[q+1]]},"< >"]&/@#~IntegerDigits~20]& using anonymous functions.
– jcai
Aug 5, 2015 at 12:48
• @Arcinde, Thanks. Good old anonymous functions. Aug 5, 2015 at 13:04

# Pyth, 41 bytes

j+bbm|jb_m.[\ 4kc:*d\.*5\.*4\-4"<  >"jQ20


Try it online: Demonstration

### Explanation:

                                     jQ20   convert input to base 20
m                                       map each value d to:
*d\.                         string of d points
:    *5\.*4\-                 replace 5 points by 4 -s
c             4                split into groups of 4
m.[\ 4k                               center each group
_                                      invert order
jb                                       join by newlines
|                         "<  >"          or if 0 use "<  >"
j+bb                                        join by double newlines


## JavaScript (ES6), 157 bytes

The newlines are significant and are counted as 1 byte each. Since printing to STDOUT is required, console.log cost me a few bytes there.

f=i=>console.log([...i.toString(20)].map(j=>(!(j=parseInt(j,20))||j%5?[<  >, ., ..,...,....][j%5]+
:'')+----
.repeat(j/5)).join
.slice(0,-1))


### Demo

For demonstration purposes I'll write an ES5 version so it works in all browsers:

// Snippet stuff
console.log = function(x) {
O.innerHTML = x;
}
f(this.valueAsNumber);
}, false);

// Actual function
f = function(i) {
console.log(i.toString(20).split('').map(function(j) {
return (! (j = parseInt(j, 20)) || j % 5 ? ['<  >', ' .', ' ..', '...', '....'][j % 5] + '\n' : '') + '----\n'.repeat(j / 5);
}).join('\n').slice(0,-1));
}
<input type=number min=0 max=2147483647 value=0 id=I>

<pre><output id=O></output></pre>

• Does the last .join need parenthesis? Aug 4, 2015 at 20:25
• 137 bytes: pastebin.com/3Zgg4qtX Aug 4, 2015 at 21:14
• @vihan That doesn't output anything, it just returns. Aug 4, 2015 at 21:21
• didn't know that was a requirement as input can be through a function. pastebin.com/0pS0XtJa is 3 bytes shorter Aug 4, 2015 at 21:23

# Python 2.x, 142 bytes:

def m(n):
h=[[""," "*(2-n%5/2)+"."*(n%5)+"\n"][n%5!=0]+"----\n"*(n%20/5),"<  >\n"][n%20==0]
n/=20
if n>0:
h=m(n)+"\n\n"+h
return h[:-1]


Example:

>>> print m(2012)
----

<  >

..
----
----
>>>


Edit: trailing line...

• I have a few improvements to suggest. First, change [n%20==0] to [n%20<1]. Second, change [[""," "*(2-n%5/2)+"."*(n%5)+"\n"][n%5!=0] to h=[(" "*(2-a/2)+"."*a+"\n")*(a>0)+"----\n"*(n%20/5),"< >\n"][n%20<1] with a=n%5, which moves all the n%5s out, and changes the conditional to *(a>0) which returns an empty string at a==0 for the same effect. Dec 31, 2015 at 13:30
• Finally, put a, h, and n on one line, like so: a=n%5;h=[(" "*(2-a/2)+"."*a+"\n")*(a>0)+"----\n"*(n%20/5),"< >\n"][n%20<1];n/=20. All this should leave you with 131 bytes. Dec 31, 2015 at 14:01

# CJam, 82 76 bytes

li{_K%[""" .
"" ..
""...
""....
"]1$5%='-4*N+2$5/3&*+"<  >
"?N+L+:L;K/}h;L);


My first CJam program, basically just a transliteration of my Perl answer to CJam.

Try online

li            # read input
{             # start of do-while loop
_K%         # check if this base-20 digit is a zero
[""" .
"" ..
""...
""....
"]1$5%= # push dots for 1s onto stack '-4*N+2$5/3&*+    # push lines for 5s onto stack

"<  >
"         # push zero on stack
?           # ternary if test (for digit equals zero)
N+L+:L;     # pre-concatenate string with output plus newline
K/          # divide by 20
}h            # end of do while loop
;L);          # push output string on stack, chop off trailing newline


## PHP, 220 bytes

Same approach as my JavaScript answer. PHP has built-in functions for everything.

Takes 1 input from the command line (i.e. STDIN), as seen with $argv[1]: <?=rtrim(implode("\n",array_map(function($j){return(!($j=base_convert($j,20,10))||$j%5?['< >', ' .', ' ..', '...', '....'][$j%5]."\n":'').str_repeat("----\n",$j/5);},str_split(base_convert($argv[1],10,20)))));


## C - 149

f(v){v/20&&(f(v/20),puts("\n"));if(v%=20)for(v%5&&printf("%3s%s","...."+4-v%5,v/5?"\n":""),v/=5;v--;v&&puts(""))printf("----");else printf("<  >");}


Uses recursion to print most significant numbers first. Then either prints zero or prints all dots with one clever printf and all fives in a loop. I am not sure if I can avoid using if-else here.

The downside of clever printf is that 1 and 3 are not aligned to each other:

Result for 23 is:

  .

...


## 119 incorrect solution - trailing newline

f(v){v/20&&(f(v/20),puts(""));if(v%=20)for(v%5&&printf("%3s\n","...."+4-v%5),v/=5;v--;)puts("----");else puts("<  >");}

• Dec 27, 2018 at 4:34
• building on @JonathanFrech 140 bytes Jul 4, 2020 at 1:41

# PHP, 202 192 bytes

function m($n){return(($c=($n-($r=$n%20))/20)?m($c)."\n":"").
($r?(($q=$r%5)?substr(" . .. ... ....",$q*4-4,4)."\n":"").
str_repeat("----\n",($r-$q)/5):"<  >\n");}
echo rtrim(m($argv[1]),"\n");  It gets the input from the first command line argument. The complete source code, with comments and tests, is available on github. • \n is two characters - but a newline in the middle of a string is only one. Sep 11, 2015 at 12:50 # Python 2, 114 bytes This answer is based on Jakube's Pyth answer and Locoluis's Python 2 answer. def f(n):d,m=divmod(n%20,5);h=[" "*(2-m/2)+"."*m+"\n----"*d,"< >"][n%20<1];n/=20;return(f(n)+"\n\n"if n else"")+h  # Jelly, 5049 47 bytes b5µṪ”.x⁶;$L<3Ɗ¡ṭ⁾--;UW¤ẋ$Ẏ$L¡Ṛø“<  >”WµẸ?
b20Ç€


Try it online!

... is now left aligned thanks to user202729's point.

When you trick yourself into thinking < > is a palindrome...

• Why do you right-align ...... Apr 6, 2018 at 2:43
• @user202729 For . and .. there needs to be a space so I put one for ... too. Is there a shorter way? Apr 6, 2018 at 2:46
• I think that according to the challenge spec ... should not be right-aligned. Just change <4 to <3? Apr 6, 2018 at 3:02
• @user202729 You’re right, I think I misread the spec. I don’t think it specifies for ... but I can change it to <2 Apr 6, 2018 at 4:03