# Compress and decompress

Your task is to make a bidirectional compressor. It takes a list of bytes as input and returns a list of bytes as output. It has the property than whenever it is iterated twice it returns the original input.

In other words, if f is your program as a function, then f(f(x))=x for all x.

Now, since this is a "compressor", it should actually try to compress it's input. Of course, it can't compress every input. It should however be able to compress your program's source code. That is, if s is your program's source code, then len(f(s))<len(s).

To recap, your program acts as a function that takes a list of bytes as input and outputs a list of bytes as output. Let's call your program f and it's source code s. Your program must satisfy both:

• f(f(x)) = x for all x
• len(f(s)) < len(s)

Remember that the empty list is a list, and thus your code should be able to handle it.

Normal quine rules apply, so no introspection is allowed (opening the file that contains your source code, etc.).

Shortest code wins!

• Why does this seem easier than it actually is? Jan 18 at 14:43
• Is it introspection to use JavaScript's built-in method for handling functions as strings? Jan 18 at 14:47
• @ThisFieldIsRequired Yes Jan 18 at 14:53
• i wonder how long the unary answer is Jan 19 at 13:35
• @ThisFieldIsRequired because he didn't say you can't use builtins for the compression :) Feb 6 at 18:30

# JavaScript (ES6), 26 bytes

-1 thanks to @thejonymyster

-7 thanks to @Arnauld

-4 thanks to @l4m2

x=>x.replace(/-2|1/,z=>~z)


Swaps the first instance of -2 and 1, saving 1 byte when the program compresses itself. Takes the list of bytes as input, as a string.

This is quite the community effort now :p

• 28
– l4m2
Jan 18 at 15:20
• 26
– l4m2
Jan 18 at 15:21
• 25
– l4m2
Jan 18 at 15:22
• /-?1/,z=>-z fail if input contain --1 and was deprecated
– l4m2
Jan 18 at 15:27
• @l4m2 Good point, that's the same thing that messed up my y" earlier Jan 18 at 15:41

# R, 102 59 bytes

function(x)if(nchar(x)%%2,sub("#$","",x),sub("$","#",x))#


Try it online!

f() removes any trailing # comment character if there are an odd number of characters in the input (as in myprog), or adds one if there are an even number.

(Removing the first f would also work, and is 1-byte shorter, but it's less satisfying as the 'compressed' function is no longer a runnable compression function itself...)

# C (gcc), 49 bytes

A;f(char*s){s[-1]=65;A=s[A=*s==65]-59?s:s-1+A*2;}


Try it online!

Surculose Sputum's idea

# C (gcc), 5554 52 bytes

A;f(char*s){A=*s-(s[-1]=65)?s-1:*++s-65?s:f(s+1)-2;}


Try it online!

Thank gastropner for -2 bytes

Requires s[-1] be writable

• 52 bytes Jan 18 at 18:45

# Zsh, 35 bytes

printf %s ${1/(#m)(-3|2)/$[~MATCH]}

Attempt This Online!

Previous version independently discovered to l4m2's golf to Redwolf's answer, goddamn ninjas! (now just a lazy port :/)

Swaps the first instance of -3 for 2 and vice versa.

# APL (Dyalog Unicode), 37 bytes

'¯2|1'⎕R{⍕83⎕Dr~11⎕Dr⍎⍵.Match}⍠'ML'1⊢


Try it online!

Same thing as everyone else here has done replaces ¯1 with 1 and vice versa

and it works this time for sure :D

• This doesn't work. applying the function to the compressed string needs to return the original string. ¯1 => 1 => ¯1 Jan 19 at 13:32
• fixed it @thejonymyster Jan 19 at 17:39
• fails for ¯¯1 (¯¯1 => ¯1 => 1) Jan 19 at 17:59

# Retina 0.8.2, 18 bytes

1-1|(2)
$#1$*-$.&  Try it online! Explanation: Port of @RedwolfPrograms' JavaScript answer. 1  Only replace the first match. -1|(2)  Match -1 or 2. $#1$*-  Output - only if 2 was matched. $.&


Output the length of the match.

f('l':')':l)='!':l;f('!':l)="l)"++l;f(x:l)=x:f l;f_=[]


Try it Online!

Switches the first occurrence of l) and !.

# Python 3, 48 bytes

Thanks @AnttiP for pointing out the error in a previous version.

lambda s:['l'+s,s[1:],s][('l'+s[:2]).find('la')]


Try it online!

Takes in a string and compress/decompress it.

• If the input starts with a, prepends l
• If the input starts with la, then remove the first character l.
• Otherwise, returns the input unchanged

('l'+s[:2]).find('la') evaluates to 0, 1, -1 for each of the above case respectively. This is because by prepending l to the first 2 characters, we get l??. If we find la at the 0th index (la?), then the first character of input must be a. If we finds la at the 1st index (lla), then the input must starts with la.

• This fails for input "ll" Jan 19 at 9:06