Mangarevan Counting
June 20, 2017
Six hundred years ago, the people of the French Polynesian island of Mangareva developed a mixed-radix counting system that combined binary and decimal elements to count from 1 to 799. They had no zero. The digits 1 through 9 had their normal decimal value. Digits K, P, T and V had values 10, 20, 40 and 80, respectively, so they increased in a binary progression. A number N was represented as N = nV + T + P + K + m, where n and m were digits; note that T, P and K did not have modifiers. Thus, 73 is represented as TPK3, 219 is represented as 2VTK9, and 799 is represented as 9VTPK9 in Mangarevan. You might enjoy this article in Nature and this article in the Proceedings of the National Academy of Sciences. Arithmetic is interesting: 1VPK9 + 1 = 1VT, and 3VPK3 + 2VTK9 = 6VK2.
Your task is to write programs that translate to and from Mangarevan counting numbers. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.
In Python, abusing the eval() function.
(@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)))) end 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 : "", '"') end function +(ma::Mava, va::Mava) reinterpret(Mava, reinterpret(UInt16, ma) + reinterpret(UInt16, va)) end endjulia> include("mangareva.jl") PracticalMangarevaCounting julia> using PracticalMangarevaCounting julia> reinterpret(Mava, UInt16(73)) mava"TPK3" julia> reinterpret(Mava, UInt16(219)) mava"2VTK9" julia> reinterpret(Mava, UInt16(799)) mava"9VTPK9" julia> mava"1VPK9" + mava"1" mava"1VT" julia> mava"3VPK3" + mava"2VTK9" mava"6VK2"@Jussi: Fixed. Thanks. WordPress recently updated its editor, again, and every time they do something breaks.
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 res.append(s) if m: res.append(str(m)) return "".join(res)From Mangarevan.c:
#include "seal_bool.h" /* <http://GitHub.com/sealfin/C-and-C-Plus-Plus/blob/master/seal_bool.h> */ #include <string.h> #include <stdio.h> #include <stdlib.h> bool f_IsInSet( const char p_character, const char * const p_set, size_t * const p_index ) { while( *p_index < strlen( p_set )) { if( p_character == p_set[ *p_index ] ) return p_character != '#'; else if(( p_set[ *p_index ] == '#' ) && ( p_character >= '1' ) && ( p_character <= '9' )) return true; ( *p_index ) ++; } return false; } bool f_FromMangarevan( const char * const p_string, unsigned int * const p_number ) /* Returns true if: * p_number != NULL; * p_string != NULL; * and p_string points to a string in the format "#VPTK#", where '#' is a digit in the range [ 1, 9 ], and where the elements "#V", 'P', 'T', 'K', and '#' are all optional, but it is required that at least one of those elements be present in the string pointed to by p_string. */ { size_t i; const char * const set = "#KPTV"; size_t index_into_set = 0; unsigned int number = 0; const unsigned int set_value[] = { 0, 10, 20, 40, 80, 0 }; if( p_number == NULL ) return false; if( p_string == NULL ) return false; i = strlen( p_string ); if( i == 0 ) return false; for( ; i > 0; i -- ) if( !f_IsInSet( p_string[ i - 1 ], set, &index_into_set )) return false; else if( index_into_set == 0 /* '#'. */ ) { number += ( p_string[ i - 1 ] - '0' ); index_into_set ++; } else if( index_into_set == 4 /* 'V'. */ ) { if( i < 2 ) return false; i --; if(( p_string[ i - 1 ] < '1' ) || ( p_string[ i - 1 ] > '9' )) return false; number += ( 80 * ( p_string[ i - 1 ] - '0' )); index_into_set ++; } else number += set_value[ index_into_set ++ ]; *p_number = number; return true; } int main( const int argc, const char * const argv[] ) { bool error_occurred = false; size_t index; unsigned int number; #ifdef LEONARDO if( argc != 1 ) error_occurred = true; else index = 0; #else if( argc != 2 ) error_occurred = true; else index = 1; #endif if( !error_occurred ) error_occurred = !f_FromMangarevan( argv[ index ], &number ); if( error_occurred ) { printf( "\nThis program must be passed, via the command line, a Mangarevan number, which this program will then convert into the equivalent denary integer.\n" ); #ifndef LEONARDO printf( "\n" ); #endif exit( EXIT_FAILURE ); } else { printf( "\nThe denary integer %u is equivalent to the Mangarevan number %s.\n", number, argv[ index ] ); #ifndef LEONARDO printf( "\n" ); #endif exit( EXIT_SUCCESS ); } }To Mangarevan.c:
#include "seal_bool.h" /* <http://GitHub.com/sealfin/C-and-C-Plus-Plus/blob/master/seal_bool.h> */ #include <string.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> bool f_StringToNumber( const char * const p_string, unsigned int * const p_number ) /* Returns true if: * p_number != NULL; * p_string != NULL; * the string pointed to by p_string is comprised of – and only of – one or more digits in the range [ 0, 9 ]; * and the number represented by the string pointed to by p_string is ≤ UINT_MAX. */ { size_t i, k = 0 /* Index of first non-zero digit. */; #ifdef LEONARDO unsigned long multiplier = 1, number = 0; #else unsigned long long multiplier = 1, number = 0; #endif if( p_number == NULL ) return false; if( p_string == NULL ) return false; i = strlen( p_string ); if( i == 0 ) return false; for( ; k < strlen( p_string ); k ++ ) if( p_string[ k ] != '0' ) break; for( ; i > k; i --, multiplier *= 10 ) { const char c = p_string[ i - 1 ]; if( multiplier > UINT_MAX ) return false; if(( c >= '0' ) && ( c <= '9' )) { number += (( c - '0' ) * multiplier ); if( number > UINT_MAX ) return false; } else return false; } *p_number = number; return true; } char *f_ToMangarevan( unsigned int p ) { unsigned int digit; static char s[ 7 ]; size_t s_length = 0; /* 'V'. */ digit = p / 80; p -= ( digit * 80 ); if( digit > 0 ) { s[ s_length ++ ] = digit + '0'; s[ s_length ++ ] = 'V'; } /* 'T'. */ digit = p / 40; p -= ( digit * 40 ); if( digit > 0 ) s[ s_length ++ ] = 'T'; /* 'P'. */ digit = p / 20; p -= ( digit * 20 ); if( digit > 0 ) s[ s_length ++ ] = 'P'; /* 'K'. */ digit = p / 10; p -= ( digit * 10 ); if( digit > 0 ) s[ s_length ++ ] = 'K'; if( p > 0 ) s[ s_length ++ ] = p + '0'; s[ s_length ] = '\0'; return s; } int main( const int argc, const char * const argv[] ) { bool error_occurred = false; size_t index; unsigned int number; #ifdef LEONARDO if( argc != 1 ) error_occurred = true; else index = 0; #else if( argc != 2 ) error_occurred = true; else index = 1; #endif if( !error_occurred ) if( !f_StringToNumber( argv[ index ], &number )) error_occurred = true; else if(( number < 1 ) || ( number > 799 )) error_occurred = true; if( error_occurred ) { printf( "\nThis program must be passed, via the command line, a denary integer in the range [ 1, 799 ], which this program will then convert into the equivalent Mangarevan number.\n" ); #ifndef LEONARDO printf( "\n" ); #endif exit( EXIT_FAILURE ); } else { printf( "\nThe Mangarevan number %s is equivalent to the denary integer %u.\n", f_ToMangarevan( number ), number ); #ifndef LEONARDO printf( "\n" ); #endif exit( EXIT_SUCCESS ); } }The programs are known to run on an Apple Power Mac G4 (AGP Graphics) (450MHz processor, 1GB memory) on both Mac OS 9.2.2 (International English) (the programs interpreted using Leonardo IDE 3.4.1) and Mac OS X 10.4.11 (the programs compiled using Xcode 2.2.1).
(I’ve just completed a gig at London South Bank University and so I’m again just trying to solve the problems posed by this ‘site whilst I try to get a job; I’m well-aware that my solutions are far from the best – but, in my defence, I don’t have any traditional qualifications in computer science :/ )