Darshan A S
Posted on August 18, 2024
Previously, we've seen how to construct QuineRelays by leveraging the existence of Introns. Now, prepare to have your minds blown once again as we unlock the next level of Quine wizardry: MultiQuines.
We start by saying what a Bi-Quine (or more generally a MultiQuine) is. To begin, here is what it is not: a Bi-Quine is not a program that prints a second program, which in turn prints the first again (actually, it's that, but things are a bit more subtle). That would just be a QuineRelay and it is too easy to do (we have proved the existence of such, using Introns).
A Bi-Quine is a very interesting kind of program: when run normally, it is a Quine. But if it is called with a particular command line argument, it will print a different program, its "sibling". Its sibling is also a Quine, but in a different programming language, so its sibling prints its own source code when run normally. But when run with a particular command line argument, the sibling prints the source code of the original program. So in effect, a Bi-Quine is a set of two programs each of which is able to print either of the two.
Essentially, MultiQuines are set of n programs, in n different languages such that:
- Each program is a valid Quine and outputs itself when executed with no input.
- Each program can output the source of any other program in the set, according to the command line argument it is passed. Think Fully-Connected-Graph here.
All MultiQuines are QuineRelays, but all QuineRelays are not MultiQuines.
Note that cheating is not allowed: the command line arguments must not be too long — passing the full source of a program is considered cheating.
5th-order MultiQuine (PentaQuine)
Here's a 5th-order MultiQuine made up of Python, Perl, F#, newLISP, and C.
GitHub
25th-order MultiQuine
A MultiQuine consisting of 25 languages, from Ruby, Octave, CoffeScript, JavaScript, Python and 20 more.
GitHub
One of the characters in Six Degrees of Separation, a popular play and film by an American playwright John Guare states:
I read somewhere that everybody on this planet is separated by only six other people. Six degrees of separation between us and everyone else on this planet. The President of the United States, a gondolier in Venice, just fill in the names. I find it extremely comforting that we're so close. I also find it like Chinese water torture, that we're so close because you have to find the right six people to make the right connection... I am bound, you are bound, to everyone on this planet by a trail of six people.
This phenomenon is also called The small world theory. Facebook's data team released two papers in November 2011 which document that amongst all Facebook users at the time of research (721 million users with 69 billion friendship links) there was an average distance of 4.74
It had come down from 5.28 in 2008. And by 2016, the separation distance was down to 4.57
Similarly, MultiQuines are a special case of the small world theory. A world where a population of self-sustainable Quines are linked to each other by just one degree of separation.
Writing your own MultiQuine
The secret behind MultiQuines lies in the clever use of, you guessed it, Introns - those non-coding regions we explored earlier. Each MultiQuine program contains n
data sets: one for its own code and one for each of its siblings. When executed with a specific argument, a MultiQuine uses the corresponding data set to generate the source code of the requested sibling. It then uses its own data sets to replicate the introns within the sibling's code, ensuring the cycle of transformation continues seamlessly.
Let's take the outline of the QuineRelay we build last time:
Python:
d1 = "code part of Python"
d2 = "code part of JavaScript"
print('d1 =', repr(d1)); print('d2 =', repr(d2)); print(d2)
JavaScript:
d1 = "code part of Python"
d2 = "code part of JavaScript"
console.log(`d1 = ${JSON.stringify(d1)}`); console.log(`d2 = ${JSON.stringify(d2)}`); console.log(d1 + '');
Now, we want to read the command line argument and decide to print d1
or d2
In Python, sys.argv
is the array that holds these.
If it says "js" we output the JavaScript MultiQuine
If it says "py" or empty we output the same Python MultiQuine back.
Note. We are choosing to ignore any other values and just output the same Python Quine
Make sure to import sys
and also check if sys.argv
does have an argument passed in.
Python:
d1 = "code part of Python"
d2 = "code part of JavaScript"
import sys; print('d1 =', repr(d1)); print('d2 =', repr(d2)); print(d2 if len(sys.argv) > 1 and sys.argv[1] == 'js' else d1)
In Javascript, process.argv
is the array that holds these.
Use the same conditions to modify the JavaScript MultiQuine as below:
JavaScript:
d1 = "code part of Python"
d2 = "code part of JavaScript"
console.log(`d1 = ${JSON.stringify(d1)}`); console.log(`d2 = ${JSON.stringify(d2)}`); console.log(process.argv.length > 2 && process.argv[2] == 'py' ? d1 : d2);
Now, all we have to do is, copy the final code part to each other's d1
and d2
variables:
Python: Try it online! (Play with argument py
or js
)
d1 = "import sys; print('d1 =', repr(d1)); print('d2 =', repr(d2)); print(d2 if len(sys.argv) > 1 and sys.argv[1] == 'js' else d1)"
d2 = "console.log(`d1 = ${JSON.stringify(d1)}`); console.log(`d2 = ${JSON.stringify(d2)}`); console.log(process.argv.length > 2 && process.argv[2] == 'py' ? d1 : d2);"
import sys; print('d1 =', repr(d1)); print('d2 =', repr(d2)); print(d2 if len(sys.argv) > 1 and sys.argv[1] == 'js' else d1)
JavaScript: Try it online! (Play with argument py
or js
)
d1 = "import sys; print('d1 =', repr(d1)); print('d2 =', repr(d2)); print(d2 if len(sys.argv) > 1 and sys.argv[1] == 'js' else d1)"
d2 = "console.log(`d1 = ${JSON.stringify(d1)}`); console.log(`d2 = ${JSON.stringify(d2)}`); console.log(process.argv.length > 2 && process.argv[2] == 'py' ? d1 : d2);"
console.log(`d1 = ${JSON.stringify(d1)}`); console.log(`d2 = ${JSON.stringify(d2)}`); console.log(process.argv.length > 2 && process.argv[2] == 'py' ? d1 : d2);
There you go. It's a valid 2nd-order MultiQuine (Bi-Quine)
If you wanna play around more, David Madore has written a C/Perl Bi-Quine. For fun, only the C version is given out; if you want the Perl version you will have to run the program with the magic word: xyzzy as the argument. In the C version, c_data
is the main data set and perl_data
is an intron; in the Perl version, of course, things are reversed.
One peculiar thing, you might notice in the above C/Perl example, is that the data section is encoded as an array of numbers. This is a common trick to avoid having to deal with nested quoting problem, while writing Quines. In fact if one wanted to, each data set could use a different coding mechanism, or even an encryption mechanism, although you'd need a key to decrypt it in the code section.
Stay tuned for the next post on Radiation-Hardened-Quine!
Sources and references:
- Quines (self-replicating programs), blog by David Madore.
- Pentaquine, GitHub repo by Rijnard van Tonder.
- Quine Chameleon, GitHub repo by Lu Wang.
- The small world theory, wiki page.
- Six Degrees of Separation - Quote, Youtube video clip from the movie Six Degrees of Separation.
- Quine, Intron, QuineRelay, MultiQuine, set of four self-referencing blogs that link to each other in a fully-connected graph.
Posted on August 18, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.