I was thinking about recursion. The classic example of an elegant recursive algorithm is:
def factorial(n):
if n <= 1:
return 1
else:
return n * factorial(n-1)
Recursive functions call themselves, eventually returning the correct answer.
I thought, why stop at the function level? I want to make it bigger - how about a whole program that calls itself -- and modifies itself? Of course, this would be really impractical in terms of memory, but it sounded like fun.
Big recursion
So, during my lunch at work, I put the following together. (C# 2.0)
If you run the program, it will compute a factorial for you... in a non-standard way.
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine("Enter number:");
string strNum = Console.ReadLine();
int nInput = int.Parse(strNum);
string strLoc = System.Reflection.Assembly.GetExecutingAssembly().Location;
int nReturn = int.Parse(ExecuteAndWait(strLoc, (nInput).ToString()));
Console.WriteLine("The answer is: "+ nReturn);
}
else
{
int nInput = int.Parse(args[0]);
Recurse(nInput);
}
}
private static void Recurse(int nInput)
{
if (nInput <= 1)
Console.Write("1");
else
{
string strLoc = System.Reflection.Assembly.GetExecutingAssembly().Location;
int nReturn = int.Parse(ExecuteAndWait(strLoc, (nInput - 1).ToString()));
Console.Write(nInput * nReturn);
}
}
Although this was fun, it wasn't extreme enough. I wanted something even bigger, more impractical, and more hacky. So, I invented "bigger" recursion. Have fun understanding what this next one does.
Even Bigger Recursion
(Warning: the following code will compile but is not guaranteed to work on your machine. It's extremely fragile, for reasons that will become clear. I used Visual Studio 2005 C# console app to build it.)
Hint 1: The byte 0xE2 is not commonly found in C# executables.
Hint 2: this is my directory listing after I run the program and find the factorial of 5:
Bigger2.exe
Bigger3.exe
Bigger4.exe
Bigger5.exe
BiggerRecursion.exe
BiggerRecursion.pdb
BiggerRecursion.vshost.exe
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using System.IO;
namespace BigRecursion
{
class Program
{
static volatile byte s_byteCurrent = 0xE2;
static volatile byte s_byteBase = 0xE1;
static void Main(string[] args)
{
if (s_byteCurrent == s_byteBase+1)
{
int nInput;
Console.WriteLine("Enter number:");
string strNum = Console.ReadLine();
nInput = int.Parse(strNum);
int nReturn = WriteIt((byte) (nInput + 220));
Console.WriteLine("The answer is: " + nInput * nReturn);
}
else
{
Recurse(s_byteCurrent);
}
}
private static void Recurse(byte byteCurrent)
{
int nCurrent = byteCurrent - 220;
if (nCurrent <= 1)
Console.Write("1");
else
{
int nReturn = WriteIt(byteCurrent);
Console.Write(nCurrent * nReturn);
}
}
private static int WriteIt(byte byteCurrent)
{
int nCurrent = byteCurrent - 220;
byte bNext = (byte) (nCurrent - 1 + 220);
string strLoc = System.Reflection.Assembly.GetExecutingAssembly().Location;
byte[] bExe = File.ReadAllBytes(strLoc);
Debug.Assert(bExe.Length == 0x4000);
bExe[0x1292] = bNext;
string strLoc2 = (Path.GetDirectoryName(strLoc) +
"\\Bigger" + nCurrent.ToString() + ".exe");
File.WriteAllBytes(strLoc2, bExe);
return int.Parse(ExecuteAndWait(strLoc2));
}
private static string ExecuteAndWait(string sFullApplicationPath)
{
string stdout = null;
Process myProcess = new Process();
try
{
myProcess.StartInfo.FileName = sFullApplicationPath;
myProcess.StartInfo.Verb = "Open";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.Start();
stdout = myProcess.StandardOutput.ReadToEnd();
myProcess.WaitForExit();
}
catch (Win32Exception e)
{
if (e.NativeErrorCode == 2) //ERROR_FILE_NOT_FOUND
{
throw new Exception(e.Message + ". Check the path: " +
myProcess.StartInfo.FileName);
}
else if (e.NativeErrorCode == 5) //ERROR_ACCESS_DENIED
{
throw new Exception(e.Message + ". Access Denied. File " +
myProcess.StartInfo.FileName + " could be in use.");
}
}
return stdout;
}
}
}