Mayan numerals

From Rosetta Code
Revision as of 20:34, 20 January 2019 by SqrtNegInf (talk | contribs) (Added Perl example)
Mayan numerals is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Task

Present numbers using the Mayan numbering system   (displaying the Mayan numerals in a cartouche).


Mayan numbers

Normally, Mayan numbers are written vertically   (top─to─bottom)   with the most significant numeral at the top   (in the sense that decimal numbers are written left─to─right with the most significant digit at the left).   This task will be using a left─to─right (horizontal) format,   mostly for familiarity and readability,   and to conserve screen space (when showing the output) on this task page.


Mayan numerals

Mayan numerals   (a base─20 "digit" or glyph)   are written in two orientations,   this task will be using the "vertical" format   (as displayed below).   Using the vertical format makes it much easier to draw/construct the Mayan numerals (glyphs) with simple dots (.) and hyphen (-);     (however, round bullets () and long dashes () make a better presentation on Rosetta Code).


Furthermore, each Mayan numeral   (for this task)   is to be displayed as a cartouche   (enclosed in a box)   to make it easier to parse (read);   the box may be drawn with any suitable (ASCII or Unicode) characters that are presentable/visible in all web browsers.


Mayan numerals added to Unicode

Mayan numerals (glyphs) were added to the Unicode Standard in June of 2018   (this corresponds with version 11.0).   But since most web browsers don't support them at this time,   this Rosetta Code task will be constructing the glyphs with "simple" characters and/or ASCII art.


The "zero" glyph

The Mayan numbering system has the concept of   zero,   and should be shown by a glyph that represents an upside─down (sea) shell,   or an egg.   The Greek letter theta   (Θ)   can be used   (which more─or─less, looks like an egg).   A   commercial at   symbol   (@)   could make a poor substitute.


Mayan glyphs (constructed)

The Mayan numbering system is a   [vigesimal (base 20)]   positional numeral system.


The Mayan numerals   (and some random numbers)   shown in the   vertical   format would be shown as
      ╔════╗                      ╔════╗                            ╔════╦════╗
      ║    ║                      ║    ║                            ║    ║    ║
      ║    ║                      ║ ∙  ║                            ║    ║    ║
 1──► ║    ║                11──► ║────║                      21──► ║    ║    ║
      ║ ∙  ║                      ║────║                            ║ ∙  ║ ∙  ║
      ╚════╝                      ╚════╝                            ╚════╩════╝
      ╔════╗                      ╔════╗                            ╔════╦════╗
      ║    ║                      ║    ║                            ║    ║    ║
      ║    ║                      ║ ∙∙ ║                            ║    ║    ║
 2──► ║    ║                12──► ║────║                      22──► ║    ║    ║
      ║ ∙∙ ║                      ║────║                            ║ ∙  ║ ∙∙ ║
      ╚════╝                      ╚════╝                            ╚════╩════╝
      ╔════╗                      ╔════╗                            ╔════╦════╗
      ║    ║                      ║    ║                            ║    ║    ║
      ║    ║                      ║∙∙∙ ║                            ║    ║    ║
 3──► ║    ║                13──► ║────║                      40──► ║    ║    ║
      ║∙∙∙ ║                      ║────║                            ║ ∙∙ ║ Θ  ║
      ╚════╝                      ╚════╝                            ╚════╩════╝
      ╔════╗                      ╔════╗                            ╔════╦════╗
      ║    ║                      ║    ║                            ║    ║    ║
      ║    ║                      ║∙∙∙∙║                            ║    ║    ║
 4──► ║    ║                14──► ║────║                      80──► ║    ║    ║
      ║∙∙∙∙║                      ║────║                            ║∙∙∙∙║ Θ  ║
      ╚════╝                      ╚════╝                            ╚════╩════╝
      ╔════╗                      ╔════╗                            ╔════╦════╗
      ║    ║                      ║    ║                            ║    ║    ║
      ║    ║                      ║────║                            ║    ║    ║
 5──► ║    ║                15──► ║────║                      90──► ║    ║────║
      ║────║                      ║────║                            ║∙∙∙∙║────║
      ╚════╝                      ╚════╝                            ╚════╩════╝
      ╔════╗                      ╔════╗                            ╔════╦════╗
      ║    ║                      ║ ∙  ║                            ║    ║    ║
      ║    ║                      ║────║                            ║    ║    ║
 6──► ║ ∙  ║                16──► ║────║                     100──► ║    ║    ║
      ║────║                      ║────║                            ║────║ Θ  ║
      ╚════╝                      ╚════╝                            ╚════╩════╝
      ╔════╗                      ╔════╗                            ╔════╦════╗
      ║    ║                      ║ ∙∙ ║                            ║    ║    ║
      ║    ║                      ║────║                            ║    ║    ║
 7──► ║ ∙∙ ║                17──► ║────║                     200──► ║────║    ║
      ║────║                      ║────║                            ║────║ Θ  ║
      ╚════╝                      ╚════╝                            ╚════╩════╝
      ╔════╗                      ╔════╗                            ╔════╦════╗
      ║    ║                      ║∙∙∙ ║                            ║    ║    ║
      ║    ║                      ║────║                     300──► ║────║    ║
 8──► ║∙∙∙ ║                18──► ║────║                            ║────║    ║
      ║────║                      ║────║                            ║────║ Θ  ║
      ╚════╝                      ╚════╝                            ╚════╩════╝
      ╔════╗                      ╔════╗                            ╔════╦════╦════╗
      ║    ║                      ║∙∙∙∙║                            ║    ║    ║    ║
      ║    ║                      ║────║                     400──► ║    ║    ║    ║
 9──► ║∙∙∙∙║                19──► ║────║                            ║    ║    ║    ║
      ║────║                      ║────║                            ║ ∙  ║ Θ  ║ Θ  ║
      ╚════╝                      ╚════╝                            ╚════╩════╩════╝
      ╔════╗                      ╔════╦════╗                       ╔════╦════╦════╦════╗
      ║    ║                      ║    ║    ║                       ║    ║    ║    ║    ║
      ║    ║                      ║    ║    ║                       ║    ║    ║    ║    ║
10──► ║────║                20──► ║    ║    ║             16,000──► ║    ║    ║    ║    ║
      ║────║                      ║ ∙  ║ Θ  ║                       ║ ∙∙ ║ Θ  ║ Θ  ║ Θ  ║
      ╚════╝                      ╚════╩════╝                       ╚════╩════╩════╩════╝


Note that the Mayan numeral   13   in   horizontal   format would be shown as:

                                  ╔════╗
                                  ║  ││║
                                  ║ ∙││║
                            13──► ║ ∙││║        ◄─── this glyph form won't be used in this Rosetta Code task.
                                  ║ ∙││║
                                  ╚════╝


Other forms of cartouches (boxes) can be used for this task.


Task requirements
  •   convert the following decimal numbers to Mayan numbers:
  •       4,005
  •       8,017
  •   326,205
  •   886,205
  •   show a   unique   interesting/pretty/unusual/intriguing/odd/amusing/weird   Mayan number
  •   show all output here


Related tasks


See also




AppleScript

Applescript supports only small integers (up to (2^29)-1 = 536870911). <lang applescript>use AppleScript version "2.4" use framework "Foundation" use scripting additions

-- MAYAN NUMBERS ------------------------------------------

-- mayanNumber:: Int -> String on mayanNumber(n)

   showIntAtBase(20, my mayanDigit, n, {})

end mayanNumber

-- mayanDigit :: Int -> String on mayanDigit(n)

   if 0 < n then
       set r to n mod 5
       bool({}, {concat(replicate(r, "●"))}, 0 < r) & ¬
           replicate(n div 5, "━━")
   else
       {"Θ"}
   end if

end mayanDigit

-- mayanFrame :: Int -> String on mayanFrame(n)

   "Mayan " & (n as string) & ":\n" & wikiTable({|class|:¬
       "wikitable", colwidth:¬
       "3em", cell:¬
       "vertical-align:bottom;", |style|:¬
       "text-align:center;background-color:#F0EDDE;" & ¬
       "color:#605B4B;border:2px solid silver"})'s ¬
       |λ|({map(intercalateS("
"), mayanNumber(n))}) & "\n"

end mayanFrame

-- TEST ---------------------------------------------------

on run

   set str to unlines(map(mayanFrame, ¬
       {4005, 8017, 326205, 886205, 2978480}))
   
   set the clipboard to (str)
   return str

end run

-- GENERIC ------------------------------------------------

-- Just :: a -> Maybe a on Just(x)

   {type:"Maybe", Nothing:false, Just:x}

end Just

-- Nothing :: Maybe a on Nothing()

   {type:"Maybe", Nothing:true}

end Nothing

-- Tuple (,) :: a -> b -> (a, b) on Tuple(a, b)

   {type:"Tuple", |1|:a, |2|:b, length:2}

end Tuple

-- bool :: a -> a -> Bool -> a on bool(f, t, p)

   if p then
       t
   else
       f
   end if

end bool

-- concat :: a -> [a] -- concat :: [String] -> String on concat(xs)

   set lng to length of xs
   if 0 < lng and string is class of (item 1 of xs) then
       set acc to ""
   else
       set acc to {}
   end if
   repeat with i from 1 to lng
       set acc to acc & item i of xs
   end repeat
   acc

end concat

-- foldl :: (a -> b -> a) -> a -> [b] -> a on foldl(f, startValue, xs)

   tell mReturn(f)
       set v to startValue
       set lng to length of xs
       repeat with i from 1 to lng
           set v to |λ|(v, item i of xs, i, xs)
       end repeat
       return v
   end tell

end foldl

-- intercalateS :: String -> [String] -> String on intercalateS(sep)

   script
       on |λ|(xs)
           set {dlm, my text item delimiters} to {my text item delimiters, sep}
           set s to xs as text
           set my text item delimiters to dlm
           return s
       end |λ|
   end script

end intercalateS

-- lookupDict :: a -> Dict -> Maybe b on lookupDict(k, dct)

   set ca to current application
   set v to (ca's NSDictionary's dictionaryWithDictionary:dct)'s objectForKey:k
   if missing value ≠ v then
       Just(item 1 of ((ca's NSArray's arrayWithObject:v) as list))
   else
       Nothing()
   end if

end lookupDict

-- Lift 2nd class handler function into 1st class script wrapper -- mReturn :: First-class m => (a -> b) -> m (a -> b) on mReturn(f)

   if class of f is script then
       f
   else
       script
           property |λ| : f
       end script
   end if

end mReturn

-- map :: (a -> b) -> [a] -> [b] on map(f, xs)

   tell mReturn(f)
       set lng to length of xs
       set lst to {}
       repeat with i from 1 to lng
           set end of lst to |λ|(item i of xs, i, xs)
       end repeat
       return lst
   end tell

end map


-- | The 'maybe' function takes a default value, a function, and a 'Maybe' -- value. If the 'Maybe' value is 'Nothing', the function returns the -- default value. Otherwise, it applies the function to the value inside -- the 'Just' and returns the result. -- maybe :: b -> (a -> b) -> Maybe a -> b on maybe(v, f, mb)

   if Nothing of mb then
       v
   else
       tell mReturn(f) to |λ|(Just of mb)
   end if

end maybe

-- quotRem :: Int -> Int -> (Int, Int) on quotRem(m, n)

   Tuple(m div n, m mod n)

end quotRem

-- Egyptian multiplication - progressively doubling a list, appending -- stages of doubling to an accumulator where needed for binary -- assembly of a target length -- replicate :: Int -> a -> [a] on replicate(n, a)

   set out to {}
   if n < 1 then return out
   set dbl to {a}
   
   repeat while (n > 1)
       if (n mod 2) > 0 then set out to out & dbl
       set n to (n div 2)
       set dbl to (dbl & dbl)
   end repeat
   return out & dbl

end replicate

-- showIntAtBase :: Int -> (Int -> [String]) -> Int -> String -> String on showIntAtBase(base, toDigit, n, rs)

   script showIt
       property f : mReturn(toDigit)
       on |λ|(nd_, r)
           set {n, d} to ({|1|, |2|} of nd_)
           set r_ to {f's |λ|(d)} & r
           if n > 0 then
               |λ|(quotRem(n, base), r_)
           else
               r_
           end if
       end |λ|
   end script
   showIt's |λ|(quotRem(n, base), rs)

end showIntAtBase

-- unlines :: [String] -> String on unlines(xs)

   set {dlm, my text item delimiters} to ¬
       {my text item delimiters, linefeed}
   set str to xs as text
   set my text item delimiters to dlm
   str

end unlines

-- unwords :: [String] -> String on unwords(xs)

   set {dlm, my text item delimiters} to ¬
       {my text item delimiters, space}
   set s to xs as text
   set my text item delimiters to dlm
   return s

end unwords

-- wikiTable :: Dict -> String -> String on wikiTable(opts)

   script ksv
       on |λ|(k)
           script
               on |λ|(s)
                   script
                       on |λ|(v)
                           k & v & s
                       end |λ|
                   end script
               end |λ|
           end script
       end |λ|
   end script
   script
       on |λ|(rows)
           script boxStyle
               on |λ|(a, k)
                   maybe(a, ksv's |λ|(a & k & "=\"")'s |λ|("\" "), ¬
                       lookupDict(k, opts))
               end |λ|
           end script
           script rowText
               on |λ|(row, iRow)
                   script cellText
                       on |λ|(cell)
                           if 1 = iRow then
                               set w to maybe("", ksv's |λ|("width:")'s |λ|(";"), ¬
                                   lookupDict("colwidth", opts))
                           else
                               set w to ""
                           end if
                           set s to maybe(w, ksv's |λ|(w)'s |λ|(""), ¬
                               lookupDict("cell", opts))
                           if 0 < length of s then
                               "style=\"" & s & "\"|" & cell
                           else
                               cell
                           end if
                       end |λ|
                   end script
                   intercalateS("\n|")'s |λ|(map(cellText, row))
               end |λ|
           end script
           "{| " & unwords(foldl(boxStyle, "", {"class", "style"})) & "\n|" & ¬
               intercalateS("\n|-\n")'s |λ|(map(rowText, rows)) & "\n|}"
       end |λ|
   end script

end wikiTable</lang>

Output:

Mayan 4005:

━━
━━
Θ ━━

Mayan 8017:

Θ Θ ●●
━━
━━
━━

Mayan 326205:

●● Θ ━━
━━
━━
━━
━━
━━

Mayan 886205:

━━ ━━
━━
━━
━━
━━
━━
━━
━━

Mayan 2978480:

●●●
━━
━━
━━
●●
━━
━━

━━
●●●● Θ

Go

<lang go>package main

import (

   "fmt"
   "strconv"

)

const (

   ul = "╔"
   uc = "╦"
   ur = "╗"
   ll = "╚"
   lc = "╩"
   lr = "╝"
   hb = "═"
   vb = "║"

)

var mayan = [5]string{

   "    ",
   " ∙  ",
   " ∙∙ ",
   "∙∙∙ ",
   "∙∙∙∙",

}

const (

   m0 = " Θ  "
   m5 = "────"

)

func dec2vig(n uint64) []uint64 {

   vig := strconv.FormatUint(n, 20)
   res := make([]uint64, len(vig))
   for i, d := range vig {
       res[i], _ = strconv.ParseUint(string(d), 20, 64)
   }
   return res

}

func vig2quin(n uint64) [4]string {

   if n >= 20 {
       panic("Cant't convert a number >= 20")
   }
   res := [4]string{mayan[0], mayan[0], mayan[0], mayan[0]}
   if n == 0 {
       res[3] = m0
       return res
   }
   fives := n / 5
   rem := n % 5
   res[3-fives] = mayan[rem]
   for i := 3; i > 3-int(fives); i-- {
       res[i] = m5
   }
   return res

}

func draw(mayans [][4]string) {

   lm := len(mayans)
   fmt.Print(ul)
   for i := 0; i < lm; i++ {
       for j := 0; j < 4; j++ {
           fmt.Print(hb)
       }
       if i < lm-1 {
           fmt.Print(uc)
       } else {
           fmt.Println(ur)
       }
   }
   for i := 1; i < 5; i++ {
       fmt.Print(vb)
       for j := 0; j < lm; j++ {
           fmt.Print(mayans[j][i-1])
           fmt.Print(vb)
       }
       fmt.Println()
   }
   fmt.Print(ll)
   for i := 0; i < lm; i++ {
       for j := 0; j < 4; j++ {
           fmt.Print(hb)
       }
       if i < lm-1 {
           fmt.Print(lc)
       } else {
           fmt.Println(lr)
       }
   }

}

func main() {

   numbers := []uint64{4005, 8017, 326205, 886205, 1081439556}
   for _, n := range numbers {
       fmt.Printf("Converting %d to Mayan:\n", n)
       vigs := dec2vig(n)
       lv := len(vigs)
       mayans := make([][4]string, lv)
       for i, vig := range vigs {
           mayans[i] = vig2quin(vig)
       }
       draw(mayans)
       fmt.Println()
   }

}</lang>

Output:
Converting 4005 to Mayan:
╔════╦════╦════╗
║    ║    ║    ║
║    ║    ║    ║
║────║    ║    ║
║────║ Θ  ║────║
╚════╩════╩════╝

Converting 8017 to Mayan:
╔════╦════╦════╦════╗
║    ║    ║    ║ ∙∙ ║
║    ║    ║    ║────║
║    ║    ║    ║────║
║ ∙  ║ Θ  ║ Θ  ║────║
╚════╩════╩════╩════╝

Converting 326205 to Mayan:
╔════╦════╦════╦════╦════╗
║    ║    ║    ║    ║    ║
║    ║    ║────║    ║    ║
║    ║    ║────║────║    ║
║ ∙∙ ║ Θ  ║────║────║────║
╚════╩════╩════╩════╩════╝

Converting 886205 to Mayan:
╔════╦════╦════╦════╦════╗
║    ║    ║    ║    ║    ║
║    ║    ║────║    ║    ║
║    ║────║────║────║    ║
║────║────║────║────║────║
╚════╩════╩════╩════╩════╝

Converting 1081439556 to Mayan:
╔════╦════╦════╦════╦════╦════╦════╗
║ ∙  ║ ∙∙ ║∙∙∙ ║∙∙∙∙║∙∙∙ ║ ∙∙ ║ ∙  ║
║────║────║────║────║────║────║────║
║────║────║────║────║────║────║────║
║────║────║────║────║────║────║────║
╚════╩════╩════╩════╩════╩════╩════╝

Haskell

<lang haskell>import Data.List (intercalate, transpose) import qualified Data.Map.Strict as M import Control.Applicative (liftA2) import Control.Monad (join) import Data.Maybe (maybe) import Data.Bool (bool)

-- MAIN --------------------------------------------------- main :: IO () main =

 (putStrLn . unlines) $
 mayanFramed <$> [4005, 8017, 326205, 886205, 1081439556, 1000000, 1000000000]

-- MAYAN NUMBERS ------------------------------------------ mayanGlyph :: Int -> String mayanGlyph =

 filter (any (not . null)) .
 transpose . leftPadded . flip (showIntAtBaseS 20 mayanDigit) []

mayanDigit :: Int -> [String] mayanDigit n

 | 0 /= n =
   replicate (rem n 5) mayaOne : join (replicate (quot n 5) [mayaFive])
 | otherwise = mayaZero

mayanFramed :: Int -> String mayanFramed =

 ("Mayan " ++) .
 liftA2
   (++)
   show
   ((":\n\n" ++) .
    wikiTable
      (M.fromList
         [ ( "style"
           , concat
               [ "text-align:center;"
               , "background-color:#F0EDDE;"
               , "color:#605B4B;"
               , "border:2px solid silver;"
               ])
         , ("colwidth", "3em;")
         ]) .
    mayanGlyph)

mayaZero, mayaOne :: Char mayaZero = '\920'

mayaOne = '\9679'

mayaFive :: String mayaFive = "\9473\9473"

-- NUMERIC BASES ------------------------------------------ -- Based on the Prelude showIntAtBase but uses an (Int -> [String]) -- (rather than Int -> Char) function as its second argument. -- -- Shows a /non-negative/ 'Integral' number using the base specified by the -- first argument, and the **String** representation specified by the second. showIntAtBaseS

 :: Integral a
 => a -> (Int -> [String]) -> a -> String -> String

showIntAtBaseS base toStr n0 r0 =

 let go (n, d) r =
       seq s $
       case n of
         0 -> r_
         _ -> go (quotRem n base) r_
       where
         s = toStr (fromIntegral d)
         r_ = s : r
 in go (quotRem n0 base) r0

-- DISPLAY ------------------------------------------------ wikiTable :: M.Map String String -> String -> String wikiTable opts rows

 | null rows = []
 | otherwise =
   "{| " ++
   foldr
     (\k a -> maybe a (((a ++ k ++ "=\"") ++) . (++ "\" ")) (M.lookup k opts))
     []
     ["class", "style"] ++
   '\n' :
   intercalate
     "|-\n"
     (zipWith
        (\row i ->
            unlines
              (fmap
                 ((bool
                     []
                     (maybe
                        "|"
                        (("|style=\"width:" ++) . (++ "\""))
                        (M.lookup "colwidth" opts))
                     (0 == i) ++) .
                  ('|' :))
                 row))
        rows
        [0 ..]) ++
   "|}\n\n"

leftPadded :: String -> String leftPadded xs =

 let w = maximum (length <$> xs)
 in ((++) =<< flip replicate [] . (-) w . length) <$> xs</lang>
Output:

Mayan 4005:

━━
━━ Θ ━━


Mayan 8017:

●●
━━
━━
Θ Θ ━━


Mayan 326205:

━━
━━ ━━
●● Θ ━━ ━━ ━━


Mayan 886205:

━━
━━ ━━ ━━
━━ ━━ ━━ ━━ ━━


Mayan 1081439556:

●● ●●● ●●●● ●●● ●●
━━ ━━ ━━ ━━ ━━ ━━ ━━
━━ ━━ ━━ ━━ ━━ ━━ ━━
━━ ━━ ━━ ━━ ━━ ━━ ━━


Mayan 1000000:

━━ ━━ Θ Θ Θ


Mayan 1000000000:

━━ ●●
━━ ━━ ━━
━━ ━━ ━━ Θ Θ Θ Θ

JavaScript

<lang JavaScript>(() => {

   'use strict';
   const main = () =>
       unlines(
           map(mayanFramed,
               [4005, 8017, 326205, 886205, 1081439556, 1000000, 1000000000]
           )
       );
   // MAYAN NUMBERS --------------------------------------
   // mayanFramed :: Int -> String
   const mayanFramed = n =>
       '\nMayan ' + n.toString() + ':\n\n' +
       wikiTable({
           style: 'text-align:center; background-color:#F0EDDE; ' +
           'color:#605B4B; border:2px solid silver',
           colwidth: '3em'
       })(
           mayanGlyph(n)
       );
   // mayanGlyph :: Int -> String
   const mayanGlyph = n =>
       filter(any(compose(not, isNull)),
           transpose(leftPadded(
               showIntAtBase(20, mayanDigit, n, [])
           ))
       );
   // mayanDigit :: Int -> [String]
   const mayanDigit = n =>
       0 !== n ? cons(
           replicateString(rem(n, 5), '●'),
           replicate(quot(n, 5), '━━')
       ) : ['Θ'];
   // FORMATTING -----------------------------------------
   // wikiTable :: Dict -> a -> String
   const wikiTable = opts => rows => {
       const colWidth = () =>
           'colwidth' in opts ? (
               '|style="width:' + opts.colwidth + ';"'
           ) : ;
       return 0 < rows.length ? (
           '{| ' + ['class', 'style'].reduce(
               (a, k) => k in opts ? (
                   a + k + '="' + opts[k] + '" '
               ) : a, 
           ) + '\n' + rows.map(
               (row, i) => row.map(
                   x => (0 === i ? (
                       colWidth() + '| '
                   ) : '|') + (x.toString() || ' ')
               ).join('\n')
           ).join('\n|-\n') + '\n|}\n\n'
       ) : ;
   };
   // leftPadded :: String -> String
   const leftPadded = xs => {
       const w = maximum(map(length, xs));
       return map(
           x => replicate(w - x.length, ).concat(x),
           xs
       );
   };
   // GENERIC FUNCTIONS ----------------------------------
   // Tuple (,) :: a -> b -> (a, b)
   const Tuple = (a, b) => ({
       type: 'Tuple',
       '0': a,
       '1': b,
       length: 2
   });
   // any :: (a -> Bool) -> [a] -> Bool
   const any = p => xs => xs.some(p);
   // comparing :: (a -> b) -> (a -> a -> Ordering)
   const comparing = f =>
       (x, y) => {
           const
               a = f(x),
               b = f(y);
           return a < b ? -1 : (a > b ? 1 : 0);
       };
   // compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
   const compose = (f, g) => x => f(g(x));
   // concatMap :: (a -> [b]) -> [a] -> [b]
   const concatMap = (f, xs) =>
       xs.reduce((a, x) => a.concat(f(x)), []);
   // cons :: a -> [a] -> [a]
   const cons = (x, xs) =>
       Array.isArray(xs) ? (
           [x].concat(xs)
       ) : 'GeneratorFunction' !== xs.constructor.constructor.name ? (
           x + xs
       ) : ( // Existing generator wrapped with one additional element
           function*() {
               yield x;
               let nxt = xs.next()
               while (!nxt.done) {
                   yield nxt.value;
                   nxt = xs.next();
               }
           }
       )();
   // filter :: (a -> Bool) -> [a] -> [a]
   const filter = (f, xs) => xs.filter(f);
   // foldl1 :: (a -> a -> a) -> [a] -> a
   const foldl1 = (f, xs) =>
       1 < xs.length ? xs.slice(1)
       .reduce(f, xs[0]) : xs[0];
   // isNull :: [a] -> Bool
   // isNull :: String -> Bool
   const isNull = xs =>
       Array.isArray(xs) || ('string' === typeof xs) ? (
           1 > xs.length
       ) : undefined;
   // Returns Infinity over objects without finite length.
   // This enables zip and zipWith to choose the shorter
   // argument when one is non-finite, like cycle, repeat etc
   // length :: [a] -> Int
   const length = xs =>
       (Array.isArray(xs) || 'string' === typeof xs) ? (
           xs.length
       ) : Infinity;
   // map :: (a -> b) -> [a] -> [b]
   const map = (f, xs) =>
       (Array.isArray(xs) ? (
           xs
       ) : xs.split()).map(f);
   // maximum :: Ord a => [a] -> a
   const maximum = xs =>
       0 < xs.length ? (
           foldl1((a, x) => x > a ? x : a, xs)
       ) : undefined;
   //  Ordering: (LT|EQ|GT):
   //  GT: 1 (or other positive n)
   //    EQ: 0
   //  LT: -1 (or other negative n)
   // maximumBy :: (a -> a -> Ordering) -> [a] -> a
   const maximumBy = (f, xs) =>
       0 < xs.length ? (
           xs.slice(1)
           .reduce((a, x) => 0 < f(x, a) ? x : a, xs[0])
       ) : undefined;
   // not :: Bool -> Bool
   const not = b => !b;
   // quot :: Int -> Int -> Int
   const quot = (n, m) => Math.floor(n / m);
   // quotRem :: Int -> Int -> (Int, Int)
   const quotRem = (m, n) =>
       Tuple(Math.floor(m / n), m % n);
   // rem :: Int -> Int -> Int
   const rem = (n, m) => n % m;
   // replicate :: Int -> a -> [a]
   const replicate = (n, x) =>
       Array.from({
           length: n
       }, () => x);
   // replicateString :: Int -> String -> String
   const replicateString = (n, s) => s.repeat(n);
   // showIntAtBase :: Int -> (Int -> [String])
   //               -> Int -> String -> String
   const showIntAtBase = (base, toStr, n, rs) => {
       const go = ([n, d], r) => {
           const r_ = cons(toStr(d), r);
           return 0 !== n ? (
               go(Array.from(quotRem(n, base)), r_)
           ) : r_;
       };
       return go(Array.from(quotRem(n, base)), rs);
   };
   // transpose :: a -> a
   const transpose = tbl => {
       const
           gaps = replicate(
               length(maximumBy(comparing(length), tbl)), []
           ),
           rows = map(xs => xs.concat(gaps.slice(xs.length)), tbl);
       return map(
           (_, col) => concatMap(row => [row[col]], rows),
           rows[0]
       );
   };
   // unlines :: [String] -> String
   const unlines = xs => xs.join('\n');
   // MAIN ---
   return main();

})();</lang>

Output:

Mayan 4005:

━━
━━ Θ ━━


Mayan 8017:

●●
━━
━━
Θ Θ ━━


Mayan 326205:

━━
━━ ━━
●● Θ ━━ ━━ ━━


Mayan 886205:

━━
━━ ━━ ━━
━━ ━━ ━━ ━━ ━━


Mayan 1081439556:

●● ●●● ●●●● ●●● ●●
━━ ━━ ━━ ━━ ━━ ━━ ━━
━━ ━━ ━━ ━━ ━━ ━━ ━━
━━ ━━ ━━ ━━ ━━ ━━ ━━


Mayan 1000000:

━━ ━━ Θ Θ Θ


Mayan 1000000000:

━━ ●●
━━ ━━ ━━
━━ ━━ ━━ Θ Θ Θ Θ

Perl

Translation of: Perl 6
Library: ntheory

<lang perl>use ntheory qw/fromdigits todigitstring/;

my $t_style = '"border-collapse: separate; text-align: center; border-spacing: 3px 0px;"'; my $c_style = '"border: solid black 2px;background-color: #fffff0;border-bottom: double 6px;'.

 'border-radius: 1em;-moz-border-radius: 1em;-webkit-border-radius: 1em;'.
 'vertical-align: bottom;width: 3.25em;"';

sub cartouches {

   my($num, @digits) = @_;
   my $render;
   for my $d (@digits) {
       $render .= "| style=$c_style | $_\n" for glyphs(@$d);
   }
   chomp $render;
   join "\n", "\{| style=$t_style", "|+ $num", '|-', $render, '|}'

}

sub glyphs {

   return 'Θ' unless $_[0] || $_[1];
   join '
', '●' x $_[0], ('───') x $_[1];

}

sub mmod {

   my($n,$b) = @_;
   my @nb;
   return 0 unless $n;
   push @nb, fromdigits($_, $b) for split , todigitstring($n, $b);
   return @nb;

}

for $n (qw<4005 8017 326205 886205 26960840421>) {

   push @output, cartouches($n, map { [reverse mmod($_,5)] } mmod($n,20) );

}

print join "\n
\n", @output;</lang>

Output:
4005

───
───
Θ
───


8017
Θ Θ ●●
───
───
───


326205
●● Θ
───
───
───

───
───

───


886205

───

───
───

───
───
───

───
───

───


26960840421

───

───

───

Perl 6

Just a matter of converting to base twenty, then divmod 5 each digit and map to the appropriate glyph. Most of this is display code to deal with the cartouche requirement.

I actually spent a little time looking into what exactly a Mayan cartouche was supposed to look like. The classic Mayan cartouche was kind of a rounded rectangle with three little semi-circles underneath. Kind of looks like a picture frame. In general, they were only used when expressing significant "holy" dates in the Mayan calender.

These don't particularly resemble the Mayan cartouche, more like mahjong tiles actually. It would have been so much easier and more compact though if I could have put all the styling into a CSS file instead of inlining it, but oh well, it was entertaining to do.

<lang perl6>my $t-style = '"border-collapse: separate; text-align: center; border-spacing: 3px 0px;"'; my $c-style = '"border: solid black 2px;background-color: #fffff0;border-bottom: double 6px;'~

 'border-radius: 1em;-moz-border-radius: 1em;-webkit-border-radius: 1em;'~
 'vertical-align: bottom;width: 3.25em;"';

sub mayan ($int) { $int.polymod(20 xx *).reverse.map: *.polymod(5) }

sub display ($num, @digits) {

   join "\n", "\{| style=$t-style", "|+ $num",
   '|-', (|@digits.map: {"| style=$c-style | $_"}), '|}'

}

my @output = <4005 8017 326205 886205 1081439556 503491211079>.map:

 { display $_, .&mayan.map: { (join '
', '●' x .[0], '───' xx .[1]) || 'Θ' } }

say @output.join: "\n
\n";</lang>

Output:
4005

───
───
Θ
───


8017
Θ Θ ●●
───
───
───


326205
●● Θ
───
───
───

───
───

───


886205

───

───
───

───
───
───

───
───

───


1081439556

───
───
───
●●
───
───
───
●●●
───
───
───
●●●●
───
───
───
●●●
───
───
───
●●
───
───
───

───
───
───


503491211079
●●●●
───
───
───
●●●
───
───
●●
───
Θ ●●
───
●●●
───
───
●●●●
───
───
───

Python

<lang Python>from functools import (reduce)


  1. main :: IO ()

def main():

   print (
       '\n'.join(mayanFramed(n) for n in [
           4005, 8017, 326205, 886205, 1081439556, 1000000, 1000000000
       ])
   )


  1. MAYAN NUMBERS -------------------------------------------


  1. mayanNumerals :: Int -> String

def mayanNumerals(n):

   return showIntAtBase(20)(
       mayanDigit
   )(n)([])


  1. mayanDigit :: Int -> [String]

def mayanDigit(n):

   if 0 < n:
       r = n % 5
       return [(['●' * r] if 0 < r else []) + (['━━'] * (n // 5))]
   else:
       return ['Θ']


  1. mayanFramed :: Int -> String

def mayanFramed(n):

   return 'Mayan ' + str(n) + ':\n\n' + (
       wikiTable({
           'class': 'wikitable',
           'style': cssFromDict({
               'text-align': 'center',
               'background-color': '#F0EDDE',
               'color': '#605B4B',
               'border': '2px solid silver'
           }),
           'colwidth': '3em',
           'cell': 'vertical-align: bottom;'
       })([[
           '
'.join(col) for col in mayanNumerals(n) ]]) )


  1. BOXES ---------------------------------------------------
  1. wikiTable :: Dict -> a -> String

def wikiTable(opts):

   def colWidth():
       return 'width:' + opts['colwidth'] + '; ' if (
           'colwidth' in opts
       ) else 
   def cellStyle():
       return opts['cell'] if 'cell' in opts else 
   return lambda rows: '{| ' + reduce(
       lambda a, k: (
           a + k + '="' + opts[k] + '" ' if k in opts else a
       ),
       ['class', 'style'],
       
   ) + '\n' + '\n|-\n'.join(
       '\n'.join(
           ('|' if (0 != i and ('cell' not in opts)) else (
               '|style="' + colWidth() + cellStyle() + '"|'
           )) + (
               str(x) or ' '
           ) for x in row
       ) for i, row in enumerate(rows)
   ) + '\n|}\n\n'


  1. GENERIC -------------------------------------------------
  1. cssFromDict :: Dict -> String

def cssFromDict(dct):

   return reduce(
       lambda a, k: a + k + ':' + dct[k] + '; ', dct.keys(), 
   )


  1. showIntAtBase :: Int -> (Int -> String) -> Int -> String -> String

def showIntAtBase(base):

   def f(toStr, n, rs):
       def go(nd, r):
           n, d = nd
           r_ = toStr(d) + r
           return go(divmod(n, base), r_) if 0 != n else r_
       return go(divmod(n, base), rs)
   return lambda toStr: lambda n: lambda rs: (
       f(toStr, n, rs)
   )


if __name__ == '__main__':

   main()</lang>
Output:

Mayan 4005:

━━
━━
Θ ━━


Mayan 8017:

Θ Θ ●●
━━
━━
━━


Mayan 326205:

●● Θ ━━
━━
━━
━━
━━
━━


Mayan 886205:

━━ ━━
━━
━━
━━
━━
━━
━━
━━


Mayan 1081439556:


━━
━━
━━
●●
━━
━━
━━
●●●
━━
━━
━━
●●●●
━━
━━
━━
●●●
━━
━━
━━
●●
━━
━━
━━

━━
━━
━━


Mayan 1000000:


━━
━━ Θ Θ Θ


Mayan 1000000000:

━━
━━
━━
●●
━━
━━
━━
━━
Θ Θ Θ Θ

REXX

<lang rexx>/*REXX program converts decimal numbers to the Mayan numbering system (with cartouches).*/ parse arg $ /*obtain optional arguments from the CL*/ if $= then $= 4005 8017 326205 886205, /*Not specified? Then use the default.*/

                172037122592320200101           /*Morse code for MAYAN; egg is a blank.*/
 do j=1  for words($)                           /*convert each of the numbers specified*/
 #= word($, j)                                  /*extract a number from (possible) list*/
 say
 say  center('converting the decimal number '     #     " to a Mayan number:", 90,  '─')
 say
 call $MAYAN   #   '(overlap)'                  /*invoke the  $MAYAN (REXX) subroutine.*/
 say
 end   /*j*/                                    /*stick a fork in it,  we're all done. */</lang>

The   $MAYAN.REX   (REXX program) subroutine can be seen here   ───►   Mayan_numerals\$MAYAN.REX.

output   when using the default inputs:
─────────────────converting the decimal number  4005  to a Mayan number:──────────────────

╔════╦════╦════╗
║    ║    ║    ║
║    ║    ║    ║
║────║    ║    ║
║────║ Θ  ║────║
╚════╩════╩════╝


─────────────────converting the decimal number  8017  to a Mayan number:──────────────────

╔════╦════╦════╦════╗
║    ║    ║    ║ ∙∙ ║
║    ║    ║    ║────║
║    ║    ║    ║────║
║ ∙  ║ Θ  ║ Θ  ║────║
╚════╩════╩════╩════╝


────────────────converting the decimal number  326205  to a Mayan number:─────────────────

╔════╦════╦════╦════╦════╗
║    ║    ║    ║    ║    ║
║    ║    ║────║    ║    ║
║    ║    ║────║────║    ║
║ ∙∙ ║ Θ  ║────║────║────║
╚════╩════╩════╩════╩════╝


────────────────converting the decimal number  886205  to a Mayan number:─────────────────

╔════╦════╦════╦════╦════╗
║    ║    ║    ║    ║    ║
║    ║    ║────║    ║    ║
║    ║────║────║────║    ║
║────║────║────║────║────║
╚════╩════╩════╩════╩════╝


─────────converting the decimal number  172037122592320200101  to a Mayan number:─────────

╔════╦════╦════╦════╦════╦════╦════╦════╦════╦════╦════╦════╦════╦════╦════╦════╗
║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║
║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║
║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║    ║
║────║────║ Θ  ║ ∙  ║────║ Θ  ║────║ ∙  ║────║────║ Θ  ║ ∙  ║────║ Θ  ║────║ ∙  ║
╚════╩════╩════╩════╩════╩════╩════╩════╩════╩════╩════╩════╩════╩════╩════╩════╝

zkl

<lang zkl>var zero=" \u0398 ",one="\u2219", five=String("\u2500"*4,"\n"), fill=" \n"; var ds=T(" "," \u2219 "," \u2219\u2219 ","\u2219\u2219\u2219 ","\u2219\u2219\u2219\u2219"); fcn mayan(m){ //--> lists of lists of strings (each line of tile)

  m.toString(20).pump(List,fcn(m){		// 4,005 --> "a05"
     bars,dots := m.toInt(20).divr(5);		// "a" --> 10 --> (2,0)
     if(0==bars==dots) return(String(fill*3, zero).split("\n"));  // 0
     String(fill*(3-bars), ds[dots],"\n", five*bars)[0,-1].split("\n") // tile
  })

}</lang> <lang zkl>var vbar="\u2551", hbar="\u2550"*4, ut=(hbar+"\u2566"), bt=(hbar+"\u2569"); fcn displayMayan(m){ // eg 11 is ( ( ),( * ),(----),(----) )

  println("\u2554", ut*(m.len()-1), hbar,"\u2557");  // top
  foreach n in (4){ println(vbar, m.apply("get",n).concat(vbar), vbar) }
  println("\u255a", bt*(m.len()-1), hbar,"\u255d");  // bottom

}</lang> <lang zkl>foreach m in (T(4_005, 8_017, 326_205, 886_205, 503_491_211_079, 88_637_341)){

  println("\n%,d:".fmt(m)); mayan(m):displayMayan(_);

}</lang>

Output:
4,005:
╔════╦════╦════╗
║    ║    ║    ║
║    ║    ║    ║
║────║    ║    ║
║────║ Θ  ║────║
╚════╩════╩════╝

8,017:
╔════╦════╦════╦════╗
║    ║    ║    ║ ∙∙ ║
║    ║    ║    ║────║
║    ║    ║    ║────║
║ ∙  ║ Θ  ║ Θ  ║────║
╚════╩════╩════╩════╝

326,205:
╔════╦════╦════╦════╦════╗
║    ║    ║    ║    ║    ║
║    ║    ║────║    ║    ║
║    ║    ║────║────║    ║
║ ∙∙ ║ Θ  ║────║────║────║
╚════╩════╩════╩════╩════╝

886,205:
╔════╦════╦════╦════╦════╗
║    ║    ║    ║    ║    ║
║    ║    ║────║    ║    ║
║    ║────║────║────║    ║
║────║────║────║────║────║
╚════╩════╩════╩════╩════╝

503,491,211,079:
╔════╦════╦════╦════╦════╦════╦════╦════╦════╗
║∙∙∙∙║    ║    ║    ║    ║    ║    ║    ║∙∙∙∙║
║────║∙∙∙ ║    ║    ║    ║    ║    ║∙∙∙ ║────║
║────║────║ ∙∙ ║    ║    ║    ║ ∙∙ ║────║────║
║────║────║────║ ∙  ║ Θ  ║ ∙  ║────║────║────║
╚════╩════╩════╩════╩════╩════╩════╩════╩════╝

88,637,341:
╔════╦════╦════╦════╦════╦════╦════╗
║    ║    ║    ║∙∙∙∙║    ║    ║    ║
║    ║    ║∙∙∙ ║────║∙∙∙ ║    ║    ║
║    ║ ∙∙ ║────║────║────║ ∙∙ ║    ║
║ ∙  ║────║────║────║────║────║ ∙  ║
╚════╩════╩════╩════╩════╩════╩════╝