# Look mom! I made my own (Base 10) numeral system! [closed]

We've all done it, well, maybe not, but making your own alien language and numbering system is a staple of especially fantasy writing, but is mostly just a fun activity.

The task is simple, take two inputs:

1. An ordered list input of 10 [ten] unique 'numerals' (any printable ASCII character) and interpret them, in order, as the values 0, 1, 2, 3,...,9

+There are exceptions to what can be a numeral here. Arithmetic operators (+, -, *, /), Parentheses, and spaces cannot be used as one of the numerals.

2. An arithmetic problem using only those 'numerals'

And output the equivalent integer result in the given form.

Here's an example:

INPUT

abcdefghij

bcd + efg + hij

OUTPUT

bdgi


In the example, the input list (you may choose which form the list comes in) of 'abcdefghij' corresponds to '0123456789' just as 'hjkloiwdfp' would also correspond 1 to 1 with '0123456789' where instead of 'a' associating with zero, 'h' does. The arithmetic following 'translates' to 123 + 456 + 789, which equals 1368. This must then be outputted in the form we gave it, so b (which represents 1) d (for 2) g (for 6) and i (for 8).

TEST CASES

abcdefghij
abc + def - ghij

-gedc

qwertyuiop
qwerty / uiop

e

%y83l;[=9|
(83l * 9) + 8%

y9|8


MORE RULES

• Standard Loopholes are forbidden!
• This is code golf, so shortest answers in bytes wins.
• Must be a full program or function taking the inputs and outputs in whatever format works best for you. (Just cannot add additioal information in the inputs, just 'numerals' and the expression.
• Use any language you'd like (as long as it complies with other rules)
• The 2nd test case suggests that the final output is rounded, otherwise the result would be q.ioiopewioyetqorw.... If so, what kind of rounding should be applied? Aug 8, 2019 at 15:26
• To add to @SriotchilismO'Zaic 's point, we also have a sandbox for your benefit and ours; the intent is to let the community help refine challenges before they are posted. Nice idea for a challenge, though! Aug 8, 2019 at 16:16
• Different languages are likely to evaluate the same equation differently, I'm not sure there is any way around it. For example, T-SQL returns 1 for 5/3, not 2, due to integer division (not rounding). This doesn't invalidate the challenge, but you may have to allow different acceptable answers for the same test case (see my T-SQL answer below). Aug 8, 2019 at 16:30
• @Giuseppe Wow, I have browsed this stack for a long while and never knew of that! Would have definitely been helpful, especially as the first time poster (long time listener) that I am. Will keep note for next time! Thanks for your comment and answer. Aug 8, 2019 at 17:06
• An interesting variation on this would be one that supports any number base, depending on the length of the first string in the input... Aug 9, 2019 at 13:54

# 05AB1E, 10 9 bytes

žh‡.Eò¹Åв


(Now) outputs as a list of characters.

Explanation:

  ‡        # Transliterate the second (implicit) input, replacing every character of
# the first (implicit) input with:
žh         # The builtin "0123456789"
.E      # Then evaluate it as Elixir code
ò     # Round it to the nearest integer
¹Åв  # And change it back by using a custom base-conversion with the first input as
# base (which results in a character list)
# (after which that result is output implicitly)


The new version of 05AB1E is build is build in Elixir. The .E function will call call_unary(fn x -> {result, _} = Code.eval_string(to_string(x)); result end, a), where the Code.eval_string is an Elixir builtin.

Note that the legacy version of 05AB1E won't work for this, because it is build in Python. The numbers with leading 0s won't be evaluated:
See all test cases in the legacy version (which uses the 10-byte version because the Åв builtin is new).

# R, 58 bytes

function(d,s,[=chartr)'0-9'[d,eval(parse(t=d['0-9',s]))]


Try it online!

Uses character translation chartr to swap the digits, parses and evals the expression, and then chartrs back to the original digits.

If rounding to nearest integer is required, this is

# R, 65 bytes

function(d,s,[=chartr)'0-9'[d,round(eval(parse(t=d['0-9',s])))]


Try it online!

• Using [ as a shorter name for a function with 3 parameters is very smart. Well done. Aug 9, 2019 at 6:12

# T-SQL, 117 bytes

DECLARE @ CHAR(99)
SELECT @='SELECT TRANSLATE('+TRANSLATE(e,c,'0123456789')+',''0123456789'','''+c+''')'FROM t
EXEC(@)


Line breaks are for readability only.

Input is via a pre-existing table t with text columns c (characters) and e (equation), per our IO rules.

Uses the SQL 2017 function TRANSLATE to switch between characters and generate a string that contains not only the equation, but the code to translate back to the original characters:

SELECT TRANSLATE(123 + 456 + 789,'0123456789','abcdefghij')


This string is then evaluated using EXEC().

There may be some characters (such as a single quote ') that would break this code; I haven't tested all possible ASCII characters.

Per the challenge, I am evaluating the expression as given, subject to how my language interprets those operators. As such, the second test case returns 1 (w), and not 2 (e), due to integer division.

# Perl 6, 38 bytes

{*.trans($_=>^10).EVAL.trans(^10=>$_)}


Try it online!

I'm not sure how the rounding is supposed to work. If it rounds at the end then I can add .round for +6 bytes. If the behaviour of / should be different then it may be longer. Takes input curried like f(arithmetic)(numerals)(arithmetic).

### Explanation:

{                                    }  # Anonymous codeblock
*                                      # Returning a whatever lambda
.trans($_=>^10) # That translates the numerals to digits .EVAL # Evaluates the result as code .trans(^10=>$_)   # And translates it back again


# Stax, 7466 65 bytes

┼ö8Q#xóπcM~oÖ÷╦├mî☼yº─▐4ç≥e╘o▄ê‼ø_k╜ø8%N╫ ╗e<.╗P[─╛èA±!xêj«w╠°{B♪


Run and debug it

Stax does not do well here, lacking a true "eval" instruction. It has one that's called "eval" in the docs, but it only works on literal values, not full expressions.

• This might not obey operator precidence. Not sure if that is required though? staxlang.xyz/…
– dana
Aug 9, 2019 at 5:16
• @dana: Good point. I didn't consider that. A fix is likely to cost some bytes, so I'll wait for some clarification before trying to change that behavior. Aug 9, 2019 at 15:02

# Bash, 97 bytes

IFS=''
A=echo "$O"|tr "$S" 0-9
printf %0.f bc<<<"(${A[@]##0})+0.5"|tr 0-9 "$S"


Could be less if we could truncate, rather than round. Also tricky to handle leading zeroes (as in test case #2) since Bash interprets numbers starting with 0 as octal.

• What is the consensus on using utilities like "bc" and "tr" for golfing? Aug 9, 2019 at 0:08
• I'm no expert, but I think those types of answers are typically submitted as something like "bash + coreutils" Aug 9, 2019 at 0:18
• @Giuseppe tr is part of the coreutils, while bc is not. Nevertheless, bc is a very common tool. Every other command in this answer is bash. Aug 9, 2019 at 6:21
• -7 bytes stolen from @CM's answer, reducing '0123456789' to '0-9' Aug 9, 2019 at 18:41
• Defining T is no longer advantageous: $T is only one byte shorter than 0-9, you only use it twice, and you spend 8 bytes to define it. – ruds Aug 10, 2019 at 19:42 # Bean, 94 90 bytes ### Hexdump 00000000: 53d0 80d6 d800 d3d0 80a0 1f20 8047 53a0 SÐ.ÖØ.ÓÐ. . .GS 00000010: 1753 d080 d3d0 80a0 5e20 800a a181 8100 .SÐ.ÓÐ. ^ ..¡... 00000020: 40a0 5f52 cac3 4da0 6580 53d0 80a0 5d20 @ _RÊÃM e.SÐ. ] 00000030: 8089 205f a065 205f 2080 0aa1 8181 0123 .. _ e _ ..¡...# 00000040: 0058 0020 800a a181 8102 40a0 6550 84a0 .X. ..¡...@ eP. 00000050: 5d20 652e dce2 b02b dc64 ] e.Üâ°+Üd  ### JavaScript ${Math.round(
eval(
b.replace(
/./g,
c => ~(i = a.indexOf(c)) ? i : c
).replace(
/\b0+/g,
''
)
)
)}.replace(
/\d/g,
i => a[i]
)


## Explanation

This program implicitly assigns the first and second lines of input as strings to the variables a and b respectively.

Each character c on line b is replaced with the respective index i of the character found on line a, or itself if it is not found.

Then it removes each sequence of one or more 0s preceded by a boundary from the resulting string. This is to prevent eval() from evaluating any sequence of digits starting with 0 as an octal literal.

After eval() and Math.round(), the result is coerced back into a string and each digit character i is replaced by the corresponding character from line a at index i.

## Test Cases

Demo

abcdefghij
abcd + efg + hij

bdgi


Demo

abcdefghij
abc + def - ghij

-gedc


Demo

qwertyuiop
qwerty / uiop

e


Demo

%y83l;[=9|
(83l * 9) + 8%

y9|8


# Perl 5-p, 63 bytes

$p=<>;eval"y/$p/0-9/";s/\b0+\B//g;$_=int.5+eval;eval"y/0-9/$p/"


Try it online!

Takes the expression on the first line of input and the translation list on the second.

# Perl 5, 130 bytes

sub f{eval sprintf"'%.0f'=~y/%s/%s/r",eval(eval(sprintf"\$_[1]=~y/%s/%s/r",@r=map"\Q$_",\$_[0],'0123456789')=~s,\b0,,gr),reverse@r}


Try it online!

Maybe that double eval somehow can be turned into s/.../.../geer.

# Charcoal, 14 bytes

⍘ＵＶ⭆η⎇№θι⌕θιιθ


Try it online! Link is to verbose version of code. Note: Expression is evaluated according to Python 3 semantics, so for instance leading zeros on non-zero numbers are illegal. Explanation:

   ⭆η           Map over expression's characters and join
ι       Current character
№θ        Count matches in first input
⎇          If non-zero
⌕θι    Replace with position in first input
ι   Otherwise keep character unchanged
ＵＶ             Evaluate as Python 3
⍘            θ  Convert to base using first input as digits

• Unfortunately leading 0s don't work in Python, which is present in the test cases. Aug 8, 2019 at 22:56

# Python 3, 137 bytes

A non-regex approach using str.translate and str.maketrans to replace the characters. I lost a lot of characters on trimming the leading zeros...

lambda s,t,d='0123456789',e=str.translate,m=str.maketrans:e(str(round(eval(' '.join(c.lstrip('0')for c in e(t,m(s,d)).split())))),m(d,s))


Try it online!

# Python 3, 167 bytes

import re
a=[*enumerate(input())]
e=input()
for i,c in a:e=re.sub(c,str(i),e)
e=str(round(eval(re.sub(r'\b0+(?!\b)','',e))))
for i,c in a:e=re.sub(str(i),c,e)
print(e)


Try it online!

Room for improvement...

• Still fails on the last test case: tio.run/… Aug 14, 2019 at 14:02

# Wolfram Language (Mathematica), 121 bytes

I define a pure function with two arguments. Since some functions are repeated I save them in a variable to save a few characters. This code simply does some string replacements and then uses ToExpression to evaluate the expression with the Wolfram kernel.

(r=Thread[StringPartition[#,1]->(t=ToString)/@Range[0,9]];u[(u=StringReplace)[#2,r]//ToExpression//Round//t,Reverse/@r])&


Try it online!

# Lua, 162151 150 bytes

• -11 bytes thanks to my idea of using load instead of function(...) end
• -1 byte by omitting newline
l,p=...print(((math.ceil(load('return '..p:gsub('.',load'n=l:find(...,1,1)return n and n-1'))()-0.5)..''):gsub('%d',load'c=...+1 return l:sub(c,c)')))


Try it online!

Not shortest thing in the world (Lua forces you to be fancy quite hard, especially by huge keywords), but was pretty fun to create. Full program taking input as arguments and printing result.

## Explanation

### Introduction

l,p=...


Assign values from arguments to variables. Our dictionary is l and expression is p.

Following expression is quite hard to understand because it have a weird order of execution, so I'll explain it step-by-step:

### Converting to normal numbers

p:gsub('.',


Perform replacement on expression string: take each symbol and pass it to function (load proved itself to be shorted than normal declaration here).

Function finds position of occurrence in dict string for passed symbol using find. ... is first (and only) argument here as we're in vaarg function (any loaded one is) that is our current symbol. Following arguments are required to make find ignore special symbols (1 is just short value which evaluates as true when converted to boolean): starting position (one is default here) and plain, which actually disables pattern handling. Without those program fails on third test case due to % being special.

If match is found, subtract one as Lua strings (and arrays btw) are 1-based. If no match is found, it will return nothing, resulting in no replacement being done.

### Solving

math.ceil(load('return '..ABOVE)()-0.5)


Prepend return to our expression to let it return result, calculate it by compiling as Lua function and calling it, perform rounding (this turned other way around to make it shorter).

It the end we get a numerical solution to our problem, only converting it back remains.

### Making it crazy again

(ABOVE..'')


First line is a short way to convert number to string, so now we can call string methods in a short way. Let's do so!

Now gsub is called again to replace everything back to insanity. This time %d is used instead of . as replacement pattern as our function may and must process only numbers (. would result to error on negative numbers). This time function (loaded again to save bytes) first adds 1 to its first (and only) vaargument, converting it to position in dict string, then returns character from it at that position.

Hooray, almost there!

### Dramatic finale, or Why Brackets Matter

print((ABOVE))


Well… why two pairs of brackets anyways? It's time to talk about parall… eh, multiple return in Lua. Thing is that one function may return few values from one call (look at this meta question for more examples).

Here, last gsub returned two values: answer string we need and amount of replacements done (count of digits actually, but who cares). If it wasn't for internal pair, both string and number would be printed, screwing us up. So here we sacrifice two bytes to omit second result and finally print product of this insanity factory.

Well, I've enjoyed explaining almost as much as golfing in first place, hope you got what's going on here.

• Note: it passes all of testcases, but probably rounds incorrectly in others. If you can find one, I'm going to fix it. Aug 9, 2019 at 22:02