Mangarevan Counting

June 20, 2017

Here are our conversion functions:

(define (integer->mangareva n)
  (when (not (<= 1 n 799))
    (error 'integer->mangareva "out of range"))
  (let loop ((n n) (m (list)))
    (cond ((<= 80 n) (loop (remainder n 80)
            (cons "V" (cons (number->string (quotient n 80)) m))))
          ((<= 40 n) (loop (- n 40) (cons "T" m)))
          ((<= 20 n) (loop (- n 20) (cons "P" m)))
          ((<= 10 n) (loop (- n 10) (cons "K" m)))
          ((<= 1  n) (loop 0 (cons (number->string n) m)))
          (else (apply string-append (reverse m))))))
(define (mangareva->integer str)
  (define (digit? c) (char<=? #\1 c #\9))
   (let loop ((cs (string->list str)) (n 0))
     (list-match cs
       (() n)
       ((m) (digit? m) (loop '() (+ n (char->integer m) -48)))
       ((#\K . rest) (loop rest (+ n 10)))
       ((#\P . rest) (loop rest (+ n 20)))
       ((#\T . rest) (loop rest (+ n 40)))
       ((m #\V . rest) (digit? m)
         (loop rest (+ n (* (- (char->integer m) 48) 80))))
      (else (error 'mangareva->integer "not a number")))))

We used pattern matching for the conversion from Mangarevan to integer, because it’s much easier to read than parsing each element of the number. Note that our error checking is not sufficient to find all mis-formed Mangarevan numbers. It’s easy to do an exhaustive test:

> (do ((n 1 (+ n 1))) ((= n 800))
  (assert (mangareva->integer (integer->mangareva n)) n))

That writes nothing, on the theory that no news is good news. Here’s a complete list of Mangarevan counting numbers:

> (map integer->mangareva (range 1 800))
("1" "2" "3" "4" "5" "6" "7" "8" "9" "K" "K1" "K2" "K3" "K4"
"K5" "K6" "K7" "K8" "K9" "P" "P1" "P2" "P3" "P4" "P5" "P6"
"P7" "P8" "P9" "PK" "PK1" "PK2" "PK3" "PK4" "PK5" "PK6"
"PK7" "PK8" "PK9" "T" "T1" "T2" "T3" "T4" "T5" "T6" "T7"
"T8" "T9" "TK" "TK1" "TK2" "TK3" "TK4" "TK5" "TK6" "TK7"
"TK8" "TK9" "TP" "TP1" "TP2" "TP3" "TP4" "TP5" "TP6" "TP7"
"TP8" "TP9" "TPK" "TPK1" "TPK2" "TPK3" "TPK4" "TPK5" "TPK6"
"TPK7" "TPK8" "TPK9" "1V" "1V1" "1V2" "1V3" "1V4" "1V5"
"1V6" "1V7" "1V8" "1V9" "1VK" "1VK1" "1VK2" "1VK3" "1VK4"
"1VK5" "1VK6" "1VK7" "1VK8" "1VK9" "1VP" "1VP1" "1VP2"
"1VP3" "1VP4" "1VP5" "1VP6" "1VP7" "1VP8" "1VP9" "1VPK"
"1VPK1" "1VPK2" "1VPK3" "1VPK4" "1VPK5" "1VPK6" "1VPK7"
"1VPK8" "1VPK9" "1VT" "1VT1" "1VT2" "1VT3" "1VT4" "1VT5"
"1VT6" "1VT7" "1VT8" "1VT9" "1VTK" "1VTK1" "1VTK2" "1VTK3"
"1VTK4" "1VTK5" "1VTK6" "1VTK7" "1VTK8" "1VTK9" "1VTP"
"1VTP1" "1VTP2" "1VTP3" "1VTP4" "1VTP5" "1VTP6" "1VTP7"
"1VTP8" "1VTP9" "1VTPK" "1VTPK1" "1VTPK2" "1VTPK3" "1VTPK4"
"1VTPK5" "1VTPK6" "1VTPK7" "1VTPK8" "1VTPK9" "2V" "2V1"
"2V2" "2V3" "2V4" "2V5" "2V6" "2V7" "2V8" "2V9" "2VK" "2VK1"
"2VK2" "2VK3" "2VK4" "2VK5" "2VK6" "2VK7" "2VK8" "2VK9"
"2VP" "2VP1" "2VP2" "2VP3" "2VP4" "2VP5" "2VP6" "2VP7"
"2VP8" "2VP9" "2VPK" "2VPK1" "2VPK2" "2VPK3" "2VPK4" "2VPK5"
"2VPK6" "2VPK7" "2VPK8" "2VPK9" "2VT" "2VT1" "2VT2" "2VT3"
"2VT4" "2VT5" "2VT6" "2VT7" "2VT8" "2VT9" "2VTK" "2VTK1"
"2VTK2" "2VTK3" "2VTK4" "2VTK5" "2VTK6" "2VTK7" "2VTK8"
"2VTK9" "2VTP" "2VTP1" "2VTP2" "2VTP3" "2VTP4" "2VTP5"
"2VTP6" "2VTP7" "2VTP8" "2VTP9" "2VTPK" "2VTPK1" "2VTPK2"
"2VTPK3" "2VTPK4" "2VTPK5" "2VTPK6" "2VTPK7" "2VTPK8"
"2VTPK9" "3V" "3V1" "3V2" "3V3" "3V4" "3V5" "3V6" "3V7"
"3V8" "3V9" "3VK" "3VK1" "3VK2" "3VK3" "3VK4" "3VK5" "3VK6"
"3VK7" "3VK8" "3VK9" "3VP" "3VP1" "3VP2" "3VP3" "3VP4"
"3VP5" "3VP6" "3VP7" "3VP8" "3VP9" "3VPK" "3VPK1" "3VPK2"
"3VPK3" "3VPK4" "3VPK5" "3VPK6" "3VPK7" "3VPK8" "3VPK9"
"3VT" "3VT1" "3VT2" "3VT3" "3VT4" "3VT5" "3VT6" "3VT7"
"3VT8" "3VT9" "3VTK" "3VTK1" "3VTK2" "3VTK3" "3VTK4" "3VTK5"
"3VTK6" "3VTK7" "3VTK8" "3VTK9" "3VTP" "3VTP1" "3VTP2"
"3VTP3" "3VTP4" "3VTP5" "3VTP6" "3VTP7" "3VTP8" "3VTP9"
"3VTPK" "3VTPK1" "3VTPK2" "3VTPK3" "3VTPK4" "3VTPK5"
"3VTPK6" "3VTPK7" "3VTPK8" "3VTPK9" "4V" "4V1" "4V2" "4V3"
"4V4" "4V5" "4V6" "4V7" "4V8" "4V9" "4VK" "4VK1" "4VK2"
"4VK3" "4VK4" "4VK5" "4VK6" "4VK7" "4VK8" "4VK9" "4VP"
"4VP1" "4VP2" "4VP3" "4VP4" "4VP5" "4VP6" "4VP7" "4VP8"
"4VP9" "4VPK" "4VPK1" "4VPK2" "4VPK3" "4VPK4" "4VPK5"
"4VPK6" "4VPK7" "4VPK8" "4VPK9" "4VT" "4VT1" "4VT2" "4VT3"
"4VT4" "4VT5" "4VT6" "4VT7" "4VT8" "4VT9" "4VTK" "4VTK1"
"4VTK2" "4VTK3" "4VTK4" "4VTK5" "4VTK6" "4VTK7" "4VTK8"
"4VTK9" "4VTP" "4VTP1" "4VTP2" "4VTP3" "4VTP4" "4VTP5"
"4VTP6" "4VTP7" "4VTP8" "4VTP9" "4VTPK" "4VTPK1" "4VTPK2"
"4VTPK3" "4VTPK4" "4VTPK5" "4VTPK6" "4VTPK7" "4VTPK8"
"4VTPK9" "5V" "5V1" "5V2" "5V3" "5V4" "5V5" "5V6" "5V7"
"5V8" "5V9" "5VK" "5VK1" "5VK2" "5VK3" "5VK4" "5VK5" "5VK6"
"5VK7" "5VK8" "5VK9" "5VP" "5VP1" "5VP2" "5VP3" "5VP4"
"5VP5" "5VP6" "5VP7" "5VP8" "5VP9" "5VPK" "5VPK1" "5VPK2"
"5VPK3" "5VPK4" "5VPK5" "5VPK6" "5VPK7" "5VPK8" "5VPK9"
"5VT" "5VT1" "5VT2" "5VT3" "5VT4" "5VT5" "5VT6" "5VT7"
"5VT8" "5VT9" "5VTK" "5VTK1" "5VTK2" "5VTK3" "5VTK4" "5VTK5"
"5VTK6" "5VTK7" "5VTK8" "5VTK9" "5VTP" "5VTP1" "5VTP2"
"5VTP3" "5VTP4" "5VTP5" "5VTP6" "5VTP7" "5VTP8" "5VTP9"
"5VTPK" "5VTPK1" "5VTPK2" "5VTPK3" "5VTPK4" "5VTPK5"
"5VTPK6" "5VTPK7" "5VTPK8" "5VTPK9" "6V" "6V1" "6V2" "6V3"
"6V4" "6V5" "6V6" "6V7" "6V8" "6V9" "6VK" "6VK1" "6VK2"
"6VK3" "6VK4" "6VK5" "6VK6" "6VK7" "6VK8" "6VK9" "6VP"
"6VP1" "6VP2" "6VP3" "6VP4" "6VP5" "6VP6" "6VP7" "6VP8"
"6VP9" "6VPK" "6VPK1" "6VPK2" "6VPK3" "6VPK4" "6VPK5"
"6VPK6" "6VPK7" "6VPK8" "6VPK9" "6VT" "6VT1" "6VT2" "6VT3"
"6VT4" "6VT5" "6VT6" "6VT7" "6VT8" "6VT9" "6VTK" "6VTK1"
"6VTK2" "6VTK3" "6VTK4" "6VTK5" "6VTK6" "6VTK7" "6VTK8"
"6VTK9" "6VTP" "6VTP1" "6VTP2" "6VTP3" "6VTP4" "6VTP5"
"6VTP6" "6VTP7" "6VTP8" "6VTP9" "6VTPK" "6VTPK1" "6VTPK2"
"6VTPK3" "6VTPK4" "6VTPK5" "6VTPK6" "6VTPK7" "6VTPK8"
"6VTPK9" "7V" "7V1" "7V2" "7V3" "7V4" "7V5" "7V6" "7V7"
"7V8" "7V9" "7VK" "7VK1" "7VK2" "7VK3" "7VK4" "7VK5" "7VK6"
"7VK7" "7VK8" "7VK9" "7VP" "7VP1" "7VP2" "7VP3" "7VP4"
"7VP5" "7VP6" "7VP7" "7VP8" "7VP9" "7VPK" "7VPK1" "7VPK2"
"7VPK3" "7VPK4" "7VPK5" "7VPK6" "7VPK7" "7VPK8" "7VPK9"
"7VT" "7VT1" "7VT2" "7VT3" "7VT4" "7VT5" "7VT6" "7VT7"
"7VT8" "7VT9" "7VTK" "7VTK1" "7VTK2" "7VTK3" "7VTK4" "7VTK5"
"7VTK6" "7VTK7" "7VTK8" "7VTK9" "7VTP" "7VTP1" "7VTP2"
"7VTP3" "7VTP4" "7VTP5" "7VTP6" "7VTP7" "7VTP8" "7VTP9"
"7VTPK" "7VTPK1" "7VTPK2" "7VTPK3" "7VTPK4" "7VTPK5"
"7VTPK6" "7VTPK7" "7VTPK8" "7VTPK9" "8V" "8V1" "8V2" "8V3"
"8V4" "8V5" "8V6" "8V7" "8V8" "8V9" "8VK" "8VK1" "8VK2"
"8VK3" "8VK4" "8VK5" "8VK6" "8VK7" "8VK8" "8VK9" "8VP"
"8VP1" "8VP2" "8VP3" "8VP4" "8VP5" "8VP6" "8VP7" "8VP8"
"8VP9" "8VPK" "8VPK1" "8VPK2" "8VPK3" "8VPK4" "8VPK5"
"8VPK6" "8VPK7" "8VPK8" "8VPK9" "8VT" "8VT1" "8VT2" "8VT3"
"8VT4" "8VT5" "8VT6" "8VT7" "8VT8" "8VT9" "8VTK" "8VTK1"
"8VTK2" "8VTK3" "8VTK4" "8VTK5" "8VTK6" "8VTK7" "8VTK8"
"8VTK9" "8VTP" "8VTP1" "8VTP2" "8VTP3" "8VTP4" "8VTP5"
"8VTP6" "8VTP7" "8VTP8" "8VTP9" "8VTPK" "8VTPK1" "8VTPK2"
"8VTPK3" "8VTPK4" "8VTPK5" "8VTPK6" "8VTPK7" "8VTPK8"
"8VTPK9" "9V" "9V1" "9V2" "9V3" "9V4" "9V5" "9V6" "9V7"
"9V8" "9V9" "9VK" "9VK1" "9VK2" "9VK3" "9VK4" "9VK5" "9VK6"
"9VK7" "9VK8" "9VK9" "9VP" "9VP1" "9VP2" "9VP3" "9VP4"
"9VP5" "9VP6" "9VP7" "9VP8" "9VP9" "9VPK" "9VPK1" "9VPK2"
"9VPK3" "9VPK4" "9VPK5" "9VPK6" "9VPK7" "9VPK8" "9VPK9"
"9VT" "9VT1" "9VT2" "9VT3" "9VT4" "9VT5" "9VT6" "9VT7"
"9VT8" "9VT9" "9VTK" "9VTK1" "9VTK2" "9VTK3" "9VTK4" "9VTK5"
"9VTK6" "9VTK7" "9VTK8" "9VTK9" "9VTP" "9VTP1" "9VTP2"
"9VTP3" "9VTP4" "9VTP5" "9VTP6" "9VTP7" "9VTP8" "9VTP9"
"9VTPK" "9VTPK1" "9VTPK2" "9VTPK3" "9VTPK4" "9VTPK5"
"9VTPK6" "9VTPK7" "9VTPK8" "9VTPK9")

You can run the program at


Pages: 1 2

4 Responses to “Mangarevan Counting”

  1. Rutger said

    In Python, abusing the eval() function.

    import string
    K = 10
    P = 20
    T = 40
    V = 80
    def to_Mangarevan(x):
    	result = ""
    	n, r = divmod(x, V)
    	if n:
    		result += str(n)+"V"
    	if r > T:
    		result += "T"
    		r -= T
    	if r > P:
    		result += "P"
    		r -= P
    	if r > K:
    		result += "K"
    		r -= K
    	if r:
    		result += str(r)
    	return result
    def from_Mangeravan(m):
    	m = m[::-1]
    	result = 0
    	for i,x in enumerate(m):
    		if i == 0 and x in string.digits:
    			result += eval(m[0])
    		if x in "KPT":
    			result += eval(x)
    		if i == len(m)-1 and x in string.digits:
    			result += eval(x)*V
    	return result
    for x in [73, 219, 799, 164]:
    	print(x, to_Mangarevan(x))
    	assert x == from_Mangeravan(to_Mangarevan(x))
    # output
    # 73 TPK3
    # 219 2VTK9
    # 799 9VTPK9
    # 164 2V4
  2. Jussi Piitulainen said

    (@Praxis, your parsing function does not look right. I see it missing newlines and cut short.)

    # In Julia 0.6.0 that was released just
    # Only notation mava"3VTPK1" and binary addition
    # Allow 0 digit and even mava"" as 0
    # No overflow check
    # Regex matching feels fragile, seems ok
    # Suboptimal error message on no match
    # Unsure of proper macro usage, returns actual value, seems ok
    # Unsure of printing mechanism, show() using print() seems ok
    module PracticalMangarevaCounting
    export Mava, @mava_str
    import Base: +, show
    primitive type Mava 16 end
    macro mava_str(digits)
        mo = match(r"^((\d)V)?(T)?(P)?(K)?(\d)?$", digits)
        _, v, t, p, k, r = mo.captures
        reinterpret(Mava, UInt16(+((v == nothing) ? 0 : parse(v) * 80,
                                   (t == nothing) ? 0 : 40,
                                   (p == nothing) ? 0 : 20,
                                   (k == nothing) ? 0 : 10,
                                   (r == nothing) ? 0 : parse(r))))
    function show(io::IO, mava::Mava)
        w = reinterpret(UInt16, mava)
        v, r = divrem(w, 80)
        t, r = divrem(r, 40)
        p, r = divrem(r, 20)
        k, r = divrem(r, 10)
        print(io, "mava", '"',
                  v > 0 ? v : "",
                  v > 0 ? "V" : "",
                  t > 0 ? "T" : "",
                  p > 0 ? "P" : "",
                  k > 0 ? "K" : "",
                  r > 0 ? r : "",
    function +(ma::Mava, va::Mava)
        reinterpret(Mava, reinterpret(UInt16, ma) +
                          reinterpret(UInt16, va))
    julia> include("mangareva.jl")
    julia> using PracticalMangarevaCounting
    julia> reinterpret(Mava, UInt16(73))
    julia> reinterpret(Mava, UInt16(219))
    julia> reinterpret(Mava, UInt16(799))
    julia> mava"1VPK9" + mava"1"
    julia> mava"3VPK3" + mava"2VTK9"
  3. programmingpraxis said

    @Jussi: Fixed. Thanks. WordPress recently updated its editor, again, and every time they do something breaks.

  4. Paul said

    In Python with checks on input.

    prog = re.compile("^([1-9]V)?(T)?(P)?(K)?([1-9])?$")
    VALUE = {str(i)+"V": i*80 for i in range(1, 10)}
    VALUE.update(dict(zip("TPK123456789", (40, 20, 10) + tuple(range(1, 10)))))
    def man_to_dec(man):
        m = prog.match(man)
        if not m:
            raise ValueError("not a valid Mangarevan format " + man)
        return sum(VALUE[group] for group in filter(None, m.groups()))
    def dec_to_man(n):
        if not 1 <= n <= 799:
            raise ValueError("Outside range [1, 799]")
        res = []
        d, m = divmod(n, 80)
        if d:
            res += [str(d), "V"]
        for i, s in zip((40, 20, 10), "TPK"):
            if m >= i:
                m -= i
        if m:
        return "".join(res)

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: