23
\$\begingroup\$

Given an integer n ≥ 0 , output it in a non-positional base-3 notation, using digits 139ABCDE… and a 1-character separator. Every digit is a consecutive power of 3 and the digits on the left side of the separator are negated, e.g. A931|B → 81−(1+3+9+27) → 41. A digit may appear only once.

Rigorously, let the value of a digit be:

  • its value if the digit is 1, 3 or 9
  • 27 if the digit is A
  • 3 times the value of the digit right before it for B..Z

Your output should satisfy sum(value of digits to the right of |) - sum(value of digits to the left of |) == input .

Examples

input     output
----------------
0         |
1         |1
7         3|91
730       |D1
9999      FEDC|GA9

You may use a different non-space character as a separator. You are also allowed to have no separator, in which case the largest digit starts the positive sequence. You don’t need to handle anything larger than 232−1 (PMIGDCBA9|RQNLH3).

You may write a full program or function, and input and output may be provided on any of the usual channels.

This is , so the shorter your answer the better!

\$\endgroup\$
  • 2
    \$\begingroup\$ (related doesn't mean duplicate, calm down) \$\endgroup\$ – Leaky Nun Mar 28 '18 at 13:47
  • 8
    \$\begingroup\$ Am I the only one who has no clue what's being asked here? \$\endgroup\$ – Shaggy Mar 28 '18 at 13:47
  • 3
    \$\begingroup\$ @Shaggy Express the input as a sum of powers of 3 and their negatives. Put the negatives left of a | and the positives to the right of it. \$\endgroup\$ – Martin Ender Mar 28 '18 at 13:48
  • 2
    \$\begingroup\$ @KevinCruijssen "no, the order is free." -- OP \$\endgroup\$ – user202729 Mar 28 '18 at 15:39
  • 3
    \$\begingroup\$ @user202729 Ah, missed that comment. Thanks. That's what happens when rules are in the comments instead of edited into the challenge.. (FrownyFrog, could you add that rule to the challenge: either order on either side of the delimiter is fine?) \$\endgroup\$ – Kevin Cruijssen Mar 28 '18 at 15:51

15 Answers 15

5
\$\begingroup\$

Java 10, 120 113 112 109 107 102 bytes

n->{var r="|";for(char c=49;n++>0;c=(char)(c+=c>64?1:c*4%22%9),n/=3)r=n%3<1?c+r:n%3>1?r+c:r;return r;}

-3 bytes by using part of the trick of @Arnauld's JavaScript (ES6) answer,
changing i=0 and i++<1?49:i<3?51:i<4?57:i+61 to i=4 and ++i>9?i+55:i>8?57:++i+43.
-6 bytes thanks to @Arnauld directly, by getting rid of i.

Order of output: Highest-to-lowest, |-delimiter, lowest-to-highest.

Explanation:

Try it online.

n->{              // Method with integer parameter and String return-type
  var r="|";      //  Result-String, starting at the delimiter "|"
  for(char c=49;  //  Character, starting at '1'
      n++>0       //  Loop as long as `n` is larger than 0
                  //  Increasing it by 1 with `n++` at the start of every iteration
      ;           //    After every iteration:
       c=(char)(  //     Change character `c` to:
          c+=c>64?//      If the current `c` is an uppercase letter:
              1   //       Simpy go to the next letter using `c+1`
             :    //      Else:
              c*4%22%9),
                  //       Change '1' to '3', '3' to '9', or '9' to 'A' 
       n/=3)      //     Integer-divide `n` by 3
     r=           //     Change the result to:
       n%3<1?     //      If `n` modulo-3 is 0:
        c+r       //       Prepend the character to the result
       :n%3>1?    //      Else-if `n` modulo-3 is 2:
        r+c       //       Append the character to the result
       :          //      Else:
        r;        //       Leave `r` unchanged
   return r;}     //  Return the result-String
\$\endgroup\$
  • 1
    \$\begingroup\$ I think this works: 103 bytes \$\endgroup\$ – Arnauld Mar 29 '18 at 9:59
  • \$\begingroup\$ @Arnauld Nice one! And -1 more byte by putting r in the loop body. Thanks! \$\endgroup\$ – Kevin Cruijssen Mar 29 '18 at 10:06
  • \$\begingroup\$ @Arnauld Out of curiosity, what does the brute-forcers look like you've used for these last two magic numbers (when you still used i, and when you re-use c)? \$\endgroup\$ – Kevin Cruijssen Mar 29 '18 at 10:11
  • 1
    \$\begingroup\$ I've already thrown it away ... :-/ But here is the last one. (Very inefficient, but that's OK for such small values.) \$\endgroup\$ – Arnauld Mar 29 '18 at 10:22
  • \$\begingroup\$ (Also, I really should test whether p=1 and don't include *1 in the code if it is -- even though it doesn't lead to a better formula in that case.) \$\endgroup\$ – Arnauld Mar 29 '18 at 10:30
5
\$\begingroup\$

Python 3, 103 99 91 bytes

4 bytes thanks to Lynn.

8 bytes thanks to ovs.

def f(n,s="|",b=0):c=('139'+chr(b+62)*b)[b];return n and f(-~n//3,[s,s+c,c+s][n%3],b+1)or s

Try it online!

Credits to xnor for the logic.

\$\endgroup\$
5
\$\begingroup\$

JavaScript (ES6), 82 80 79 bytes

Outputs in lowercase, which should hopefully be fine.

f=(n,s=(k=4,'|'),c=++k>8?k.toString(36):++k-5)=>n?f(++n/3|0,[c+s,s,s+c][n%3]):s

Try it online!

Similar to Leaky "Ninja Master" Nun's answer and also based on xnor's answer.

Digit conversion

We start with k = 4. While k is less than 9, we increment it twice at each iteration and subtract 5. After that, we increment it only once and convert it to base-36.

  k  | ++k > 8       | k.toString(36) | ++k - 5  | result
-----+---------------+----------------+----------+--------
  4  | k=5  -> false |                | k=6 -> 1 | 1
  6  | k=7  -> false |                | k=8 -> 3 | 3
  8  | k=9  -> true  | '9'            |          | '9'
  9  | k=10 -> true  | 'a'            |          | 'a'
  10 | k=11 -> true  | 'b'            |          | 'b'
 ... | ...           | ...            | ...      | ...
\$\endgroup\$
4
\$\begingroup\$

Jelly, 26 bytes

‘:3Ɗ⁹С‘%3ẹЀ0,2ị139D;ØA¤Y

Try it online!

Use a newline as the separator.

\$\endgroup\$
2
\$\begingroup\$

Perl 6, 80 bytes

{map({|(1,3,9,|('A'..'Z'))[grep :k,*%3==$^v,($_,-+^*div 3...0)]},1,2).join.flip}

Try it online!

No separator. Based on xnor's answer.

\$\endgroup\$
2
\$\begingroup\$

Stax, 30 29 bytes

£└≤☻╘pÿ╖╡A[ô%æτ⌐}►ºôßHl4⌡π%^ 

Run and debug it

Port of my Stax answer in Balanced Ternary Converter.

Explanation

Uses the unpacked version to explain.

139$VA+cz{;3%+,^3/~;wY1|I@'|ay2|I@L
139$VA+c                               "139AB...Z", make a copy
        z                              Empty array to store the digits
          {         w                  Do the following until 0.
           ;3%+                           Append `b%3` to the digits
                                          Originally, `b` is the input
              ,^3/                        `b=(b+1)/3`
                  ~;                       Make a copy of `b` which is used as the condition for the loop

                     Y                 Save array of digits in `y` for later use
                      1|I              Find index of 1's
                         @             Find the characters in "139AB...Z" corresponding to those indices
                          '|           A bar
                            ay2|I@     Do the same for 2's
                                  L    Join the two strings and the bar and implicit output
\$\endgroup\$
1
\$\begingroup\$

C# .NET, 103 bytes

n=>{var r="|";for(var c='1';n++>0;c=(char)(c>64?c+1:c+c*4%22%9),n/=3)r=n%3<1?c+r:n%3>1?r+c:r;return r;}

Port of my Java 10 answer. If a direct port (except for n-> to n=>) would have been possible, I would have edited my Java answer with this polyglot. Unfortunately however, c+= on characters or having c=49 isn't possible in C#, hence this loose ported answer.

Try it online.

\$\endgroup\$
1
\$\begingroup\$

Perl 5 -p, 71 69 bytes

uses no separator. The negative and positive parts are in "roman order" (largest digit first)

#!/usr/bin/perl -p
$n=$_}{s/@{[$n++%3]}\K/]/,$n/=3,y/?-]/>-]/for($_=21)x31;y/>?@12/139/d

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Ruby, 87 84 82 bytes

Saved 2 bytes thanks to @benj2240.

->n,s=[?1,?3,?9,*?A..?Z],r=[""]*3{r[-m=n%3]+=s.shift
n=n/3+m/2
n>0?redo:r[1,2]*?|}

Try it online!

\$\endgroup\$
  • \$\begingroup\$ I'd be lying if I said I'm completely following this code, but I do know you shave off 2 bytes with the redo trick: Try it online! \$\endgroup\$ – benj2240 Mar 28 '18 at 22:26
1
\$\begingroup\$

J, 129 bytes

f=:3 :0
a=.'139',u:65+i.26
s=.'|'while.y>0 do.if.1=c=.3|y do.s=.s,{.a end.y=.<.y%3
if.c=2 do.s=.s,~{.a 
y=.1+y end.a=.}.a end.s
)

Try it online!

Too lengthy, especially for a J program...

Explanation:

f =: 3 : 0
   a =. '139',u:65+i.26   NB. a list '139ABC...Z'
   s =. '|'               NB. initialize the list for the result  
   while. y>0 do.         NB. while the number is greater than 0
      c =. 3|y            NB. find the remainder (the number modulo 3)
      y =. <.y%3          NB. divide the number by 3 
      if. c = 1 do.       NB. if the remainder equals 1
         s =. s,{.a       NB. Append the current power of 3 to the result
      end.
      if. c = 2 do.       NB. if the remainder equals 2 
         s =. s,~{.a      NB. prepends the result with the current power of 3
         y =. 1+y         NB. and increase the number with 1
      end.
      a =. }.a            NB. next power of 3 
   end.
   s                      NB. return the result  
)
\$\endgroup\$
1
\$\begingroup\$

C, int: 138 123 bytes, long: 152 131 bytes

I have created two versions of this, as the challenges' limit of a working max input of 0x100000000 seemed a bit odd. One version works with 32 bit integers (which fails the limit for obvious reasons), the other version works with 64 bits (which goes way beyond the given limit, at the cost of 14 8 extra bytes).

32 bit version:

char b[22],*r=b;f(v,l)char*l;{v%3>1?*r++=*l,v++:0;v&&f(v/3,l+1);v%3?*r++=*l:0;}g(v){f(v,"139ABCDEFGHIJKLMNOPQR");*r=0;r=b;}

64 bit version:

char b[22],*r=b;f(long v,char*l){v%3>1?*r++=*l,v++:0;v&&f(v/3,l+1);v%3?*r++=*l:0;}g(long v){f(v,"139ABCDEFGHIJKLMNOPQR");*r=0;r=b;}

This is identical except that it declares the integer variable to be long (which is 64 bits on linux).

The ungolfed long version:

char buffer[22],*result=buffer;
f(long value,char*letter){
    if(value%3>1){
        *result++=*letter,value++;
    }
    if(value){
        f(value/3,letter+1);
    }
    if(value%3){
        *result++=*letter;
    }
}
g(long value){
    f(value,"139ABCDEFGHIJKLMNOPQR");
    *result=0;
    result=buffer;
}

As you can see, this works by recursive decent: If the remainder is 1, the respective character is appended to the output string after the recursive call. If the remainder is 2, the output is performed before the recursing. In this case, I also increment the value by one to handle the negative digit correctly. This has the added benefit of changing the remainder to zero, allowing me to use value%3 as the condition for the post-recursion if.

The result of the conversion is placed into the global buffer. The g() wrapper has the job of zero terminating the resulting string correctly, and to reset the result pointer to its start (which is also how g() "returns" the result).

Test the long version with this code:

#include <stdio.h>

char b[22],*r=b;f(long v,char*l){v%3>1?*r++=*l,v++:0;v&&f(v/3,l+1);v%3?*r++=*l:0;}g(long v){f(v,"139ABCDEFGHIJKLMNOPQR");*r=0;r=b;}

void printConversion(long value) {
    g(value);
    printf("%ld: %s\n", value, r);
}

int main() {
    for(long i = 0; i <= 40; i++) {
        printConversion(i);
    }
    printConversion(0x7fffffff);
    printConversion(0xffffffffu);
    printConversion(0x100000000);
}

Possible further, but destructive golfing:

  • -4 bytes: make the function a one-shot by removing the pointer reset in g().

  • -5 bytes: force the caller to perform the string termination, returning the string without termination in buffer, and the end of the string in result.

\$\endgroup\$
1
\$\begingroup\$

Charcoal, 36 bytes

NθF³⊞υ⟦⟧F⁺139α«⊞§υθι≔÷⊕θ³θ»F²«×|ι↑⊟υ

Try it online! Link is to verbose version of code. Explanation:

Nθ

Input the value.

F³⊞υ⟦⟧

Push three empty lists to the predefined empty list.

F⁺139α«

Loop through the characters 139 and the uppercase alphabet.

⊞§υθι

Cyclically index the list of lists with the value and push the current character to it.

≔÷⊕θ³θ»

Divide the value by 3 but round it by adding 1 first.

F²«×|ι

Loop twice. The second time, print a |.

↑⊟υ

Each loop we pop the last entry from the list; the first time this gives us the entries that had a remainder of 2 (which corresponds to a balanced ternary digit of -1), while the second time this gives us the entries corresponding to a balanced ternary digit of 1. The resulting array would normally print vertically, but rotating the print direction upwards cancels that out.

\$\endgroup\$
1
\$\begingroup\$

J, 69 64 58 bytes

('931',~u:90-i.26){~0(>,&I.<)(29$3)((+1&|.-3&*)]-*)^:_@#:]

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Perl 5, 92 89 bytes

Inspired by the java and python answers.

sub n{($n,$r,$c,@a)=(@_,'|',1,3,9,'A'..'Z');$n?n(int++$n/3,($c.$r,$r,$r.$c)[$n%3],@a):$r}

Try it online!

With some white space:

sub n {
  ($n, $r, $c, @_) = (@_, "|", 1, 3, 9, 'A' .. 'Z');
  $n ? n( int++$n/3, ($c.$r, $r, $r.$c)[$n%3], @_)
     : $r
}
\$\endgroup\$
0
\$\begingroup\$

PHP, 73 bytes

for(;0|$n=&$argn;$n/=3)${$n++%3}.=_139[++$i]?:chr(61+$i);echo${2},_,${1};

port of xnor´s answer, 53 bytes

for(;0|$n=&$argn;$n/=3)$s="0+-"[$n++%3].$s;echo$s??0;

Run as pipe with -nr or try them online.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.