# Multibase Numbers

(Similar: Through the bases)

Normally, our number system is base ten, with the digits 0123456789. However, we can increase or decrease the base count (so hexadecimal uses 0123456789abcdef and octal uses 01234567.

Your goal is: given a string, interpret it in all bases 2-36 for which it's valid, compute the total, and express that total in the smallest base that was valid for the original string.

The valid base characters are 0123456789abcdefghijklmnopqrstuvwxyz. It is not case sensitive!

If it is invalid, return zero. There may be a sign for the first character (a + or -): if it is a -, then return the result with a - before it.

## Test cases:

Xx -> 32s (base 34-36, in base 34, after lowercasing)
zzzzzz -> zzzzzz (36 is the only base that works, the number in base 36 is just "zzzzzz")
12 -> 1000002
MultiBase -> 8qa4f6laud
1v1 -> app
gy -> ye
+Huh -> 41ou
+h -> hh
abc -> 58b5b
-hello -> -k029mh
one -> hmm5
NaN -> k1hl
-Infinity -> -16di5ohk2
hello world -> 0
$0.99 -> 0  This is , so the shortest answer in bytes wins! • The last test case implies case-sensitivity. Most of the time, in number bases above 10, case-sensitivity is NOT assumed; that is, in hexadecimal, face and FACE are the same number. If you really intend case-sensitivity, please so state explicitly. Feb 28 at 20:06 • given a string, find the sum of all bases that can be represented from 1-36, in the smallest base that can be represented Can you explain that more clearly? I've read the sentence several times and I can't figure out what you mean Feb 28 at 20:11 • I've removed the requirement, as it doesn't make much sense, and I've clarified how the problem works. Feb 28 at 20:19 • NaN can be parsed in base 24 to base 36, for a total of 277485, which is k1hl in base 24. I also get a different result for +Huh. Feb 28 at 20:41 • Maybe include a test case with a period? My preferred golfing language (Raku) has a built-in parsing method that parses a string with a single period into a floating-point number, which is not allowed by the problem statement, so I had to explicitly test for a period. Perhaps the same issue affects other languages. – Sean Mar 1 at 21:38 ## 16 Answers # Go, 146 bytes import."strconv" func f(n string)string{s,m:=int64(0),36 for b:=m;b>1;b--{if i,e:=ParseInt(n,b,0);e==nil{s+=i;if m>b{m=b}}} return FormatInt(s,m)}  Attempt This Online! ### Explanation import."strconv" // boilerplate func f(n string)string{ // boilerplate s,m:=int64(0),36 // sum, minimum usable base for b:=m;b>1;b--{ // for all bases from 36 to 2... if i,e:=ParseInt(n,b,0);e==nil{ // parse n as that base. if successful... s+=i // add to the sum if m>b{m=b}}} // set to the lower base return FormatInt(s,m)} // format the number in the minimum base.  # JavaScript (ES7), 83 bytes s=>(g=b=>s>'!'&(P=parseInt)(s+0,b)**2>(v=P(s,b))*v&&v+g(b-1,q=b))(q=36).toString(q)  Try it online! • What does s>'!'& do? – l4m2 Oct 15 at 11:41 • Seems like s>'!' could simply be replaced with a 1, as it still passes all the test cases and the only time s>'!' evaluates to false is when s starts with an exclamation mark or a space (or some non-printable ASCII characters), and even when testing with strings containing exclamation marks the output is unchanged compared to the original function here (returning "0"). Although I'm still unsure as to how exactly this works, at least this would already shave off 4 bytes. Oct 15 at 20:52 • I don't remember what was the idea behind this s>'!' nor why I added the test case " zzzzz". This may be related to some Q&A with the OP that have since been deleted. Oct 15 at 23:20 # Ruby, 89 bytes ->s{o=p;(2..36).sum{|b|q=s.to_i b;s.tr(?+,'').downcase==q.to_s(b)?(o||=b;q):0}.to_s o||2}  Attempt This Online! # Factor, 77 75 bytes [ 2 36 [a,b] [ base> ] with map dup sift sum swap [ f = ] count 2 + >base ]  Try it online!  ! "abc" 2 36 [a,b] ! "abc" { 2 3 ... 36 } [ base> ] with map ! { f f f f f f f f f f f 1845 2126 2427 2748 3089 3450 3831 4232 4653 5094 5555 6036 6537 7058 7599 8160 8741 9342 9963 10604 11265 11946 12647 13368 } dup ! { f f f f f f f f f f f 1845 2126 2427 2748 3089 3450 3831 4232 4653 5094 5555 6036 6537 7058 7599 8160 8741 9342 9963 10604 11265 11946 12647 13368 } { f f f f f f f f f f f 1845 2126 2427 2748 3089 3450 3831 4232 4653 5094 5555 6036 6537 7058 7599 8160 8741 9342 9963 10604 11265 11946 12647 13368 } sift ! { f f f f f f f f f f f 1845 2126 2427 2748 3089 3450 3831 4232 4653 5094 5555 6036 6537 7058 7599 8160 8741 9342 9963 10604 11265 11946 12647 13368 } { 1845 2126 2427 2748 3089 3450 3831 4232 4653 5094 5555 6036 6537 7058 7599 8160 8741 9342 9963 10604 11265 11946 12647 13368 } sum ! { f f f f f f f f f f f 1845 2126 2427 2748 3089 3450 3831 4232 4653 5094 5555 6036 6537 7058 7599 8160 8741 9342 9963 10604 11265 11946 12647 13368 } 162316 swap ! 162316 { f f f f f f f f f f f 1845 2126 2427 2748 3089 3450 3831 4232 4653 5094 5555 6036 6537 7058 7599 8160 8741 9342 9963 10604 11265 11946 12647 13368 } [ f = ] count ! 162316 11 2 + ! 162316 13 >base ! "58b5b"  # Charcoal, 48 bytes ≔⌕θ-η×¬η-≔↧Φθ∨κ∧η⌕θ+θ≔ＥΦ³⁷⬤θ№Ｅι⍘νιλ⍘θιη⍘↨η¹⁻³⁷Ｌη  Try it online! Link is to verbose version of code. Explanation: ≔⌕θ-η  See whether the string begins with -. ×¬η-  Output a - if it does. ≔↧Φθ∨κ∧η⌕θ+θ  Remove the first character of the string if it was - or + and lowercase the result. ≔ＥΦ³⁷⬤θ№Ｅι⍘νιλ⍘θιη  Loop through the bases up to 36, checking that all the characters in the string can be represented in that base, and if so, convert the string from that base. No number can successfully be represented in base 0, and while 0 can be successfully represented in base 1, fortunately this won't affect the result. ⍘↨η¹⁻³⁷Ｌη  Take the sum of all of the conversions and convert that into the lowest successful base, calculated as 37 minus the number of successful base conversions (if there were none then the 0 will be converted into base 37 but that's still 0). # 05AB1E, 36 35 bytes „+-мl©žLRlÙη¤®Skà.$¬UδÅβþOXÅвJI'-Ãì


Explanation:

(Assumes the input will never contain any - or + in the middle of the input, only optionally at the front.)

„+-м            # Remove a potential "+" or "-" from the (implicit) input
l           # Convert it to lowercase
©          # Store this modified input in variable ® (without popping)
žL              # Push builtin "zyx..cbaZYX..CBA987..210"
R             # Revert it
l            # Convert it to lowercase as well
Ù           # Uniquify it to "012...789abc...xyz"
D          # Duplicate this string
η         # Get all its prefixes
s        # Swap so the string is at the top again
®       # Push modified input ®
S      # Convert it to a list of characters
k     # Get the 0-based index of each character in the string
à    # Pop and push the maximum
.$# Remove that many leading items from the list of prefixes ¬ # Push the first prefix (without popping the list of prefixes) U # Pop and store this first prefix in variable X δ # Map and use each prefix separately as argument on the modified input: Åβ # Convert it to this prefix as custom base þ # Only keep all items consisting solely of digits (for invalid inputs) O # Sum this list together X # Push the first prefix from variable X Åв # Convert the sum to this custom base as list J # Join this list of characters together to a string I # Push the input-again '-Ã '# Only keep its "-" ì # Prepend it in front of the string # (after which the result is output implicitly)  # Pip, 22 bytes aFB:,37($+FIaTB:uNa)|0


Outputs in uppercase. Attempt This Online! Or, verify all test cases.

### Explanation

Pip's base-conversion builtins were made for this challenge.

• They work for bases 2 through 36, using 0..9A..Z as digits.
• They accept both lower- and uppercase inputs.
• They even handle the leading + and - correctly.
• If FB can't convert from a given base, it returns nil (and issues a warning which is ignored by default).

Thus:

aFB:,37($+FIaTB:uNa)|0 a ; First command-line input FB: ; Convert from the following base(s) and assign back to a: ,37 ; All numbers from 0 through 36 ; This results in a 37-element list, which contains nil for ; any base that's too small (including 0 and 1) and a ; decimal integer for the rest FIa ; Filter the falsey values out of that list$+             ; Sum it
uNa    ; Count the number of times nil occurs in the list
TB:       ; Convert the sum to that base
; If the input was invalid, every element of the list was
; nil, which means we just tried to convert 0 to base 37,
; which returns nil rather than 0, so...
(           )|0 ; If that result was falsey, use 0 instead


# Excel, 136 bytes

=LET(
b,"-",
c,TEXTSPLIT(A1,"+",b,1),
d,ROW(A:A),
e,DECIMAL(c,d),
f,1-ISERR(e),
IFERROR(REPT(b,LEFT(A1)=b)&BASE(SUM(IF(f,e)),MIN(IF(f,d))),0)
)


Input in cell A1.

# Rust, 362 bytes

|s|{let(mut o,mut m)=("".repeat(1),36);let mut n=(2i32..=36).rev().filter_map(|i|i128::from_str_radix(s,i as u32).ok()).zip((2i32..=36).rev()).fold(0,|a,(n,b)|{if m>b{m=b}a+n});let k=n<0;n=n.abs();while{let(d,r)=(n/m as i128, n%m as i128);o=format!("{}{o}",&"0123456789abcdefghijklmnopqrstuvwxyz"[r as usize..=r as usize]);n=d;n>0}{}if k{format!("-{o}")}else{o}}


Attempt This Online!

I'm not very familiar with golfing in Rust, but here is a quick-and-dirty port of my Golang answer.

About half the bytes are dedicated specifically for turning the sum back into base m, because apparently, Rust doesn't have a to_str_radix or equivalent method for an arbitrary base <=36.

# Raku, 70 bytes

{{.sum.base(.first(?*):k//2)}([map {!/\./&&.parse-base($^b)//0},^37])}  Try it online! This is an anonymous function taking its argument in $_. Its form is { ... }(expr), where the inner braces are another anonymous function, which is immediately evaluated with the argument expr, available in the inner function as $_. This is a golfing idiom I use to avoid assigning to a temporary variable. • map { !/\./ && .parse-base($^b) // 0 }, ^37 parses the input string into all bases from zero to thirty-six. parse-base returns an error for the invalid bases 0 and 1, as well as any bases for which the input string has invalid characters, so the // 0 converts those errors into the number zero. parse-base is case-insensitive and also handles leading "+" and "-" characters. Unfortunately for this problem, it also accepts a single period character, parsing such a string into a floating-point number, so I have to explicitly guard against that with !/\./.
• [ ... ] wraps the result of that mapping into an array, so that we can traverse it twice later. (map returns a lazy sequence object that can only be traversed once.)
• .sum.base(...) is the sum of those parsed numbers (plus zeroes for the invalid bases) expressed in the base given by the ..., to wit:
• .first(?*):k returns the index (thanks to the :key adverb) of the first element of those parsed numbers that is nonzero, per the ?* anonymous function that converts its argument to a boolean value.
• // 2 defaults the base to 2 if first did not find any nonzero numbers, as when the input string is not valid in any base. The sum in this case will always be zero, so any valid base could be used here, though of course 2-9 are the best for golfing.
• how nice and thanks for the explanations too. i was about to post a Raku solution, but saw this. mine is somewhat similar to this, so i'm commenting instead of posting. {$_=($^n.parse-base($_)//""for ^37);.sum&&.sum.base(.first(~*):k)}. It weighs 66 bytes. Mar 2 at 9:26 • @MustafaAydın Nice! Although note that your solution parses a string with a period in it into a floating-point number, which the problem statement does not permit. My original 63-byte solution did too, so I had to add the !/\./&& for seven extra bytes. – Sean Mar 2 at 18:49 # Zsh, 84 bytes for b ({36..1})((r+=$b#${(M)1:#(+|-|)[[:alnum:]]##}))||break <<<$[[##$[b+1]]r]||<<<0  Try it online! for base ({36..1}) #${(M)1:#$pattern} substitutes$1 if $pattern matches # Zsh supports [base]#[string] numbers. (( sum +=$base#${(M)1:#(+|-|)[[:alnum:]]##} )) || # The previous statement fails in three ways: # - base is invalid (1#...) # - the string is empty # - the string contains invalid characters for the base (13#abc) break # increment$b back to the last valid base. This fails when $1 was invalid, # since then$b is still 36. In that case, we output 0.
<<< $[ [##$[base+1]] sum ] || <<<0


# Pyth, 35 bytes

K37#=+Ziz=tK;s+*>0Z\-@Ljk+UTGj.aZhK


Try it online!

### Explanation

                                       # implicitly Z=0 and z=input()
K37                                    # K = 37
#        ;                          # repeat infinitely until an error occurs, then break
=tK                           #   K = K-1
iz                              #   convert the input from base K to int
=+Z                                #   Z = Z + input as int (base K)
.aZ      # absolute value of Z
j   hK    # converted to base K+1 as a list of ints
@L                # convert each int to the value at its index in
+UTG          # [0-9] + the alphabet
jk              # joined as a string
*>0Z\-                  # "-" if Z is less than 0, "" otherwise
+                        # prepend
s                         # join all elements together


# Java (JDK), 114 bytes

s->{Integer t=0,b=1,r=40;for(;++b<37;)try{t+=t.parseInt(s,b);r=b<r?b:r;}finally{continue;}return t.toString(t,r);}


Try it online!

Saved 2 bytes thanks to ceilingcat.

# C (gcc) with -lgmp, 231 225 bytes

• -6 thanks to ceilingcat

Uses GMP in case of large values.

#import<gmp.h>


Try it online!

Ungolfed:

#import<gmp.h>
f(s,t,b,r,g)char*s,*t;{
mpz_t x,y;
mpz_inits(x,y,r=0);
t=s;
g=*s; // get sign (if any)
for(g-43&&g-45?g=0:(t=++s); // advance past sign (GMP only parses unsigned)
*t;)r+=!isalnum(*t++); // check for non-digit characters
for(b=36; // start at base 36
b>1&& // end at base 2 if it gets this far
!r&& // only parse valid strings
!mpz_set_str(y,s,b); // stop when the parse fails
b--) // base worked: go to next-lowest base
printf("-%s"+(g!=45),mpz_get_str(0,b+1,x)); // print the sum in lowest base, adding sign if needed
}


# Julia 1.0, 84 bytes

!a=string((l=2:36 .|>b->try parse(Int,a;base=b)catch;0end)|>sum;base=1+argmax(l.>0))


Try it online!

# Scala, 325 bytes

I was rewriting @naffetS's Ruby answer in Scala.

$$\ \color{red}{\text{But the scala code fails on 4 cases. Any help would be appreciated.}} \$$

Golfed version. Try it online!

def f(s:String)={val l=s.toLowerCase;val B="0123456789abcdefghijklmnopqrstuvwxyz".toList;var o:Option[Int]=None;val r=(2 to 36).flatMap{b=>if(l.forall(c=>B.take(b).contains(c))){val q=BigInt(s,b);if(s.replace("+","").toLowerCase==q.toString(b)){o=o.orElse(Some(b));Some(q)}else None}else None}.sum;r.toString(o.getOrElse(2))}


Ungolfed version. Try it online!

object Main extends App {
def f(s: String): String = {
var o: Option[Int] = None
val baseChars = "0123456789abcdefghijklmnopqrstuvwxyz".toList
val sum = (2 to 36).flatMap { b =>
if (s.toLowerCase.forall(char => baseChars.take(b).contains(char))) {
val q = BigInt(s, b)
if (s.replace("+", "").toLowerCase == q.toString(b)) {
o = o.orElse(Some(b))
Some(q)
} else {
None
}
} else {
None
}
}.sum

sum.toString(o.getOrElse(2))
}

val data = List(
("Xx", "32s (base 34-36, in base 34, after lowercasing)"),
("zzzzzz", "zzzzzz (36 is the only base that works, the number in base 36 is just \"zzzzzz\")"),
("12", "1000002"),
("MultiBase", "8qa4f6laud"),
("1v1", "app"),
("gy", "ye"),
("+Huh", "41ou"),
("+h", "hh"),
("abc", "58b5b"),
("-hello", "-k029mh"),
("one", "hmm5"),
("NaN", "k1hl"),
("-Infinity", "-16di5ohk2"),
("hello world", "0"),
("\$0.99", "0")
)

data.map { case (y, z) =>
val s = f(y)