16
\$\begingroup\$

A language that is very useful in restricted source and other such challenges is Unary, a brainfuck derivative in which programs are written in with only one character. Your job is to write a program to convert programs from brainfuck to unary and a program to do the opposite, both programs in the same language. Your score will be the sum of the lengths of the two programs.

How do you convert from brainfuck to Unary?

  • First convert your brainfuck code to binary according to this table:
Brainfuck Binary
> 000
< 001
+ 010
- 011
. 100
, 101
[ 110
] 111
  • Now concatenate the code into one giant binary number in order of the code.

  • Prepend a 1 to the string to ensure a unique binary number.

  • Convert from a binary number to a unary number using any character.

  • Example:

    +.
    

    would become

    000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    

    which is 84 zeroes.

brainfuck → Unary specification:

  • Since the resulting programs will be impossibly huge, print not the actual program but merely the length of the resulting program.
  • Take the brainfuck program as a string through stdin, function arg, etc and output the length.
  • The program will always be valid and have only those 8 chars in it.

Unary → brainfuck specification:

  • You will have to implement the reverse of the above algorithm.
  • Again because of the huge sizes in question, the input will be a number describing the length of the Unary code.
  • Same I/O rules as always.
  • The program will always be valid and have only those 8 chars in it.

Test Cases

  • Hello World:

    ++++++[>++++++++++++<-]>.>++++++++++[>++++++++++<-]>+.+++++++..+++.>++++[>+++++++++++<-]>.<+++[>----<-]>.<<<<<+++[>+++++<-]>.>>.+++.------.--------.>>+.
    

    becomes:

    239234107117088762456728667968602154633390994619022073954825877681363348343524058579165785448174718768772358485472231582844556848101441556`
    
  • Fibonacci:

    ++++++++++++++++++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++++>++++++++++++++++>>+<<[>>>>++++++++++<<[->+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>[<+>-]>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>+>>]<<<<<]>[-]>>[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]<[++++++++++++++++++++++++++++++++++++++++++++++++.[-]]<<<++++++++++++++++++++++++++++++++++++++++++++++++.[-]<<<<<<<.>.>>[>>+<<-]>[>+<<+>-]>[<+>-]<<<-]<<++...
    

    becomes:

    13067995222095367150854793937817629722033205198624522624687536186118993888926522550140580142585590431635487113180955099384652678100247403485397450658564826143160529351955621991895221530908461364045400531236124980271740502887704217664044858614821622360156740992393765239123681327824577149595724956207165558106099868913919959549896553103116795519592552089266360725543244154867904980260
    

This is so the shortest code in bytes wins!

Anyone up for a solution in Unary? ;P

\$\endgroup\$
7
  • 8
    \$\begingroup\$ A more appropriate title would probably be "Brainfuck to Golunar and Back" \$\endgroup\$
    – Sp3000
    Jul 7, 2015 at 4:32
  • \$\begingroup\$ @Sp3000 good point, but I think that most people haven't really heard of it that one (myself included). \$\endgroup\$
    – Maltysen
    Jul 7, 2015 at 4:36
  • \$\begingroup\$ @Maltysen I don't think your test cases are correct. For instance, the leading digits of the first number in binary are 10101010101010, when they should be 1010010010010 \$\endgroup\$
    – isaacg
    Jul 7, 2015 at 5:16
  • \$\begingroup\$ @isaacg sorry, got them off a website that was using a different translation mechanism, will fix. \$\endgroup\$
    – Maltysen
    Jul 7, 2015 at 6:55
  • 1
    \$\begingroup\$ Can we convert them to a program which isn't exactly equal, but does exactly the same thing? \$\endgroup\$
    – jimmy23013
    Jul 7, 2015 at 13:28

10 Answers 10

12
\$\begingroup\$

Pyth, 17 + 17 = 34 bytes

BF -> Unary, 17 bytes

i+1xL"><+-.,[]"z8

Unary -> BF, 17 bytes

s@L"><+-.,[]"tjQ8
\$\endgroup\$
10
\$\begingroup\$

brainfuck, 563 335 318 316 296 + 529 373 366 336 = 632 bytes

Since this was obviously missing a solution in a related language, here is the solution in brainfuck and Golunar. I couldn't post an answer in unary, because that would need a few fantasticatillion times more memory than there are atoms in the universe ^^

The "back" routine won't check if the Golunar/Unary code is valid. If the bit count mod 3 != 1 then it will run into an endless loop printing a lot of ">"s.

Thanks to Nitrodon for helping me getting below 300 chars for the bf to unary code

brainfuck to unary

->+>>>>,[>+++[>+++++<-]>[<+<---->>-]<<--[<+>++[<<+>>>[<+>-]<[<->++[<<<+>->+>-[<->--[<<+>>>+++++++++[<----->-]<[<+>--]]]]]]]>[-]>>,]<<<<+[<+]>[>]->+[<]>+[[->+]->[->[<++>-[<++>-[<++>-[<++>-[<-------->>[-]++<-[<++>-]]]]]]<[>+<-]+>>]<<[<<]>[<]>-[[->+]->>+<[<<]>[<]]>+]>[>>]<<[+++++++[>++++++<-]>.<<<]

Try it online!

and back

->>,[<++++++[>--------<-]+>>>>>>,]>->-<<<+[-<+]>[[->[->+<[->->+>]<[<<<]>]<+>>>[-<<+>>]<[>>>>++++++++++<<<<-]>>>]>>>+[->+]>-<+[-<+]-<[>>+[->+]-<++[-<+]-<[-]]<<<<<<[<<<<<]>>>>>>[<<]<[->>>]>>]>>>+[->+]<-<+[-[<++>-]<[<++>-]>+++[>+++++<-]>[<+<++++>>-]<<++<[>--<-[>>[<->-]<--<-[>++<-[>+<-[>--<-[>+[>+<-]>[<++>-]<+<-[>++<-]]]]]]]>.[-]>[-]<<<+]

Try it online!

Golunar/unary-digits, 509 303 288 286 268 + 478 337 331 304 = 572 bytes

brainfuck to unary

2845581296974449674357817038179762273623136917867627972159702240227366875240878616687779429553529795902322625321040063298921498529640547483869509829184440577052825434462245755576011912505085065586076069824710351537537205287083477698633592357950165322060367940923703887

and back

3775574485023133646619269732540391678811443648964274086227256847322821618228135493733703990523803451383315165001915937932498966394771849173263120467073642011339214182483748816052890450078070151307011943625602391238338941712116968736593594971620990210178757280976709140113340322124688909388916094040773207

Source codes

brainfuck to unary

[
unary:
><+-.,[]
01234567

62 > 62
60 < -2
45 - 15
43 + 2
44 , 1
46 . 2
91 [ 45
93 ] 2

tape (while reading input): Left tape end marker/LTE, [binary data], input, 15, (15 multiplicator)
tape (while base conversion): LTE, [binary data], Value Start/VS, [decimal digits]

decimal digits: digit used/DU, value
]

-                       set LTE
>+                      set leading 1
>>>>,[                  while input
  >+++[>+++++<-]        set 15 (multiplicator)
  >[<+<---->>-]         set 15 and decrease input by 60

                    check for greater than
                        set current bits = 000 (greater than)
  <<--[                 if input != 62 try next char

                    check for less than
  <+>                   set current bits = 001 (less than)
  ++[                   if input != 60 try next char

                    check for minus
  <<+>>                 set current bits = 011 (minus)
  >[<+>-]<[             if input != 45 try next char

                    check for plus
  <->                   set current bits = 010 (plus)
  ++[                   if input != 43 try next char

                    check for comma
  <<<+>->+>             set current bits = 101 (comma)
  -[                    if input != 44 try next char

                    check for dot
  <->                   set current bits = 100 (dot)
  --[                   if input != 46 try next char

                    check for left bracket
  <<+>>                set current bits = 110 (left bracket)
  >+++++++++[<----->-]<[   if input != 91 go to next char


                    use right bracket
  <+>                   set current bits = 111 (right bracket)
  --                    decrease input by 2 / should be 0 now

  ]]]]]]]               close all ifs
  >[-]>>                delete 15 if still existant
  ,                     input next character
]
<<<<+[<+]>[>]           add one to each bit and delete LTE (for shorter search routine)

                    Start of binary to decimal routine

-                       set value start marker (VS)
>+                      set digit used marker (DU)
[<]                     go to LTE

                    binary to decimal loop: use "double and add algorithm" to calculate the digits of the decimal value
>+[                     if not on VS then
  [->+]-                restore current bit value and go to VS
  >                     go to first DU
  [                     digit doubling loop
    ->                  remove DU and go to corresponding digit
    [
      <++>-             decrement current value and add 2 to temp value four times
      [
        <++>-
        [
          <++>-
          [
            <++>-
            [                   if value was greater than 4 then
              <---- ----        subtract 8 from temp
              >>[-]++           set next digit temp = 2 (DU plus 1)
              <-                decrement current digit
              [<++>-]           set temp = remaining value * 2
            ]
          ]
        ]
      ]
    ]
    <[>+<-]             set current digit = temp
    +                   set DU
    >>                  go to next digit
  ]                     end of digit doubling loop
  <<[<<]>[<]>           go to current bit
  -[                    if bit is 2 (used plus 1)
    [->+]-              delete bit and go to VS
    >>+                 increment least significant digit
    <[<<]>[<]           go to current bit
  ]
  >+                    if not on VS then repeat  
]                   end of binary to decimal loop

>[>>]<                  go to most significant digit
<[                  printing loop: for each DU print corresponding value
  +++++++[>++++++<-]>.  add 48 to value (ASCII 0) and print
  <<<                   go to next DU
]

and back

[
tape: left tape end marker/LTE(-1), [digits], digit end marker/DE(0), carry, SB(-1), [binary data], 60, 15
digits: digit used marker/DU(1), digit, remainder, solution, 0
        else]                                    [exit else, exit if
binary data: value (, else, exit if, exit else)
]

                    input decimal value
->>                     set LTE
,[                      while input
  <++++++[>--------<-]  decrease input by 48
  +                     set DU
  >>>>> >,              input next digit
]
>->-                    set start of bits (SB) and first CCB
<<<+[-<+]>              delete LTE and go to first DU

                    division loop: calculate the remainders of the input divided by 2 repeatedly to get the (inverted) bits
[
                        divide each digit by 2
  [                     for each DU
    -                   delete DU (for exit if)
    >                   go to digit
    [->+<               dec digit / set remainder
      [->->+>]          if digit gt 0: dec digit / del remainder / inc solution / goto 0
                        pointer: (value(0) remainder is set) or (0 solution gt 1)
      <[<<<]            go to DU
      >                 go to digit
    ]
    <+                  set DU
    >>>[-<<+>>]         move solution to digit
    <[                  if remainder
      >>>>              go to next digit
      +++++ +++++       add 10 to digit/carry
      <<<<-             go back and delete remainder
    ]
    >>>                 go to next DU
  ]

                    append new bit
  >>>+[->+]             go to and delete CCB
  >-                    set new CCB
  <+[-<+]-<             go to carry
  [                     if carry
    >>+[->+]-<+         set last bit
    +[-<+]-<[-]         go to and clear carry
  ]

                    check if first digit became 0 / neccessary to check if value has been completely processed
  < <<<<<[<<<<<]>>>>>   go to first DU
  >[                    if digit gt 0
    <<                  go to exit if
  ]<[                   else
    -                   delete DU
    >>>                 go to exit else of next digit
  ]
  >>                    go to DU / DE if all digits processed
]                   end of division loop

                    decode binary values
>>>+[->+]               go to and delete CCB (after last bit)
<-                      delete leading 1
<                       go to first bit


                    Start of bit decoder
[
unary:
><+-.,[]
01234567

62 > 62
60 < -2
43 + -17
45 - 2
46 . 1
44 , -2
91 [ 47
93 ] 2

tape: start of bytes marker/SB(-1), [binary data], 60(print char/PC), 15
]

+[-                     while not SB

                    Set least significant to octal value of three bits
  [<++>-]               if first bit set add 2 to second bit
  <[<++>-]              for each second bit add 2 to third bit

  >+++[>+++++<-]        multiplier 15
  >[<+<++++>>-]         setup table 60 15

                    run through the 8 possibilities

                    0 greater than
  <<++                  set PC = 62 (greater than)
  <[                    if value gt 0 go to next char

                    1 less than
  >--                   set PC = 60 (less than)
  <-[                   if value gt 1 go to next char

                    2 plus
  >>[<->-]<--           set PC = 43 (plus)
  <-[                   if value gt 1 go to next char

                    3 minus
  >++                   set PC = 45 (minus)
  <-[                   if value gt 1 go to next char

                    4 dot
  >+                    set PC = 46 (dot)
  <-[                   if value gt 1 go to next char

                    5 comma
  >--                   set PC = 44 (comma)
  <-[                   if value gt 1 go to next char

                    6 left bracket
  >+[>+<-]>[<++>-]<+    set PC = 91 (left bracket) (inc (45) / double (90) / inc (91))
  <-[                   if value gt 1 go to next char

                    7 right bracket
  >++                   set PC = 93 (right bracket)
  <-                    decrease value the last time to exit if

  ]]]]]]]               close all ifs
  >.[-]                 print char and clear PC
  >[-]                  clear 15 if still existant

  <<<                   go to next bits
  +                     repeat if not SB
]
\$\endgroup\$
2
  • 1
    \$\begingroup\$ When converting to Unary, you can subtract the 60 directly from the input cell instead of putting it in its own cell first, saving 16 bytes. A further 4 bytes can be saved by not creating the 45 immediately (thus compacting the tape layout even further). Additionally, it is slightly golfier to check the input bytes in the order 01325467. \$\endgroup\$
    – Nitrodon
    May 24, 2019 at 15:49
  • \$\begingroup\$ The alternative I meant was to create the 45 while you're adding 15 to the input cell. \$\endgroup\$
    – Nitrodon
    May 30, 2019 at 4:05
6
\$\begingroup\$

Python 2, 80 79 63 55 + 86 64 = 119 Bytes

Thanks to Sp3000 for his numerous suggestions, saving a lot of bytes.

Brainfuck to Unary, 78 77 61 53 + 2 = 55 bytes

Added two bytes to account for surrounding "s on input.

print int(`[1]+map("><+-.,[]".find,input())`[1::3],8)

Unary to Brainfuck, 86 64 bytes

print''.join("><+-.,[]"[int(i)]for i in oct(input())[2:]if'L'>i)

Check it out on ideone here.

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

CJam, 35 bytes

Brainfuck to Unary, 17 bytes

1r"><+-.,[]"f#+8b

Try it online.

How it works

 r                e# Read a whitespace-separated token from STDIN.
            f     e# For each character in the token:
  "><+-.,[]" #    e#     Find its index in this string.
1             +   e# Prepend a 1 to the results.
               8b e# Convert to integer, using base 8 conversion.

Unary to Brainfuck, 18 bytes

ri8b"><+-.,[]"f=1>

Try it online.

How it works

r                  e# Read a whitespace separated token from STDIN.
 i                 e# Interpret as integer.
  8b               e# Convert to array using base 8 conversion.
              f    e# For each digit:
    "><+-.,[]" =   e#     Select the corresponding character from the string.
                1> e# Discard the first character.
\$\endgroup\$
2
\$\begingroup\$

Bash + coreutils, 39 + 47 = 86

b2u.sh:

dc -e8i1`tr '<>+-.,[]' 0-7`p|tr -dc 0-9

u2b.sh:

dc -e8o?p|tr -dc 0-9|tr 0-7 '<>+-.,[]'|cut -c2-

Test output:

$ echo "++++++[>++++++++++++<-]>.>++++++++++[>++++++++++<-]>+.+++++++..+++.>++++[>+++++++++++<-]>.<+++[>----<-]>.<<<<<+++[>+++++<-]>.>>.+++.------.--------.>>+." | ./b2u.sh
239234206933197750788456456928845900180965531636435002144714670872282710109774487453364223333807054152602699434658684117337034763550216789 
$ echo 239234206933197750788456456928845900180965531636435002144714670872282710109774487453364223333807054152602699434658684117337034763550216789 | ./u2b.sh
++++++[>++++++++++++<-[>.>++++++++++[>++++++++++<-[>+.+++++++..+++.>++++[>+++++++++++<-[>.<+++[>----<-[>.<<<<<+++[>+++++<-[>.>>.+++.------.--------.>>+.
$
\$\endgroup\$
1
  • 1
    \$\begingroup\$ tr -dc 0-9 (and in code golf you can assume that ? is fine unescaped) \$\endgroup\$
    – izabera
    Jul 7, 2015 at 17:34
1
\$\begingroup\$

Japt, 13 + 13 = 26 bytes

Brainfuck to Unary

i< n"><+-.,[]

Try it!

Explanation:

i<               :Insert a "<" at the start of the string (representing 1)
   n             :Convert string to decimal by interpreting as:
    "><+-.,[]    : A base 8 number represented by the 8 characters of BF

Unary to Brainfuck

s"><+-.,[]" Å

Try it!

Explanation:

s                :Convert decimal to string representation of:
 "><+-.,[]"      : Base 8 using the BF characters to represent the 8 digits
            Å    :Remove the extra "<" at the front

Notes

I can't find the meta post, but if my memory serves correctly answers are allowed to limit I/O to numbers that their language can support, as long as they implement an algorithm that would work if the language starts supporting larger numbers. That is the case here, Japt's ability to treat a string as "base n using these n characters for the digits" can only use the number data type on the other side of the operation, and thus the test cases won't actually run successfully; the output of the first program and the input of the second program will coerce the number to one which can be represented as a number, rather than using the actual number. For numbers which can be perfectly represented by Japt's number data type these programs will work as desired, and if the number data type changes to support larger numbers then these programs will start supporting those numbers as well.

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

05AB1E, 33 (17 + 16) bytes

Brainfuck to Unary-length:

"><+-.,[]"sSk1š8β

Try it online or verify all test cases.

Explanation:

"><+-.,[]"           # Push string "><+-.,[]"
          s          # Swap to take the (implicit) input
           S         # Convert it to a list of characters
            k        # Check for each the index in the string
             1š      # Prepend a 1 to the list of indices
               8β    # Convert the list to Base-8 (and output implicitly)

Unary-length to Brainfuck

8в¦"><+-.,[]"sèJ

Try it online or verify all test cases.

Explanation:

8в                  # Convert the (implicit) input-list from Base-8 to Base-10
  ¦                 # Remove the first 1
   "><+-.,[]"       # Push string "><+-.,[]"
             s      # Swap the list and string on the stack
              è     # Index each integer into this string
               J    # Join everything together (and output implicitly)
\$\endgroup\$
0
\$\begingroup\$

Dart, 77 + 142 = 219 bytes

f(s)=>BigInt.parse('1'+s.split('').map('><+-.,[]'.indexOf).join(''),radix:8);

F(n)=>BigInt.parse(n).toRadixString(8).split('').map((s)=>'><+-.,[]'.substring(int.parse(s),int.parse(s)+1)).join('').toString().substring(1);

Try it online!

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

C (gcc), 254 bytes

#include"gmp.h"
f(i,o)char*i,*o;{mpz_t l;char*c="><+-.,[]";if(*i>47&*i<58)for(mpz_init_set_str(l,i,0),mpz_get_str(o,8,l);*o;*o++=o[1]?c[o[1]-48]:0);else for(mpz_init_set_si(l,1);mpz_get_str(o,10,l),*i;mpz_mul_si(l,l,8),mpz_add_ui(l,l,strchr(c,*i++)-c));}

Try it online!

Determines which direction to go based on input (i), stores the result in the passed buffer (o). Note that some compilers allow saving of 4 bytes based on the implementation-defined ordering of the o++. In these cases the provided solution will truncate an extra character off of the Unary->BF conversion and the o[1]'s can all be replaced by *o to recover behaviour.

\$\endgroup\$
5
  • 1
    \$\begingroup\$ The language should be "C (gcc) + GMP" here \$\endgroup\$
    – ASCII-only
    May 25, 2019 at 1:55
  • \$\begingroup\$ Also, is this shorter as one program than as 2? And I'd also recommend putting #include <string.h> in the footer instead of the header to show it works without that import. Also wouldn't C++ be shorter because of its operator overloading? :P \$\endgroup\$
    – ASCII-only
    May 25, 2019 at 2:48
  • \$\begingroup\$ Also not important, but I'd change every si to ui maybe \$\endgroup\$
    – ASCII-only
    May 25, 2019 at 2:54
  • \$\begingroup\$ *i>47&*i<58 -> *i%48<10? \$\endgroup\$
    – ASCII-only
    May 25, 2019 at 3:03
  • \$\begingroup\$ also mpz_init_set_str -> mpz_set_str \$\endgroup\$
    – ASCII-only
    May 25, 2019 at 3:13
0
\$\begingroup\$

Thunno 2, 17 + 15 = 32 bytes

"><+-.,[]"$€Ȯ1Ƥ8ḋ

Try it online! (Brainfuck -> Unary)

8Bḣ"><+-.,[]"si

Try it online! (Unary -> Brainfuck)

Explanation

"><+-.,[]"$€Ȯ1Ƥ8ḋ  # Implicit input
"><+-.,[]"         # Push the string
          $€Ȯ      # Index of each character in the input
             1Ƥ    # Prepend a 1
               8ḋ  # Convert from base 8
                   # Implicit output
8Bḣ"><+-.,[]"si  # Implicit input
8B               # Convert to base 8
  ḣ              # Remove the leading 1
   "><+-.,[]"si  # Index into the string
                 # Implicit output
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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