{
  "id": "P084",
  "slug": "permutations",
  "key": "P084-permutations",
  "title": "Permutations and combinations",
  "summary": "The orderings of a word's letters by backtracking and in dictionary order, with repeated letters counted once; the next and previous ordering; an ordering's place and the ordering at a place; combinations; derangements, whose share closes in on 1/e; and Gray codes.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P084 - Permutations and combinations\n\nThe orderings of a word's letters, by backtracking and in dictionary order,\nwith repeated letters counted once. Also: the next and previous ordering;\nan ordering's place among all of them and the ordering at a given place;\ncombinations; derangements, whose share closes in on 1/e; and Gray codes.\n\n- `main.eml` - the menu, reading words and numbers, and the reports\n- `arrange.eml` - backtracking, counting different words, next and\n  previous orderings, rank and unrank, combinations, derangements, Gray\n  codes\n\nHow each part works:\n\n- Backtracking, as in the corpus case `permutations-generator`: pick an\n  unused item, go on with the rest, then free it again for the next\n  branch. The list of used items is changed in place, so it must be\n  undone; the ordering so far is a new list each time, so it need not be.\n  It makes all n! orderings, repeats included: 720 for \"banana\".\n- Dictionary order: the next ordering finds the rightmost place where an\n  item is smaller than the one after it, swaps it with the rightmost\n  bigger item, and reverses the rest. Starting from the sorted letters,\n  that lists each different word exactly once - 60 for \"banana\", which is\n  6! / (3! 2!).\n- An ordering's place: for each position, count the smaller items still to\n  come and multiply by the orderings of the rest (the factorial number\n  system). The ordering at a place reads the same digits back.\n- Derangements, the orderings that leave nothing in its place:\n  D(n) = (n - 1)(D(n - 1) + D(n - 2)). Their share of all orderings closes\n  in on 1/e: D(10)/10! = 0.3678794..., against 1/e = 0.3678794411...\n- Gray code: the (n - 1)-bit code, then the same reversed, with 0 and then\n  1 in front. Each code differs from the one before in one bit, so the\n  subsets come one item in or out at a time.\n\nWhat is checked: menu choices 1 to 8; words of letters or digits, up to 6\nfor listing orderings, 12 for neighbours and places, 10 for combinations,\n5 for Gray codes, with all items different where a place or a subset\nneeds it; a place from 1 to n!; 0 to n items to choose; n from 1 to 20 for\nderangements. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the orderings of abc and of banana (720 made, 60 different);\n- the neighbours of monday, and of abc (the first) and cba (the last);\n- monday as number 327 of 720, ordering 327 of admnoy, and ordering 24 of\n  abcd (dcba);\n- 3 of 5 items (10 ways) and 0 of 5 (1 way);\n- derangements of 3 and of 10, and the Gray code for abc.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- a 7-letter word, \"a b\", a word with a repeated letter where they must\n  differ, place 7 of 6, 11 items, choosing 4 of 3, n of 0 and 21, and 6\n  items for a Gray code - each refused, then cancelled.\n\nBuilt on the verified corpus case `permutations-generator` (backtracking,\nwith one kind of state undone and the other rebuilt).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P084 permutations and combinations: the orderings of a word's letters,\n# the next and previous in dictionary order, an ordering's place and the\n# ordering at a place, combinations, derangements and Gray codes.\nimport arrange\n\ndef trim(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef number(s):\n    if len(s) == 0 or len(s) > 9:\n        return -1\n    0 => n\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n * 10 + int(c) => n\n    return n\n\ndef letters(s):\n    [] => out\n    for c in s:\n        out + [c] => out\n    return out\n\ndef word(xs):\n    \"\" => s\n    for x in xs:\n        s + x => s\n    return s\n\ndef good(s, most):\n    if len(s) == 0 or len(s) > most:\n        return False\n    for c in s:\n        if not ((c >= \"a\" and c <= \"z\") or (c >= \"A\" and c <= \"Z\") or (c >= \"0\" and c <= \"9\")):\n            return False\n    return True\n\ndef all_different(s):\n    for i in [0:len(s) - 1]:\n        for j in [i + 1:len(s) - 1]:\n            if s[i] == s[j]:\n                return False\n    return True\n\ndef ask_word(prompt, most, different):\n    while True:\n        trim(input(prompt)) => s\n        if s == \"\":\n            return \"\"\n        if good(s, most) and (not different or all_different(s)):\n            return s\n        if different:\n            (\"Type 1 to \" + str(most) + \" letters or digits, all different.\") ^0\n        else:\n            (\"Type 1 to \" + str(most) + \" letters or digits.\") ^0\n\ndef ask(prompt, low, high):\n    while True:\n        trim(input(prompt + \" (\" + str(low) + \" to \" + str(high) + \")> \")) => a\n        if a == \"\":\n            return -1\n        number(a) => n\n        if n >= low and n <= high:\n            return n\n        (\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\") ^0\n\ndef orderings():\n    ask_word(\"word (1 to 6 letters)> \", 6, False) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return\n    letters(s) => xs\n    [0] * len(xs) => used\n    arrange.permute(xs, [], used, []) => every\n    arrange.distinct(xs) => d\n    arrange.sorted_copy(xs) => p\n    [] => shown\n    0 => count\n    while len(p) > 0:\n        if count < 24:\n            shown + [word(p)] => shown\n        count + 1 => count\n        arrange.next_perm(p) => p\n    \"\" => line\n    for w in shown:\n        if line != \"\":\n            line + \" \" => line\n        line + w => line\n    if count > 24:\n        line + \" ... (\" + str(count - 24) + \" more)\" => line\n    line ^0\n    (\"Backtracking makes \" + str(len(every)) + \" orderings = \" + str(len(xs)) + \"!; \" + str(d) + \" of them are different words, and stepping to the next in dictionary order lists exactly \" + str(count) + \".\") ^0\n    if d < len(every):\n        \"Repeated letters make the same word more than once: n! divided by k! for each letter used k times.\" ^0\n\ndef neighbours():\n    ask_word(\"word (1 to 12 letters)> \", 12, False) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return\n    letters(s) => xs\n    arrange.next_perm(xs) => n\n    arrange.prev_perm(xs) => p\n    if len(p) == 0:\n        (s + \" is the first ordering of its letters; nothing comes before it.\") ^0\n    else:\n        (\"before \" + s + \": \" + word(p)) ^0\n    if len(n) == 0:\n        (s + \" is the last ordering of its letters; nothing comes after it.\") ^0\n    else:\n        (\"after \" + s + \":  \" + word(n)) ^0\n\ndef place():\n    ask_word(\"word with different letters (1 to 12)> \", 12, True) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return\n    letters(s) => xs\n    arrange.rank(xs) => r\n    arrange.factorial(len(xs)) => total\n    (s + \" is number \" + str(r) + \" of the \" + str(total) + \" orderings of its letters, in dictionary order.\") ^0\n    arrange.unrank(xs, r) => back\n    (\"Number \" + str(r) + \" read back the other way is \" + word(back) + \".\") ^0\n\ndef at_place():\n    ask_word(\"letters, all different (1 to 12)> \", 12, True) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return\n    arrange.factorial(len(s)) => total\n    ask(\"place\", 1, total) => k\n    if k == -1:\n        \"Cancelled.\" ^0\n        return\n    arrange.unrank(letters(s), k) => w\n    (\"Ordering number \" + str(k) + \" of \" + word(arrange.sorted_copy(letters(s))) + \" is \" + word(w) + \".\") ^0\n\ndef choose():\n    ask_word(\"items, all different (1 to 10)> \", 10, True) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return\n    ask(\"how many to choose\", 0, len(s)) => k\n    if k == -1:\n        \"Cancelled.\" ^0\n        return\n    arrange.combinations(letters(s), k, 0, [], []) => cs\n    \"\" => line\n    0 => i\n    for c in cs:\n        if i < 30:\n            if line != \"\":\n                line + \" \" => line\n            if len(c) == 0:\n                line + \"(none)\" => line\n            else:\n                line + word(c) => line\n        i + 1 => i\n    if len(cs) > 30:\n        line + \" ... (\" + str(len(cs) - 30) + \" more)\" => line\n    line ^0\n    \"ways\" => w\n    if len(cs) == 1:\n        \"way\" => w\n    (str(len(cs)) + \" \" + w + \" = \" + str(len(s)) + \"! / (\" + str(k) + \"! \" + str(len(s) - k) + \"!).\") ^0\n\ndef decimal(a, b, places):\n    # a / b to `places` decimals, cut off, by long division (0 <= a < b).\n    \"0.\" => s\n    a => r\n    for i in [1:places]:\n        r * 10 => r\n        0 => d\n        while r >= b:\n            r - b => r\n            d + 1 => d\n        s + str(d) => s\n    return s\n\ndef mixed_up():\n    ask(\"n\", 1, 20) => n\n    if n == -1:\n        \"Cancelled.\" ^0\n        return\n    arrange.derangements(n) => d\n    arrange.factorial(n) => f\n    (\"Of the \" + str(f) + \" orderings of \" + str(n) + \" things, \" + str(d) + \" leave none in its place.\") ^0\n    (\"That is \" + decimal(d, f, 12) + \" of them; 1/e is 0.367879441171..., which the share closes in on as n grows.\") ^0\n    \"Each count is (n - 1) x (the two before added): the first thing goes to one of n - 1 places, and then it either swaps or does not.\" ^0\n\ndef gray_code():\n    ask_word(\"items for the subsets (1 to 5)> \", 5, True) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return\n    arrange.gray(len(s)) => codes\n    for c in codes:\n        \"\" => sub\n        for i in [0:len(s) - 1]:\n            if c[i] == \"1\":\n                sub + s[i] => sub\n        if sub == \"\":\n            \"(none)\" => sub\n        (\"  \" + c + \"  \" + sub) ^0\n    (\"All \" + str(len(codes)) + \" subsets, each one item in or out from the one before - the Gray code.\") ^0\n\n\"== Permutations and combinations ==\" ^0\n\"Orderings, dictionary order, places, combinations, derangements and Gray codes.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\" ^0\n    trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        orderings()\n    elif choice == \"2\":\n        neighbours()\n    elif choice == \"3\":\n        place()\n    elif choice == \"4\":\n        at_place()\n    elif choice == \"5\":\n        choose()\n    elif choice == \"6\":\n        mixed_up()\n    elif choice == \"7\":\n        gray_code()\n    elif choice == \"8\":\n        False => running\n    else:\n        \"Pick a number from 1 to 8.\" ^0\n\"Bye.\" ^0\n",
      "python": "import arrange\n\ndef trim(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef number(s):\n    if len(s) == 0 or len(s) > 9:\n        return -1\n    n = 0\n    for c in s:\n        if not (c >= \"0\" and c <= \"9\"):\n            return -1\n        n = n * 10 + int(c)\n    return n\n\ndef letters(s):\n    out = []\n    for c in s:\n        out = out + [c]\n    return out\n\ndef word(xs):\n    s = \"\"\n    for x in xs:\n        s = s + x\n    return s\n\ndef good(s, most):\n    if len(s) == 0 or len(s) > most:\n        return False\n    for c in s:\n        if not (c >= \"a\" and c <= \"z\" or c >= \"A\" and c <= \"Z\" or c >= \"0\" and c <= \"9\"):\n            return False\n    return True\n\ndef all_different(s):\n    for i in range(0, len(s)):\n        for j in range(i + 1, len(s)):\n            if s[i] == s[j]:\n                return False\n    return True\n\ndef ask_word(prompt, most, different):\n    while True:\n        s = trim(input(prompt))\n        if s == \"\":\n            return \"\"\n        if good(s, most) and (not different or all_different(s)):\n            return s\n        if different:\n            print(\"Type 1 to \" + str(most) + \" letters or digits, all different.\")\n        else:\n            print(\"Type 1 to \" + str(most) + \" letters or digits.\")\n\ndef ask(prompt, low, high):\n    while True:\n        a = trim(input(prompt + \" (\" + str(low) + \" to \" + str(high) + \")> \"))\n        if a == \"\":\n            return -1\n        n = number(a)\n        if n >= low and n <= high:\n            return n\n        print(\"Type a whole number from \" + str(low) + \" to \" + str(high) + \".\")\n\ndef orderings():\n    s = ask_word(\"word (1 to 6 letters)> \", 6, False)\n    if s == \"\":\n        print(\"Cancelled.\")\n        return\n    xs = letters(s)\n    used = [0] * len(xs)\n    every = arrange.permute(xs, [], used, [])\n    d = arrange.distinct(xs)\n    p = arrange.sorted_copy(xs)\n    shown = []\n    count = 0\n    while len(p) > 0:\n        if count < 24:\n            shown = shown + [word(p)]\n        count = count + 1\n        p = arrange.next_perm(p)\n    line = \"\"\n    for w in shown:\n        if line != \"\":\n            line = line + \" \"\n        line = line + w\n    if count > 24:\n        line = line + \" ... (\" + str(count - 24) + \" more)\"\n    print(line)\n    print(\"Backtracking makes \" + str(len(every)) + \" orderings = \" + str(len(xs)) + \"!; \" + str(d) + \" of them are different words, and stepping to the next in dictionary order lists exactly \" + str(count) + \".\")\n    if d < len(every):\n        print(\"Repeated letters make the same word more than once: n! divided by k! for each letter used k times.\")\n\ndef neighbours():\n    s = ask_word(\"word (1 to 12 letters)> \", 12, False)\n    if s == \"\":\n        print(\"Cancelled.\")\n        return\n    xs = letters(s)\n    n = arrange.next_perm(xs)\n    p = arrange.prev_perm(xs)\n    if len(p) == 0:\n        print(s + \" is the first ordering of its letters; nothing comes before it.\")\n    else:\n        print(\"before \" + s + \": \" + word(p))\n    if len(n) == 0:\n        print(s + \" is the last ordering of its letters; nothing comes after it.\")\n    else:\n        print(\"after \" + s + \":  \" + word(n))\n\ndef place():\n    s = ask_word(\"word with different letters (1 to 12)> \", 12, True)\n    if s == \"\":\n        print(\"Cancelled.\")\n        return\n    xs = letters(s)\n    r = arrange.rank(xs)\n    total = arrange.factorial(len(xs))\n    print(s + \" is number \" + str(r) + \" of the \" + str(total) + \" orderings of its letters, in dictionary order.\")\n    back = arrange.unrank(xs, r)\n    print(\"Number \" + str(r) + \" read back the other way is \" + word(back) + \".\")\n\ndef at_place():\n    s = ask_word(\"letters, all different (1 to 12)> \", 12, True)\n    if s == \"\":\n        print(\"Cancelled.\")\n        return\n    total = arrange.factorial(len(s))\n    k = ask(\"place\", 1, total)\n    if k == -1:\n        print(\"Cancelled.\")\n        return\n    w = arrange.unrank(letters(s), k)\n    print(\"Ordering number \" + str(k) + \" of \" + word(arrange.sorted_copy(letters(s))) + \" is \" + word(w) + \".\")\n\ndef choose():\n    s = ask_word(\"items, all different (1 to 10)> \", 10, True)\n    if s == \"\":\n        print(\"Cancelled.\")\n        return\n    k = ask(\"how many to choose\", 0, len(s))\n    if k == -1:\n        print(\"Cancelled.\")\n        return\n    cs = arrange.combinations(letters(s), k, 0, [], [])\n    line = \"\"\n    i = 0\n    for c in cs:\n        if i < 30:\n            if line != \"\":\n                line = line + \" \"\n            if len(c) == 0:\n                line = line + \"(none)\"\n            else:\n                line = line + word(c)\n        i = i + 1\n    if len(cs) > 30:\n        line = line + \" ... (\" + str(len(cs) - 30) + \" more)\"\n    print(line)\n    w = \"ways\"\n    if len(cs) == 1:\n        w = \"way\"\n    print(str(len(cs)) + \" \" + w + \" = \" + str(len(s)) + \"! / (\" + str(k) + \"! \" + str(len(s) - k) + \"!).\")\n\ndef decimal(a, b, places):\n    s = \"0.\"\n    r = a\n    for i in range(1, places+1):\n        r = r * 10\n        d = 0\n        while r >= b:\n            r = r - b\n            d = d + 1\n        s = s + str(d)\n    return s\n\ndef mixed_up():\n    n = ask(\"n\", 1, 20)\n    if n == -1:\n        print(\"Cancelled.\")\n        return\n    d = arrange.derangements(n)\n    f = arrange.factorial(n)\n    print(\"Of the \" + str(f) + \" orderings of \" + str(n) + \" things, \" + str(d) + \" leave none in its place.\")\n    print(\"That is \" + decimal(d, f, 12) + \" of them; 1/e is 0.367879441171..., which the share closes in on as n grows.\")\n    print(\"Each count is (n - 1) x (the two before added): the first thing goes to one of n - 1 places, and then it either swaps or does not.\")\n\ndef gray_code():\n    s = ask_word(\"items for the subsets (1 to 5)> \", 5, True)\n    if s == \"\":\n        print(\"Cancelled.\")\n        return\n    codes = arrange.gray(len(s))\n    for c in codes:\n        sub = \"\"\n        for i in range(0, len(s)):\n            if c[i] == \"1\":\n                sub = sub + s[i]\n        if sub == \"\":\n            sub = \"(none)\"\n        print(\"  \" + c + \"  \" + sub)\n    print(\"All \" + str(len(codes)) + \" subsets, each one item in or out from the one before - the Gray code.\")\n\nprint(\"== Permutations and combinations ==\")\nprint(\"Orderings, dictionary order, places, combinations, derangements and Gray codes.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\")\n    choice = trim(input(\"choice> \"))\n    if choice == \"1\":\n        orderings()\n    elif choice == \"2\":\n        neighbours()\n    elif choice == \"3\":\n        place()\n    elif choice == \"4\":\n        at_place()\n    elif choice == \"5\":\n        choose()\n    elif choice == \"6\":\n        mixed_up()\n    elif choice == \"7\":\n        gray_code()\n    elif choice == \"8\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 8.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "arrange.eml",
      "eml": "# P084 permutations and combinations - every ordering of a few items, the\n# next one in dictionary order, an ordering's place in that order and the\n# ordering at a given place, combinations, derangements and Gray codes.\n# Items are lists of one-character strings.\n\ndef permute(items, current, used, out):\n    # The corpus case permutations-generator: pick an unused item, go on\n    # with the rest, then free it again for the next branch. `used` is\n    # changed in place, so it must be undone; `current` is a new list each\n    # time, so it need not be.\n    if len(current) == len(items):\n        return out + [current]\n    for i in [0:len(items) - 1]:\n        if used[i] == 0:\n            1 => used[i]\n            permute(items, current + [items[i]], used, out) => out\n            0 => used[i]\n    return out\n\ndef sorted_copy(xs):\n    [0] * len(xs) => out\n    for i in [0:len(xs) - 1]:\n        xs[i] => out[i]\n    for i in [1:len(out) - 1]:\n        out[i] => x\n        i - 1 => j\n        while j >= 0 and out[j] > x:\n            out[j] => out[j + 1]\n            j - 1 => j\n        x => out[j + 1]\n    return out\n\ndef distinct(items):\n    # Orderings with repeated items counted once: n! / (k1! k2! ...).\n    # Exact: the factorials divide.\n    sorted_copy(items) => s\n    factorial(len(s)) => total\n    0 => i\n    while i < len(s):\n        i => j\n        while j < len(s) and s[j] == s[i]:\n            j + 1 => j\n        divide(total, factorial(j - i)) => total\n        j => i\n    return total\n\ndef factorial(n):\n    1 => f\n    for k in [2:n]:\n        f * k => f\n    return f\n\ndef divide(a, b):\n    # a / b when b divides a, digit by digit (exact at any size).\n    0 => q\n    0 => r\n    for ch in str(a):\n        r * 10 + int(ch) => r\n        0 => d\n        while r >= b:\n            r - b => r\n            d + 1 => d\n        q * 10 + d => q\n    return q\n\ndef next_perm(xs):\n    # The next ordering in dictionary order, or [] after the last: find the\n    # rightmost place i with xs[i] < xs[i + 1], swap xs[i] with the\n    # rightmost item bigger than it, and reverse what follows i.\n    [0] * len(xs) => a\n    for k in [0:len(xs) - 1]:\n        xs[k] => a[k]\n    len(a) - 2 => i\n    while i >= 0 and a[i] >= a[i + 1]:\n        i - 1 => i\n    if i < 0:\n        return []\n    len(a) - 1 => j\n    while a[j] <= a[i]:\n        j - 1 => j\n    a[i] => t\n    a[j] => a[i]\n    t => a[j]\n    i + 1 => lo\n    len(a) - 1 => hi\n    while lo < hi:\n        a[lo] => t\n        a[hi] => a[lo]\n        t => a[hi]\n        lo + 1 => lo\n        hi - 1 => hi\n    return a\n\ndef prev_perm(xs):\n    # The ordering just before, or [] before the first: the same steps with\n    # every comparison turned round.\n    [0] * len(xs) => a\n    for k in [0:len(xs) - 1]:\n        xs[k] => a[k]\n    len(a) - 2 => i\n    while i >= 0 and a[i] <= a[i + 1]:\n        i - 1 => i\n    if i < 0:\n        return []\n    len(a) - 1 => j\n    while a[j] >= a[i]:\n        j - 1 => j\n    a[i] => t\n    a[j] => a[i]\n    t => a[j]\n    i + 1 => lo\n    len(a) - 1 => hi\n    while lo < hi:\n        a[lo] => t\n        a[hi] => a[lo]\n        t => a[hi]\n        lo + 1 => lo\n        hi - 1 => hi\n    return a\n\ndef rank(xs):\n    # The place (from 1) of an ordering of different items among all\n    # orderings of them in dictionary order: for each position, how many\n    # smaller items are still to come, times the orderings of the rest -\n    # the factorial number system.\n    0 => r\n    len(xs) => n\n    for i in [0:n - 1]:\n        0 => smaller\n        for j in [i + 1:n - 1]:\n            if xs[j] < xs[i]:\n                smaller + 1 => smaller\n        r + smaller * factorial(n - 1 - i) => r\n    return r + 1\n\ndef unrank(items, place):\n    # The ordering at `place` (from 1): the same digits read the other way.\n    sorted_copy(items) => left\n    place - 1 => r\n    [] => out\n    len(items) => n\n    for i in [0:n - 1]:\n        factorial(n - 1 - i) => f\n        0 => d\n        while r >= f:\n            r - f => r\n            d + 1 => d\n        out + [left[d]] => out\n        [] => rest\n        for k in [0:len(left) - 1]:\n            if k != d:\n                rest + [left[k]] => rest\n        rest => left\n    return out\n\ndef combinations(items, k, start, current, out):\n    # Every choice of k items, in the order the items come.\n    if len(current) == k:\n        return out + [current]\n    for i in [start:len(items) - 1]:\n        if len(items) - i >= k - len(current):\n            combinations(items, k, i + 1, current + [items[i]], out) => out\n    return out\n\ndef derangements(n):\n    # Orderings that leave no item in its place: D(n) = (n - 1)(D(n - 1) +\n    # D(n - 2)), D(0) = 1, D(1) = 0.\n    1 => a\n    0 => b\n    if n == 0:\n        return 1\n    for k in [2:n]:\n        (k - 1) * (a + b) => c\n        b => a\n        c => b\n    return b\n\ndef gray(n):\n    # The n-bit reflected Gray code: the (n - 1)-bit code, then the same\n    # reversed, with 0 and then 1 in front - one bit changes each step.\n    [\"\"] => codes\n    for k in [1:n]:\n        [] => out\n        for c in codes:\n            out + [\"0\" + c] => out\n        len(codes) - 1 => i\n        while i >= 0:\n            out + [\"1\" + codes[i]] => out\n            i - 1 => i\n        out => codes\n    return codes\n",
      "python": "def permute(items, current, used, out):\n    if len(current) == len(items):\n        return out + [current]\n    for i in range(0, len(items)):\n        if used[i] == 0:\n            used[i] = 1\n            out = permute(items, current + [items[i]], used, out)\n            used[i] = 0\n    return out\n\ndef sorted_copy(xs):\n    out = [0] * len(xs)\n    for i in range(0, len(xs)):\n        out[i] = xs[i]\n    for i in range(1, len(out)):\n        x = out[i]\n        j = i - 1\n        while j >= 0 and out[j] > x:\n            out[j + 1] = out[j]\n            j = j - 1\n        out[j + 1] = x\n    return out\n\ndef distinct(items):\n    s = sorted_copy(items)\n    total = factorial(len(s))\n    i = 0\n    while i < len(s):\n        j = i\n        while j < len(s) and s[j] == s[i]:\n            j = j + 1\n        total = divide(total, factorial(j - i))\n        i = j\n    return total\n\ndef factorial(n):\n    f = 1\n    for k in range(2, n+1):\n        f = f * k\n    return f\n\ndef divide(a, b):\n    q = 0\n    r = 0\n    for ch in str(a):\n        r = r * 10 + int(ch)\n        d = 0\n        while r >= b:\n            r = r - b\n            d = d + 1\n        q = q * 10 + d\n    return q\n\ndef next_perm(xs):\n    a = [0] * len(xs)\n    for k in range(0, len(xs)):\n        a[k] = xs[k]\n    i = len(a) - 2\n    while i >= 0 and a[i] >= a[i + 1]:\n        i = i - 1\n    if i < 0:\n        return []\n    j = len(a) - 1\n    while a[j] <= a[i]:\n        j = j - 1\n    t = a[i]\n    a[i] = a[j]\n    a[j] = t\n    lo = i + 1\n    hi = len(a) - 1\n    while lo < hi:\n        t = a[lo]\n        a[lo] = a[hi]\n        a[hi] = t\n        lo = lo + 1\n        hi = hi - 1\n    return a\n\ndef prev_perm(xs):\n    a = [0] * len(xs)\n    for k in range(0, len(xs)):\n        a[k] = xs[k]\n    i = len(a) - 2\n    while i >= 0 and a[i] <= a[i + 1]:\n        i = i - 1\n    if i < 0:\n        return []\n    j = len(a) - 1\n    while a[j] >= a[i]:\n        j = j - 1\n    t = a[i]\n    a[i] = a[j]\n    a[j] = t\n    lo = i + 1\n    hi = len(a) - 1\n    while lo < hi:\n        t = a[lo]\n        a[lo] = a[hi]\n        a[hi] = t\n        lo = lo + 1\n        hi = hi - 1\n    return a\n\ndef rank(xs):\n    r = 0\n    n = len(xs)\n    for i in range(0, n):\n        smaller = 0\n        for j in range(i + 1, n):\n            if xs[j] < xs[i]:\n                smaller = smaller + 1\n        r = r + smaller * factorial(n - 1 - i)\n    return r + 1\n\ndef unrank(items, place):\n    left = sorted_copy(items)\n    r = place - 1\n    out = []\n    n = len(items)\n    for i in range(0, n):\n        f = factorial(n - 1 - i)\n        d = 0\n        while r >= f:\n            r = r - f\n            d = d + 1\n        out = out + [left[d]]\n        rest = []\n        for k in range(0, len(left)):\n            if k != d:\n                rest = rest + [left[k]]\n        left = rest\n    return out\n\ndef combinations(items, k, start, current, out):\n    if len(current) == k:\n        return out + [current]\n    for i in range(start, len(items)):\n        if len(items) - i >= k - len(current):\n            out = combinations(items, k, i + 1, current + [items[i]], out)\n    return out\n\ndef derangements(n):\n    a = 1\n    b = 0\n    if n == 0:\n        return 1\n    for k in range(2, n+1):\n        c = (k - 1) * (a + b)\n        a = b\n        b = c\n    return b\n\ndef gray(n):\n    codes = [\"\"]\n    for k in range(1, n+1):\n        out = []\n        for c in codes:\n            out = out + [\"0\" + c]\n        i = len(codes) - 1\n        while i >= 0:\n            out = out + [\"1\" + codes[i]]\n            i = i - 1\n        codes = out\n    return codes\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n1\nabcdefg\na b\n\n3\nhello\n\n4\nabc\n7\n\n5\nabcdefghijk\naab\nabc\n4\n\n6\n0\n21\n\n7\nabcdef\n\n8\n",
      "screen": "== Permutations and combinations ==\nOrderings, dictionary order, places, combinations, derangements and Gray codes.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 0\nPick a number from 1 to 8.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> x\nPick a number from 1 to 8.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 1\nword (1 to 6 letters)> abcdefg\nType 1 to 6 letters or digits.\nword (1 to 6 letters)> a b\nType 1 to 6 letters or digits.\nword (1 to 6 letters)> \nCancelled.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 3\nword with different letters (1 to 12)> hello\nType 1 to 12 letters or digits, all different.\nword with different letters (1 to 12)> \nCancelled.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 4\nletters, all different (1 to 12)> abc\nplace (1 to 6)> 7\nType a whole number from 1 to 6.\nplace (1 to 6)> \nCancelled.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 5\nitems, all different (1 to 10)> abcdefghijk\nType 1 to 10 letters or digits, all different.\nitems, all different (1 to 10)> aab\nType 1 to 10 letters or digits, all different.\nitems, all different (1 to 10)> abc\nhow many to choose (0 to 3)> 4\nType a whole number from 0 to 3.\nhow many to choose (0 to 3)> \nCancelled.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 6\nn (1 to 20)> 0\nType a whole number from 1 to 20.\nn (1 to 20)> 21\nType a whole number from 1 to 20.\nn (1 to 20)> \nCancelled.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 7\nitems for the subsets (1 to 5)> abcdef\nType 1 to 5 letters or digits, all different.\nitems for the subsets (1 to 5)> \nCancelled.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 8\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\nabc\n1\nbanana\n2\nmonday\n2\nabc\n2\ncba\n3\nmonday\n4\nadmnoy\n327\n4\nabcd\n24\n5\nabcde\n3\n5\nabcde\n0\n6\n3\n6\n10\n7\nabc\n8\n",
      "screen": "== Permutations and combinations ==\nOrderings, dictionary order, places, combinations, derangements and Gray codes.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 1\nword (1 to 6 letters)> abc\nabc acb bac bca cab cba\nBacktracking makes 6 orderings = 3!; 6 of them are different words, and stepping to the next in dictionary order lists exactly 6.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 1\nword (1 to 6 letters)> banana\naaabnn aaanbn aaannb aabann aabnan aabnna aanabn aananb aanban aanbna aannab aannba abaann abanan abanna abnaan abnana abnnaa anaabn anaanb anaban anabna ananab ananba ... (36 more)\nBacktracking makes 720 orderings = 6!; 60 of them are different words, and stepping to the next in dictionary order lists exactly 60.\nRepeated letters make the same word more than once: n! divided by k! for each letter used k times.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 2\nword (1 to 12 letters)> monday\nbefore monday: monayd\nafter monday:  mondya\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 2\nword (1 to 12 letters)> abc\nabc is the first ordering of its letters; nothing comes before it.\nafter abc:  acb\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 2\nword (1 to 12 letters)> cba\nbefore cba: cab\ncba is the last ordering of its letters; nothing comes after it.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 3\nword with different letters (1 to 12)> monday\nmonday is number 327 of the 720 orderings of its letters, in dictionary order.\nNumber 327 read back the other way is monday.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 4\nletters, all different (1 to 12)> admnoy\nplace (1 to 720)> 327\nOrdering number 327 of admnoy is monday.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 4\nletters, all different (1 to 12)> abcd\nplace (1 to 24)> 24\nOrdering number 24 of abcd is dcba.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 5\nitems, all different (1 to 10)> abcde\nhow many to choose (0 to 5)> 3\nabc abd abe acd ace ade bcd bce bde cde\n10 ways = 5! / (3! 2!).\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 5\nitems, all different (1 to 10)> abcde\nhow many to choose (0 to 5)> 0\n(none)\n1 way = 5! / (0! 5!).\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 6\nn (1 to 20)> 3\nOf the 6 orderings of 3 things, 2 leave none in its place.\nThat is 0.333333333333 of them; 1/e is 0.367879441171..., which the share closes in on as n grows.\nEach count is (n - 1) x (the two before added): the first thing goes to one of n - 1 places, and then it either swaps or does not.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 6\nn (1 to 20)> 10\nOf the 3628800 orderings of 10 things, 1334961 leave none in its place.\nThat is 0.367879464285 of them; 1/e is 0.367879441171..., which the share closes in on as n grows.\nEach count is (n - 1) x (the two before added): the first thing goes to one of n - 1 places, and then it either swaps or does not.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 7\nitems for the subsets (1 to 5)> abc\n  000  (none)\n  001  c\n  011  bc\n  010  b\n  110  ab\n  111  abc\n  101  ac\n  100  a\nAll 8 subsets, each one item in or out from the one before - the Gray code.\n\n1) orderings  2) next and previous  3) place of an ordering  4) ordering at a place  5) combinations  6) derangements  7) Gray code  8) quit\nchoice> 8\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "permutations-generator",
      "caseId": "121-permutations-generator",
      "title": "Permutations generator"
    }
  ],
  "updated": "2026-10-15"
}
