function cons(a, b) { return function con$_cell(pick) { return pick ? b : a; }; }
function car(c) { return c(0); }
function cdr(c) { return c(1); }
function list()
{
  var h = arguments[0],
      t = Array.prototype.slice.call(arguments, 1);
  if (t.length == 0)
    return cons(h, undefined);
  return cons(h, list.apply(undefined, t));
}

function cons(a, b) function con$_cell(pick) pick ? b : a;
function car(c) consp(c) ? c(0) : undefined;
function cdr(c) consp(c) ? c(1) : undefined;
function list() arguments.length == 0 ? cons() : Array.prototype.reduceRight.call(arguments, function(x, y) cons(y, x), undefined);

function nilp(c) c == undefined;
function consp(c) typeof c == "function" && c.name == "con$_cell";
function listp(l) nilp(l) || (consp(l) && listp(cdr(l)));

function caar(c) car(car(c));
function cadr(c) car(cdr(c));
function cdar(c) cdr(car(c));
function cddr(c) cdr(cdr(c));

function caaar(c) car(caar(c));
function caadr(c) car(cadr(c));
function cadar(c) car(cdar(c));
function caddr(c) car(cddr(c));
function cdaar(c) cdr(caar(c));
function cdadr(c) cdr(cadr(c));
function cddar(c) cdr(cdar(c));
function cdddr(c) cdr(cddr(c));

function caaaar(c) car(caaar(c));
function caaadr(c) car(caadr(c));
function caadar(c) car(cadar(c));
function caaddr(c) car(caddr(c));
function cadaar(c) car(cdaar(c));
function cadadr(c) car(cdadr(c));
function caddar(c) car(cddar(c));
function cadddr(c) car(cdddr(c));
function cdaaar(c) cdr(caaar(c));
function cdaadr(c) cdr(caadr(c));
function cdadar(c) cdr(cadar(c));
function cdaddr(c) cdr(caddr(c));
function cddaar(c) cdr(cdaar(c));
function cddadr(c) cdr(cdadr(c));
function cdddar(c) cdr(cddar(c));
function cddddr(c) cdr(cdddr(c));

function pprint(c)
{
  if (nilp(c))
    return "undefined";
  if (typeof c == "number")
    return ""+c;
  if (typeof c == "string")
    return '"'+ c.replace(/"/g, '\\"') +'"';
  if (consp(c))
  {
    var a = car(c), d = cdr(c);
    function pl(l)
    {
      var a = car(l), d = cdr(l);
      if (nilp(l))
        return "";
      if (!nilp(d))
        if (listp(d))
          return pprint(a) +", "+ pl(d);
        else
          return pprint(a) +", "+ pprint(d);
      else
        return pprint(a);
    }
    if (nilp(a) && nilp(d))
      return "list()";
    if (listp(d))
      return "list("+ pl(c) +")";
    else
      return "cons("+ pprint(a) +", "+ pprint(d) +")";
  }
  if (c instanceof Array)
    return c.toSource();
  if (typeof c == "function")
    return c.toSource();
  return c;
}

function test(b, msg)
{
  tests++;
  !!b && passes++;
  !b && print("fail", msg);
}
function ok(a, b) test((a === b), pprint(a) +" !== "+ pprint(b));
function print_ok(a, b) test((a === pprint(b)), a +" !== "+ pprint(b));
function report()
{
  if (tests == passes)
    print("All "+ tests +" tests passed.");
  else
    print((tests - passes) +" out of "+ tests +" tests failed.");
}

var tests = 0, passes = 0;

test(!consp(42),
     "42 isn't supposed to be a cons");
test(!consp(function(x) x*x),
     "function(x) x*x) isn't supposed to be a cons");
test(!consp([1,2]),
     "[1,2] isn't supposed to be a cons");
test(!listp(42),
     "40 isn't supposed to be a list");
test(!listp(function(x) x*x),
     "function(x) x*x) isn't supposed to be a list");
test(!listp([1,2]),
     "[1,2] isn't supposed to be a list");
test(consp(cons()),
     "cons() is supposed to be a cons");
test(listp(cons()),
     "cons() is supposed to be a list");
test(consp(list()),
     "list() is supposed to be a cons");
test(listp(list()),
     "list() is supposed to be a list");
test(listp(list(42, "b", "q")),
     "list(42, \"b\", \"q\") is supposed to be a list");
test(consp(list(42, "b", "q")),
     "list(42, \"b\", \"q\") is supposed to be a cons");
test(listp(list(25, undefined, "s")),
     "list(25, undefined, \"s\") is supposed to be a list");
test(listp(list(82, cons("a", "b"))),
     "list(82, cons(\"a\", \"b\")) is supposed to be a list");
test(!listp(cons(82, cons("a", "b"))),
     "cons(82, cons(\"a\", \"b\")) isn't supposed to be a list");
test(listp(cons(82, cons("a", cons("b")))),
     "cons(82, cons(\"a\", cons(\"b\"))) is supposed to be a list");
test(!listp(cons(82, cons("a", cons("b", cons("c", cons("d", "e")))))),
     "cons(82, cons(\"a\", cons(\"b\", cons(\"c\", cons(\"d\", \"e\"))))) isn't supposed to be a list");
test(listp(cons(cons(24, 42), cons("a", cons("b")))),
     "cons(cons(24, 42), cons(\"a\", cons(\"b\"))) is supposed to be a list");
test(!listp(cons(cons(42, 24), cons("a", cons("b", cons("c", cons("d", "e")))))),
     "cons(cons(42, 24), cons(\"a\", cons(\"b\", cons(\"c\", cons(\"d\", \"e\"))))) isn't supposed to be a list");
test(!listp(cons(list("a", 42, "c"), list("b", 24, "d"))),
     "cons(list(\"a\", 42, \"c\"), list(\"b\", 24, \"d\")) isn't supposed to be a list");

var t = cons(cons(cons(cons("aaaa",
                            "daaa"),
                       cons("adaa",
                            "ddaa")),
                  cons(cons("aada",
                            "dada"),
                       cons("adda",
                            "ddda"))),
             cons(cons(cons("aaad",
                            "daad"),
                       cons("adad",
                            "ddad")),
                  cons(cons("aadd",
                            "dadd"),
                       cons("addd",
                            "dddd"))));

print_ok('"aaaa"', caaaar(t));
print_ok('"aaad"', caaadr(t));
print_ok('"aada"', caadar(t));
print_ok('"aadd"', caaddr(t));
print_ok('"adaa"', cadaar(t));
print_ok('"adad"', cadadr(t));
print_ok('"adda"', caddar(t));
print_ok('"addd"', cadddr(t));
print_ok('"daaa"', cdaaar(t));
print_ok('"daad"', cdaadr(t));
print_ok('"dada"', cdadar(t));
print_ok('"dadd"', cdaddr(t));
print_ok('"ddaa"', cddaar(t));
print_ok('"ddad"', cddadr(t));
print_ok('"ddda"', cdddar(t));
print_ok('"dddd"', cddddr(t));

print_ok('42',
         42);
print_ok('undefined',
         undefined);
print_ok('(function (x) x * x)',
         function(x) x*x);
print_ok('["a", 42]',
         ["a", 42]);
print_ok('list()',
         cons());
print_ok('cons(undefined, 42)',
         cons(undefined, 42));
print_ok('cons("a", 42)',
         cons("a", 42));
print_ok('cons(list("a"), cons(42, "b"))',
         cons(cons("a"), cons(42, "b")));
print_ok('list()',
         list());
print_ok('list("a", 42, "c")',
         cons("a", cons(42, cons("c"))));
print_ok('list("\\\"foo\\\"", 42, "\\\"bar\\\"")',
         list("\"foo\"", 42, "\"bar\""));
print_ok('list(cons("a", "b"), undefined, cons("c", "d"))', 
         list(cons("a", "b"), undefined, cons("c", "d")));
print_ok('list(cons("a", "b"), (function (x) x * x), cons("c", "d"))',
         list(cons("a", "b"), function(x) x*x, cons("c", "d")));
print_ok('list(cons("a", "b"), cons(42, 24), cons("c", "d"))',
         list(cons("a", "b"), cons(42, 24), cons("c", "d")));
print_ok('list("a", 42, "c", cons(1, 2))', 
         list("a", 42, "c", cons(1, 2)));
print_ok('cons(list("a", 42, "c"), list("b", 24, "d"))',
         cons(list("a", 42, "c"), list("b", 24, "d")));
print_ok('list(list("a", 42, "c"), list("b", 24, "d"))',
         list(list("a", 42, "c"), list("b", 24, "d")));

report();

