Unary, \$8.71412 \times 10^{420}\$ bytes, score 1
…Obviously, I will not be posting the full code here. Or anywhere. It is 8714123138855978699360164050596229281826476597589481446286633953212548084711579903126530450262195558539773177853879595103615733973606156395440197295886222547053888870061749608029900423204725572303716359786086331805843402197818088474380787864934634643574154692137242860845559867854533001295180659830847166956220859057004333374262038608140098168588215568980939574816997785951406147471781245309306817118354877602371834815751
bytes long. That's a lot of bytes.
Handles all characters as input but 0
, which is the only character that appears in the output. A rare case where Unary does not trivialize a challenge like this? I've had my eyes set on a non-trivial score 1 answer for a while, and Unary is a perfect candidate.
Here is the script I used to generate the program size, and here is the corresponding Brainfuck program (formatted with newlines for readability):
+>,>>>>>>+++>>>+<<<<<<<<<<[>>[-]+>[-]<<[>>>[-]>[-]+>[-]<<<<<+[>-<[>>+<<-]]>>[<<+
>>-]<[>>>-<<<<[-],[++>-<[>>+<<-]]>>[<<+>>-]<[<<->>-]]<-[>>+<<-]]>>[<<+>>-]<[>>[-
]+>[-]>[-]<<<<-]>>>>>[>>+[>[<-]<[->+<]>]>[->[>]>[>]+>+[<]<[<]>]+[<[>-]>[-<+>]<]<
<<<[<+>->>>>+[>[<-]<[->+<]>]>[>]+[<]+[<[>-]>[-<+>]<]<<<<]<[->+<]>[-<+>>-<]>[-<+>
]<<[->>+<+<]>>[-<<+>>>-<]>[-<+>]<<<[->>>+<+<<]>>>>-]+++<<<<<<-<]>>>>>>>>+[>[<-]<
[->+<]>]>[+++++++++++++++++++++++++++++++++++++++++++++++.>]
Assumes an interpreter with dynamic memory (right-infinite) and EOF = 0 (or 0x00 manually appended to the input).
Unfortunately, even this program is obscene, since we do still have 8-bit cells. We store the unary output program as a series of 1s on the tape, which grows exponential with the input size. As a proof of concept, you can Try it online! with input 0x02. This outputs some debug information to show that the corresponding program in Unary is 1 010 010 100 000
(corresponding to ++.>
), which corresponds to the 5,280 0s which follow that debug information.
Explanation
Here is my fully commented corresponding Brainfuck program:
[-][
ASSUMES:
- Dynamic Memory Tape (Right-Infinite)
- EOF = 0x00 and/or null terminated input
for reference, the Unary bijection:
| > corresponds to 000
| + corresponds to 010
| . corresponds to 100
our desired output is Nx'0' characters, where
| + + ... + . > + + ... + . > ...
| N = 1 010 010 ... 010 100 000 010 010 ... 010 100 000 ...
| {ord n.0 times} {ord n.1 times}
assuming dynamic tape memory (but finite cell size), since we need only to output
in unary, we may store N as a unary integer in tape size
our bit to unary algorithm can be expressed on a high level:
| for each bit:
| n *= 2
| if bit:
| n++
| end if
| end for
we need to be able to stream bits from the input not proportional to the input size
that is, there is a large, variable number of bits corresponding to each , input
we will try to process this character-by-character instead of reading all the input
and generating the corresponding bit string, and iterating over that afterwords, so
as to hopefully avoid weird tape layouts
assuming some procedure for emit, our algorithm works as:
| emit 1
| for each input character C:
| while C is not 0:
| emit 010
| C -= 1
| end while
| emit 100
| emit 000
| end for
| for each bit in unary representation, output '0'
however, obviously, we do not procedure calls available to us. ideally, we linearize
our loop to be in terms of processing emitted bits:
| C = input
| loop:
| if C == 0:
| emit_bits = 1 0 0
| elsif C == 255:
| emit_bits = 0 0 0
| C = input
| if C is EOF:
| break
| end if
| C = C + 1
| else:
| emit_bits = 0 1 0
| end if
| times 3:
| double bitstring
| if cur_bit:
| increment bitstring
| end if
| move_bit_right
| end times
| C -= 1
| end loop
we can abuse the fact our input is guaranteed to be printable ascii and use 255 (-1)
as a state value to get an extra 3 bits per input character
as for implementing this algorithm, our tape layout will be:
0: L (looping counter for break)
1: C (input character)
2: t0 (temp)
3: t1 (temp)
4: e0 (emit bit)
5: e1 (emit bit)
6: e2 (emit bit)
7: t3 (3 counter)
8: 0 (0 padding)
9: 0 (0 sentinel)
10: 1 (start of unary representation)
]
real code starts here
+ set L
>, get input
>> move past temps
>>> move past e0 e1 e2
> +++ set counter
> > two 0s of padding
> + emit 1
debug initial unary representation
debug++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------
>debug++++++++++++++++++++++++++++++++.--------------------------------<
L < << <<< <<< < return to looping counter
[
if else algorithm with (C t0 t1) tape layout
C>
temp0> [-]+ temp1> [-] C<< [
IF C NOT ZERO
>>
>[-] e0 = 0
>[-]+ e1 = 1
>[-] e2 = 0
<<< <<
(temp0 temp1) = (1 0) at this point always so we can reuse them
as long as they are that way to end with
recall our if else algorithm requires t0 to be 1 initially anyway
C+ test for c being 255 that is negative 1
C[ {empty else} temp0> - C< [temp1>>+C<<-]]
temp1>>[C<<+temp1>>-]
temp0<[
{if 255 sentinel}
e1>>>- e1 = 0
debug++++++++++.----------
C<<<<[-]
input ,
here is where we need to detect EOF and set L accordingly
we can use the same fact to reuse t0 t1 in our if statement
C[
++ increment C
temp0>-
C<[temp1>>+C<<-]
]
temp1>>[C<<+temp1>>-]
temp0<[
on EOF break by setting L to 0
L<<-
temp0>>
temp0-]
+ restore temp0
C<
temp0>
temp0-]
C<
restoration
C-
temp0>+
<
temp0> - C< [temp1>> + C<< -] ] temp1>> [C<< +temp1>> -] temp0< [
IF C ZERO
temp0
> temp1
>[-]+ e0 = 1
>[-] e1 = 0
>[-] e2 = 0
<<<<<<
temp0>>
temp0-]
debug contents of (e0 e1 e2)
>> debug++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------
> debug++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------
> debug++++++++++++++++++++++++++++++++++++++++++++++++.------------------------------------------------
<<<< temp0 debug++++++++++++++++++++++++++++++++.--------------------------------
>> e0
ADVANCE THE TAPE
>>>
[
>> seek to 0 sentinel
+[>[<-]<[->+<]>]> seek right to first 1 in tape
doubling loop
[
- erase current 1
>[>] navigate to right border 0
>[>] go past rightmost 1 on copy tape to second right border 0
+>+ add two entries
[<] go back to first right border 0
<[<]> navigate to start of 1 tape
]
tape to start was e0 e1 e2 1 0 {a0 a1 a2 etc}
tape to end is e0 e1 e2 1 0 {0 0 0 etc} 0 {a0 a0 a1 a1 a2 a2 etc}
+[<[>-]>[-<+>]<]< seek left to 1 sentinel
<<< seek back to e0
APPEND A ONE IF E0 IS NONZERO
[
<+> save to temp0
- zero out to close loop
append a 1
>>>> seek to 0 sentinel
+[>[<-]<[->+<]>]> seek to first 1 in tape
[>] seek to end
+ append 1
[<] seek to start
+[<[>-]>[-<+>]<]< seek to count sentinel
<<< seek back to e0
]
<[->+<] add temp0 to e0 (either computes 0 plus 1 or 1 plus 0 so its idempotent)
> focus back on e0
permute e0 e1 e2 to e1 e2 e0
[-<+>>-<]>[-<+>]<<[->>+<+<] swap{@1 @2}
>>[-<<+>>>-<]>[-<+>]<<<[->>>+<+<<] swap{@2 @3}
> refocus e0
>>> seek to count sentinel
- decrement
]
reset counter +++
END ADVANCE THE TAPE
<<<<<
C<-
L<
]
>>>>>>>> move past last data entry
+[>[<-]<[->+<]>]> skip all 0s to our unary tape
for each one on the unary representation
[
output a 0
+++++++++++++++++++++++++++++++++++++++++++++++.>
]
[:print:]
class (ASCII range 0x20–0x7E)? Thus, it includes the whitespace 0x20, but not the TAB 0x09? \$\endgroup\$