33
\$\begingroup\$

Challenge

Make two programs, A and B, which are both cat programs in the same language. When concatenated, AB (also in the same language) should be a quine.

For example, suppose hello and world are both cat programs in language XYZ. If helloworld is a quine in said language, then your solution is valid.

For those of you unfamiliar with cats and quines, a cat program is one that prints exactly what was given to it via stdin and a quine is a program that prints its own source code.

Scoring and Rules

  • The total byte count of the concatenated AB program is your score. As this is code golf, the lowest score wins.
  • Standard loopholes are forbidden
  • Input must be taken from stdin and output must go to stdout.
  • The cat programs do not need to take arguments; they only need to copy stdin to stdout.
  • The quine should work when the program is given no input, but does not have to work correctly (but may) for other inputs.
  • The quine does not need to terminate, provided it prints exactly its source code once, nothing more.
  • The quine needs to be at least one byte long.
  • A and B can be the same program.
  • BA does not need to be a quine or even a valid program.
\$\endgroup\$
6
  • \$\begingroup\$ I don't think you should have the A and B can be same program rule \$\endgroup\$ Commented Jun 1, 2018 at 16:42
  • 2
    \$\begingroup\$ @MuhammadSalman My original idea was to have a cat program be doubled to turn into a quine. I just wanted to open the door to easier solutions because I wasn't entirely sure it was possible. Looks like I was wrong on both counts, but I'm fine with that. \$\endgroup\$
    – Beefster
    Commented Jun 1, 2018 at 16:46
  • 3
    \$\begingroup\$ You should probably add that AB must be non-empty, since lots of languages have a 0-byte cat allowing for a 0-byte quine. \$\endgroup\$
    – DJMcMayhem
    Commented Jun 1, 2018 at 16:50
  • 12
    \$\begingroup\$ @DJMcMayhem a 0-byte quine wouldn't be a valid quine though. \$\endgroup\$
    – Nissa
    Commented Jun 1, 2018 at 16:52
  • 4
    \$\begingroup\$ What is a cat program? \$\endgroup\$
    – Pedro A
    Commented Jun 1, 2018 at 22:21

17 Answers 17

37
\$\begingroup\$

V, 2 + 2 == 4 bytes

2i2i

Try the quine!

Try the cat!

A is 2i

B is also 2i

How does it work?

First off, some explanations of how V works. One notable thing that makes this answer possible is that in V, the empty program is a cat program. This isn't a special case, it's inherent to how V operates. On startup, all input is loaded into a "buffer", each command modifies the buffer in someway, and then when the program is done, the buffer is implicitly printed. This means that any string of NOPs is also a cat program.

The i command means enter insert mode, which means every character following an i will be added into the buffer. With a number preceding it, that text will be duplicated n times.

This means that for the cat program, nothing will be added to the buffer, and it will be printed as it was read. In other words:

        " (Implicitly) Load all input
2       " 2 times,
 i      " Insert the following text into the buffer...
        " (nothing)
        " (Implicitly) Print the buffer

But for the quine, there is text after the i:

2       " 2 times,
 i      " Insert the following text into the buffer...
  2i    "   "2i"
        " (Implicitly) Print the buffer

Cheeky non-answer

V, 0 bytes

Try it online!

A is the empty program.

B is also the empty program.

:P

\$\endgroup\$
1
  • 23
    \$\begingroup\$ Every other language: Oy, we've hit a dead end!. V: *posts standard quine*. \$\endgroup\$ Commented Jun 1, 2018 at 20:15
13
\$\begingroup\$

Ruby, 71 bytes

2;puts (<<2*2+?2)[/.+2/m]||$<.read
2;puts (<<2*2+?2)[/.+2/m]||$<.read
2

Can be broken up into cats as follows:

2;puts (<<2*2+?2)[/.+2/m]||$<.read
2

and

;puts (<<2*2+?2)[/.+2/m]||$<.read
2

The two cats are identical except for the leading 2, which is a no-op in all three programs. The <<2 is a herestring, meaning that everything starting on the next line until a terminating 2 on its own line is a string, which we concatenate to itself (*2) and append a trailing 2. In the cats the herestring is well-formed but empty, so the regular expression won't match it and we'll fall through to the $<.read expression and output STDOUT. Once we concat the cats, though, the string won't terminate until the third line, so the regex matches and we short-circuit and output the quine.

\$\endgroup\$
13
\$\begingroup\$

C# (Visual C# Compiler), 551 bytes

A: 95 bytes

class A{public static int i=2;static void Main(string[]args){System.Console.Write(args[0]);}}//

Try it online!

B: 438 + 18 bytes

class A{public static int i=0;}
class B{static void Main(string[]args){if(A.i<1){System.Console.Write(args[0]);return;}var a=@"class A{{public static int i=2;static void Main(string[]args){{System.Console.Write(args[0]);}}}}//class A{{public static int i=0;}}
class B{{static void Main(string[]args){{if(A.i<1){{System.Console.Write(args[0]);return;}}var a=@{0}{1}{0};System.Console.Write(a,'{0}',a);}}}}";System.Console.Write(a,'"',a);}}

Try it online!

A+B: 533 + 18 bytes

class A{public static int i=2;static void Main(string[]args){System.Console.Write(args[0]);}}//class A{public static int i=0;}
class B{static void Main(string[]args){if(A.i<1){System.Console.Write(args[0]);return;}var a=@"class A{{public static int i=2;static void Main(string[]args){{System.Console.Write(args[0]);}}}}//class A{{public static int i=0;}}
class B{{static void Main(string[]args){{if(A.i<1){{System.Console.Write(args[0]);return;}}var a=@{0}{1}{0};System.Console.Write(a,'{0}',a);}}}}";System.Console.Write(a,'"',a);}}

Try it online!

A and B take input as a command-line argument. A+B ignores any input. 18 bytes on B and A+B are added for the /p:StartupObject=B option sent to MSBuild. It's only strictly necessary on A+B, but it seemed like cheating to not also have it in B. This way, the compiler flags for A+B are the compiler flags for A (none) plus the compiler flags for B.

Explanation

Program A is straightforward. Class A contains an (unused) static variable i initialized to 2, and prints its first argument when run. The // at the end is important for the A+B code, but does nothing in A itself.

Program B is weird in isolation, but essentially the same. It creates a Class A containing a static variable i initialized to 0, and then runs the Main method of Class B, which does the same as Program A because A.i is less than 1, and returns before any of the weird stuff. The newlines aren't necessary here, but are important for A+B.

When combined, the // from Program A comments out the Class A declaration from Program B, but because of the newline Class B is fine, allowing A.i to refer to the 2 value from Program A instead. The Compiler Flag makes the Program run B.Main() since A.Main() also exists. The result is that Program A+B does not output its argument, but instead goes to the following segment of B.Main(), which is basically just the standard C# quine.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ "the cat programs...need to copy stdin to stdout" \$\endgroup\$
    – Jakob
    Commented Jun 2, 2018 at 1:59
11
\$\begingroup\$

Pyth, 29 bytes (5 + 24) 27 bytes (5 + 22)

pz=T0?TzjN*2]"pz=T0?TzjN*2]     # Quine
pz=T0                           # Cat A
     ?TzjN*2]"pz=T0?TzjN*2]     # Cat B

That was fun.
Try the quine here
Try the first cat here
Try the second cat here

Explanations

Cat A
pz=T0
pz       Print the input.
  =T0    (Irrelevant for cat)

Cat B
?TzjN*2]"pz=T0?TzjN*2]
?Tz                      If T (10) is truthy, output the input.
   jN*2]"pz=T0?TzjN*2]   (Irrelevant for cat)

Quine
pz=T0?TzjN*2]"pz=T0?TzjN*2]
pz                            Print the (empty) input (without a newline).
  =T0                         Set T to 0.
     ?Tz                      If T (0) is truthy, output the input.
             "pz=T0?TzjN*2]   Otherwise, get this string...
          *2]                 ... take two copies...
        jN                    ... and join them with a quote.
\$\endgroup\$
0
9
\$\begingroup\$

Haskell, 116 + 20 = 187 175 174 136 bytes

A bunch of bytes saved since Ørjan Johansen showed me interact

Cat 1

g=";main|idmain<-(++\"g=\"++show g++g)=interact idmain|1>0=interact id";main|idmain<-(++"g="++show g++g)=interact id

Try it online!

Cat 2

main|1>0=interact id

Try it online!

Quine

g=";main|idmain<-(++\"g=\"++show g++g)=interact idmain|1>0=interact id";main|idmain<-(++"g="++show g++g)=interact idmain|1>0=interact id

Try it online!


The basic principle at work here is that when we add the second cat to the first we change the name of the function we are interacting with from a to idmain. Since interact id is a cat we want idmain to me a function that returns a quine. The obvious solution would be to use const, however since we can assume the input is empty (++) works as well. From here we find the source code via pretty standard means, we have a variable g that encodes the source and we use a special wrapper to print it in string form and code form. There is a slight exception that we need to put our encoder at the front because we already need to end with interact id. This means an extra g= is not encoded and has to be handled manually. Our next cat is pretty standard except we need to make it valid code when tacked on to the end of the other cat so we need both cats to be instances of pattern guards.

Alternative Strategy, 43 + 105 = 186 148

Cat 1

g="";main|idmain<-(++g++show g)=interact id

Try it online!

Cat 2

main|1>0=interact id where g="g=\"\";main|idmain<-(++g++show g)=interact idmain|1>0=interact id where g="

Try it online!

Quine

g="";main|idmain<-(++g++show g)=interact idmain|1>0=interact id where g="g=\"\";main|idmain<-(++g++show g)=interact idmain|1>0=interact id where g="

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You can shorten that quite a bit by replacing getContents + putStr with interact id. Try it online! (The quine no longer works with non-empty input, which allows using a (++ ...) section for the idmain.) \$\endgroup\$ Commented Jun 2, 2018 at 0:47
  • \$\begingroup\$ @ØrjanJohansen Thanks! I didn't know about interact, I guess that's because I rarely do IO stuff with Haskell. I've editted the post. \$\endgroup\$
    – Wheat Wizard
    Commented Jun 2, 2018 at 1:16
8
\$\begingroup\$

Python 3, 286 bytes

My first Python golf and my first quine! Not very elegant, but it works.

Program A (238 bytes)

from atexit import*;s="from atexit import*;s=%r;register(lambda:print(end='b'in globals()and s%%s or open(0).read()));b=0\nif's'not in vars():print(end=open(0).read())";register(lambda:print(end='b'in globals()and s%s or open(0).read()));

(no trailing newline)

Program B (48 bytes)

b=0
if's'not in vars():print(end=open(0).read())

(no trailing newline)

Try It Online

Acknowledgments

  • -24 bytes thanks to Jo King
  • -82 bytes thanks to Jo King
\$\endgroup\$
4
  • \$\begingroup\$ You can do end=open... and use %r instead of %s to not have to do the newline and quotes \$\endgroup\$
    – Jo King
    Commented Jun 2, 2018 at 1:28
  • \$\begingroup\$ Cool, I switched to %r. Not sure what you mean about the newline though. \$\endgroup\$
    – Jakob
    Commented Jun 2, 2018 at 6:02
  • 1
    \$\begingroup\$ Instead of %s to format a newline in, you can just do the literal \n. You can also use ; to split statements instead of \n (except the if needs to be on its own line). % can be escaped in the string by doing %%. The only argument needed to format the string is the string itself, everything else can be striped \$\endgroup\$
    – Jo King
    Commented Jun 2, 2018 at 7:49
  • 2
    \$\begingroup\$ Program B (and the text for it) can use locals() to save 2 bytes. \$\endgroup\$ Commented Jun 2, 2018 at 14:49
7
\$\begingroup\$

Python 3, 137 78 + 37 = 115 bytes

-22 bytes thanks to Jakque!

Program A:

s='s=%r;exec(s[20:45])#print(end=open(0).read())\nprint(s%%s)';exec(s[20:45])#

Try it online!

And program B:

print(end=open(0).read())
print(s%s)

Try it online!

Make Quine AB!

s='s=%r;exec(s[20:45])#print(end=open(0).read())\nprint(s%%s)';exec(s[20:45])#print(end=open(0).read())
print(s%s)

Try it online!

Only works when input is empty, otherwise it prepends the input to the output.

\$\endgroup\$
5
  • \$\begingroup\$ The double quotes should be single ones. \$\endgroup\$ Commented Jun 2, 2018 at 14:57
  • \$\begingroup\$ Crashing is allowed? \$\endgroup\$
    – Jakob
    Commented Jun 2, 2018 at 23:13
  • \$\begingroup\$ @Jakob The question doesn't say that crashing is not allowed, and typically output to STDERR is ignored \$\endgroup\$
    – Jo King
    Commented Jun 4, 2018 at 5:07
  • \$\begingroup\$ Ok, fair enough. Clever shortcuts! \$\endgroup\$
    – Jakob
    Commented Jun 4, 2018 at 6:55
  • 1
    \$\begingroup\$ -22 bytes : s='s=%r;exec(s[20:45])#print(end=open(0).read())\nprint(s%%s)';exec(s[20:45])#. The idea is to reuse the print(end=open(0).read()) by executing if from a substring of s. You just have to write exec(s[20:45]) (14 bytes instead of 25). As you write it twice you gain 11+11=22 bytes \$\endgroup\$
    – Jakque
    Commented May 19, 2021 at 12:40
6
\$\begingroup\$

C++ (clang), 313 + 102 = 415 bytes

Program A (ends in a newline):

#include<cstdio>
#define Q(x,y)#x,B=#y;x
int c;auto I="#include<cstdio>",A=Q(int main(){if(c)printf("%s\n#define Q(x,y)#x\",\"#y;x\nint c;auto I=\"%s\",A=Q(%s,)\n#ifdef Q\nint n=++c;\n#else\n%s\n%s\n#endif",I,I,A,I,B);else while((c=getchar())>=0)putchar(c);},int c;int main(){while((c=getchar())>=0)putchar(c);})

Program B (does not end in newline):

#ifdef Q
int n=++c;
#else
#include<cstdio>
int c;int main(){while((c=getchar())>=0)putchar(c);}
#endif

Not terribly sneaky, and as usual C++ isn't that great for quining. I won't be surprised if there are ways to shave bytes here and there off the same idea. The one little catch is changing the behavior of something after it's been defined, and a dynamic variable initializer with side effect does the trick. (Can this even be done in C without compiler extensions?)

Try it online: A, B, AB

(The only portability concern I'm aware of is that the program assumes <cstdio> puts names both in the global namespace and in std.)

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

Befunge-98 (FBBI), 8 + 15 = 23 bytes

A + B: (only works for no input)

+9*5~,#@#@~,9j>:#,_:@#"

Try it online!

A:

+9*5~,#@

Try it online!

B:

#@~,9j>:#,_:@#"

Try it online!

\$\endgroup\$
2
  • \$\begingroup\$ I was wondering if someone would manage a befunge answer. It's too bad this one isn't 2D :-/ \$\endgroup\$
    – Beefster
    Commented Jun 1, 2018 at 22:44
  • \$\begingroup\$ @Beefster yeah. The problem is it’s pretty hard to do a 2d Quine. Maybe I’ll work on something though \$\endgroup\$ Commented Jun 1, 2018 at 22:45
4
\$\begingroup\$

Attache, 15 + 126 = 141 bytes

A:

AllInput[]|Echo

Try it online!

B:

@{If[_,s.="AllInput[]|Echo@{If[_,s.=%s;;Printf[s,Repr!s],AllInput[]|Echo]es}|Call";;Printf[s,Repr!s],AllInput[]|Echo]es}|Call

Try it online!

A + B:

AllInput[]|Echo@{If[_,s.="AllInput[]|Echo@{If[_,s.=%s;;Printf[s,Repr!s],AllInput[]|Echo]es}|Call";;Printf[s,Repr!s],AllInput[]|Echo]es}|Call

Try it online!

Explanation

Each of the cat program encodes AllInput[]|Echo, which is a simple cat program. B is the main quine phase; alone, it is a vectorized function (through unary @) called with no inputs (being called as |Call). Thus, the first conditional If[_,A,B] executes B, which is simply AllInput[]|Echo.

When A + B is executed, unary @ becomes binary @ due to Echo merging with the lambda:

AllInput[]|Echo@{If[_, ...

Now, this means that the lambda is executed before Echo is. Back to the conditional, this function now has all of STDIN as an argument. So, If[_,A,B] executes A, which is the standard quine framework.

\$\endgroup\$
4
\$\begingroup\$

Stax, 16 + 12 = 28 bytes

Cat 1:

"yi|d|ca34b4lr"y

Run and debug it

"yi|d|ca34b4lr"y Full program, implicit input
"yi|d|ca34b4lr"  Push the string
               y Push raw input
                 Implicit output of top item

Cat 2:

i|d|ca34b4lr

Run and debug it

i|d|ca34b4lr Full program, implicit input
i            Don't parse input (prefix directive)
 |d          Push main stack depth, always zero
   |c        Cancel because top item is falsy, and implicitly print
     a34b4lr Never executed

Quine:

"yi|d|ca34b4lr"yi|d|ca34b4lr

Run and debug it

"yi|d|ca34b4lr"yi|d|ca34b4lr Full program
"yi|d|ca34b4lr"              Push the string
               y             Push input
                i            No-op
                 |d          Push main stack depth, i.e. 2
                   |c        Do nothing because top is truthy
                     a       Get the string to the top
                      34     Push 34 (charcode of ")
                        b    Copy both
                         4l  Listify top 4
                           r Reverse
                             Implicit output
\$\endgroup\$
3
\$\begingroup\$

Cat 1 :

Lua, 41 bytes

a=io.read;while a(0)do io.write(a(1))end;

Try it online!


Cat 2 :

Lua, 70 bytes

if a then io.input(arg[0])end;a=io.read;while a(0)do io.write(a(1))end

Try it online!


Quine :

Lua, 111 bytes

a=io.read;while a(0)do io.write(a(1))end
if a then io.input(arg[0])end;a=io.read;while a(0)do io.write(a(1))end

Try it online!

io.input(arg[0]) in Cat 2 sets the current file as standard input, and as a result, the cat prints the source code

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Welcome to PPCG \$\endgroup\$ Commented Jun 3, 2018 at 15:10
  • \$\begingroup\$ You can save one byte in cat 1 by removing the last semi-colon ? \$\endgroup\$ Commented Jun 3, 2018 at 15:45
  • 2
    \$\begingroup\$ Unfortunately, reading the current file is a standard loophole. But nice try anyway. \$\endgroup\$
    – Beefster
    Commented Jun 4, 2018 at 17:06
2
\$\begingroup\$

><>, 12 +3 = 15 bytes

Program A:

<oi

Try it online!

And Program B:

>i!o|o<}*d3'

Try it online!

Make Program AB:

<oi>i!o|o<}*d3'

Try it online!

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

Python 3.8 (pre-release), 26 + 71 = 97 bytes

Cat 1 (26 bytes):


id=print(open(0).read());

Try it online!

Cat 2 (71 bytes):

exec(s:='id=print(open(0).read()if id else s[:23]+");exec(s:=%r)"%s)')

Try it online!

Quine = Cat 1 + Cat 2 (97 bytes):


id=print(open(0).read());exec(s:='id=print(open(0).read()if id else s[:23]+");exec(s:=%r)"%s)')

Try it online!

How it works:

Cat 1:

  • open(0).read reads the STDIN
  • id=print(...) prints it and returns None which will be stored into id (remember it for later)

Cat 2:

  • <instructions for cat> if id else <other instuctions> will execute <instructions for cat> because id is a built-in and is evaluated as True.

Quine:

  • Because of Cat 1, id is now equal to None and <instructions for cat> if id else <other instuctions> will now execute <other instructions>
  • Because both our program have the same structure for cat, we print the cat part of Cat 2 for quining cat1 : s[:23]+");
  • Then the programm is just a variant of exec(s:='print("exec(s:=%r)"%s)'), a quine in Pyhton 3.8+

Notes:

Both my cat programs add a trailing new line at the end. I assume that it was accepted since it wasn't frormally banned and since most of the cat challenges accept it.

Otherwise I have a 103 bytes solution which doesn't add trailing newlines:

id=print(end=open(0).read());                                                                           #Cat1
                             exec(s:='id=print(end=open(0).read()if id else s[:27]+");exec(s:=%r)"%s)') #Cat2
id=print(end=open(0).read());exec(s:='id=print(end=open(0).read()if id else s[:27]+");exec(s:=%r)"%s)') #Quine

Try it online!

Bonus:

Here is a solution in 116 bytes which accepts not-empty input for the quine program:

while 1:id or print(input());id=0;                                                                                   #Cat1
                                  exec(s:='id or~input(s[36:]+"exec(s:=%r)"%s)\nwhile 1:id or print(input());id=0;') #Cat2
while 1:id or print(input());id=0;exec(s:='id or~input(s[36:]+"exec(s:=%r)"%s)\nwhile 1:id or print(input());id=0;') #Quine

Try it online!

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

Klein 0XX, 6 + 7 = 13 bytes

Works in 000, 001, 010 and 011.

Note that these answers all contain the unprintible byte 17, or "device control 1".

Cat 1

<:2*@

Try it online!

The < redirects the pointer off the left. It appears on the right at @ and halts immediately spilling its input as output.

Cat 2

@*2:<"

Try it online!

It immediately encounters @ and halts with no further execution. It outputs the input with no changes.

Quine

<:2*@@*2:<"

Try it online!

Like cat 1 this first encounters < which sends it off the left and onto the right. But this time on the right we encounter " which puts us into string mode. This causes us to push the entire program (minus the quote) to the stack backwards. In order to make things easier for myself this part of the program is a palindrome, so we don't have to reverse it. Now we just need to add the " at the end. The program begins with a , which is chosen because multiplying it by 2 gives us ". So we duplicate and multiply by two to make the " and exit at @.

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

Vyxal ajṠ, 13 + 1 = 14 bytes

A: (Try it Online!)

`:qpw$$`:qpw$

B: (Try it Online!)

$

A + B: (Try it Online!)

`:qpw$$`:qpw$$

Explained

`:qpw$$`:qp      # Modified standard Vyxal quine
           w     # Wrap quine in array, so join on newline works
            $    # Swap the quine and the input

$                # Swap
                 #   If the program is standalone, this does nothing
                 #   If concatenated, this undoes the previous swap and puts the quine at the top
  • a: Push entire input split on newlines to the top of the stack
  • j: Join top of stack on newlines and output (undo split from a)
  • : Treat all inputs as strings, so proper output is given for inputs like [] and ""
\$\endgroup\$
1
0
\$\begingroup\$

JavaScript (Node.js), 199 bytes


a=()=>console.log(require('fs').readFileSync(0)+'');a();var a=a||0;b=()=>console.log(require('fs').readFileSync(0)+'');!a?b():c=()=>console.log(`a=${a};a();var a=a||0;b=${b};!a?b():c=${c},c()`),c()

Try it online!

Cat A, 57 bytes


a=()=>console.log(require('fs').readFileSync(0)+'');a();

Try it online!

Cat B, 142 bytes

var a=a||0;b=()=>console.log(require('fs').readFileSync(0)+'');!a?b():c=()=>console.log(`a=${a};a();var a=a||0;b=${b};!a?b():c=${c},c()`),c()

Try it online!

\$\endgroup\$

Your Answer

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

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