diff --git a/docs/notebooks/BigInts.ipynb b/docs/notebooks/BigInts.ipynb index c678efc..e5e0777 100644 --- a/docs/notebooks/BigInts.ipynb +++ b/docs/notebooks/BigInts.ipynb @@ -2,55 +2,77 @@ "cells": [ { "cell_type": "markdown", - "id": "be337543", + "id": "f7ecdba7", "metadata": {}, "source": [ "# BigInts in Joy\n", "\n", "Part of the puzzle is implementing \"bigints\", unbounded integers, by means of Oberon RISC signed 32-bit ints and their operations.\n", "\n", - "Model bigints as a pair of Boolean for the sign and a list of integers for the digits, to keep things simple let the bool be the first item on a list followed by zero or more int digits. The Least Signifigant digit is at the top or head of the list. The digits shall be confined to the range zero to `pow(2, 31) - 1`\n", + "We can model bigints as a pair of a Boolean value for the sign and a list of integers for the digits, to keep things simple let the bool be the first item on a list followed by zero or more int digits. The Least Signifigant digit is at the top or head of the list. Our *base* for the digits is:\n", "\n", - "Another way to say that is that our numbers are in base `2147483648` and our \"nine\" is `2147483647` (`0b1111111111111111111111111111111`, 31 ones.)\n", + "$$2^{31}$$\n", "\n", - "This lets us use (Oberon RISC) 32-bit signed ints to store our digits." + "Our digits are 0..2147483647 (our \"nine\".)" ] }, { "cell_type": "code", "execution_count": 1, - "id": "b34d58ef", + "id": "95cc03ee", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [] - } - ], + "outputs": [], "source": [ - "[base 2147483648] inscribe" + "1 31 << " + ] + }, + { + "cell_type": "markdown", + "id": "d677df93", + "metadata": {}, + "source": [ + "We can `inscribe` a constant function `base` to keep this value handy." ] }, { "cell_type": "code", "execution_count": 2, + "id": "b34d58ef", + "metadata": {}, + "outputs": [], + "source": [ + "unit [base] swoncat inscribe" + ] + }, + { + "cell_type": "markdown", + "id": "713e5a2e", + "metadata": {}, + "source": [ + "This also permits a kind of parameterization. E.g. let's say we wanted to use base 10 for our digits, maybe during debugging. All that requires is to rebind the symbol `base` to 10." + ] + }, + { + "cell_type": "markdown", + "id": "f46632bd", + "metadata": {}, + "source": [ + "We could define a Boolean predicate that returns `true` for integers that are valid as digits and `false` otherwise:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, "id": "35476eac", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [] - } - ], + "outputs": [], "source": [ "[valid_digit [0 >] [base <] &&] inscribe" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "id": "02a48806", "metadata": {}, "outputs": [ @@ -63,13 +85,28 @@ } ], "source": [ - "clear\n", "32 valid_digit 1232147483648 valid_digit" ] }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4286d332", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" + ] + }, { "cell_type": "markdown", - "id": "dea19f43", + "id": "1e9d9ec9", "metadata": {}, "source": [ "## Converting to and from Python Integers\n", @@ -81,39 +118,12 @@ "id": "bfed1f25", "metadata": {}, "source": [ - "To get the sign bool we can just use `!-` (\"not negative\")" + "To get the sign bool we can just use `!-` (\"not negative\"), to get the list of digits we repeatedly `divmod` the number by our `base`:" ] }, { "cell_type": "code", - "execution_count": 4, - "id": "8a60b54b", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "true false" - ] - } - ], - "source": [ - "clear\n", - "23 !- -23 !-" - ] - }, - { - "cell_type": "markdown", - "id": "9f7b5ab2", - "metadata": {}, - "source": [ - "To get the list of digits we repeatedly `divmod` the number by our `base`:" - ] - }, - { - "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "id": "3fc98ccd", "metadata": {}, "outputs": [ @@ -126,14 +136,12 @@ } ], "source": [ - "clear\n", - "\n", "12345678901234567890 base divmod swap" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "id": "b838c4cb", "metadata": {}, "outputs": [ @@ -151,8 +159,8 @@ }, { "cell_type": "code", - "execution_count": 7, - "id": "f5dc3799", + "execution_count": 8, + "id": "42c9d92d", "metadata": {}, "outputs": [ { @@ -172,14 +180,14 @@ "id": "73bfa634", "metadata": {}, "source": [ - "We keep it up until we get to zero.\n", + "We keep it up until we get to zero. This suggests a `while` loop:\n", "\n", " [0 >=] [base divmod swap] while" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "id": "faaac9d6", "metadata": {}, "outputs": [ @@ -192,15 +200,14 @@ } ], "source": [ - "clear\n", + "clear 1234567890123456789012345678901234567890\n", "\n", - "1234567890123456789012345678901234567890\n", - " [0 >] [base divmod swap] while pop" + "[0 >] [base divmod swap] while pop" ] }, { "cell_type": "markdown", - "id": "50c4cf87", + "id": "01fa7493", "metadata": {}, "source": [ "But we want these numbers in a list. The naive way using `infra` generates them in the reverse order of what we would like." @@ -208,7 +215,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "2a613f36", "metadata": {}, "outputs": [ @@ -221,24 +228,24 @@ } ], "source": [ - "clear\n", + "clear [1234567890123456789012345678901234567890]\n", "\n", - "[1234567890123456789012345678901234567890]\n", "[ [0 >] [base divmod swap] while pop ]\n", + "\n", "infra" ] }, { "cell_type": "markdown", - "id": "d4e1877f", + "id": "0054d0f7", "metadata": {}, "source": [ - "Instead we want a simple \"anamorphism\" builds the result list in the order we want LSB->MSB. " + "We could just reverse the list, but it's more efficient to build the result list in the order we want, LSB to MSB:" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "id": "e97b149d", "metadata": {}, "outputs": [ @@ -251,9 +258,8 @@ } ], "source": [ - "clear\n", + "clear 1234567890123456789012345678901234567890\n", "\n", - "1234567890123456789012345678901234567890\n", "[0 <=]\n", "[pop []]\n", "[base divmod swap]\n", @@ -261,9 +267,19 @@ "genrec" ] }, + { + "cell_type": "markdown", + "id": "1ef0b3f4", + "metadata": {}, + "source": [ + "#### Representing Zero\n", + "\n", + "This will return the empty list for zero:" + ] + }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "id": "67bb934b", "metadata": {}, "outputs": [ @@ -283,15 +299,30 @@ }, { "cell_type": "markdown", - "id": "98c78bfe", + "id": "038c99b9", "metadata": {}, "source": [ - "#### `digitalize`" + "I think this is better than returning `[0]` because that amounts to a single leading zero.\n", + "\n", + " [bool] is \"0\"\n", + " [bool 0] is \"00\"\n", + "\n", + "Eh?" + ] + }, + { + "cell_type": "markdown", + "id": "8f18b370", + "metadata": {}, + "source": [ + "#### `digitalize`\n", + "\n", + "Let's `inscribe` this function under the name `digitalize`:" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "id": "227271a9", "metadata": {}, "outputs": [ @@ -307,9 +338,17 @@ "[digitalize [0 <=] [pop []] [base divmod swap] [i cons] genrec] inscribe" ] }, + { + "cell_type": "markdown", + "id": "d8b91cb4", + "metadata": {}, + "source": [ + "Try it out:" + ] + }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "id": "b6f77ac6", "metadata": {}, "outputs": [ @@ -332,12 +371,12 @@ "id": "1bf8ba9e", "metadata": {}, "source": [ - "So `!-` for the sign and `abs digitalize` for the digits, followed by `cons`:" + "Putting it all together we have `!-` for the sign and `abs digitalize` for the digits, followed by `cons`:" ] }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "id": "246c2a58", "metadata": {}, "outputs": [ @@ -350,15 +389,30 @@ } ], "source": [ - "clear\n", + "clear 1234567890123456789012345678901234567890\n", "\n", - "1234567890123456789012345678901234567890\n", "[!-] [abs digitalize] cleave cons" ] }, + { + "cell_type": "code", + "execution_count": 16, + "id": "599ea445", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" + ] + }, { "cell_type": "markdown", - "id": "e995ac71", + "id": "c89e6054", "metadata": {}, "source": [ "#### `to_bigint`" @@ -366,25 +420,31 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "id": "5a72ce30", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", - "text": [ - "[true 1312754386 1501085485 57659106 105448366 58]" - ] + "text": [] } ], "source": [ "[to_bigint [!-] [abs digitalize] cleave cons] inscribe" ] }, + { + "cell_type": "markdown", + "id": "14724d21", + "metadata": {}, + "source": [ + "Try it out:" + ] + }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "id": "59514dcd", "metadata": {}, "outputs": [ @@ -397,14 +457,20 @@ } ], "source": [ - "clear\n", - "\n", - "1234567890123456789012345678901234567890 to_bigint" + "clear 1234567890123456789012345678901234567890 to_bigint" + ] + }, + { + "cell_type": "markdown", + "id": "c5a054dc", + "metadata": {}, + "source": [ + "With negative numbers:" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "id": "f3c19364", "metadata": {}, "outputs": [ @@ -417,14 +483,12 @@ } ], "source": [ - "clear\n", - "\n", - "-1234567890123456789012345678901234567890 to_bigint" + "clear -1234567890123456789012345678901234567890 to_bigint" ] }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "id": "998512c6", "metadata": {}, "outputs": [ @@ -440,7 +504,7 @@ }, { "cell_type": "markdown", - "id": "4976cf87", + "id": "b44d9d47", "metadata": {}, "source": [ "### Converting from bigint to Python ints\n", @@ -457,7 +521,7 @@ }, { "cell_type": "markdown", - "id": "e814f934", + "id": "0b7d0d1f", "metadata": {}, "source": [ "So the problem is to derive:\n", @@ -475,7 +539,7 @@ }, { "cell_type": "markdown", - "id": "8c3acc24", + "id": "5fd5f38f", "metadata": {}, "source": [ "Now this is an interesting function. The first thing I noticed is that it has two results that can be computed independently, suggesting a form like:\n", @@ -493,48 +557,58 @@ }, { "cell_type": "code", - "execution_count": 19, - "id": "4b988095", + "execution_count": 21, + "id": "bdd5ceab", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + " 1 0 23 • popop base *\n", + " 1 • base *\n", + " 1 • 2147483648 *\n", + "1 2147483648 • *\n", + "1 2147483648 • mul\n", + " 2147483648 • \n", + "\n", "2147483648" ] } ], "source": [ - "clear\n", - "\n", - "1 0 23 popop base *" + "clear 1 0 23 [popop base *] trace" ] }, { "cell_type": "code", - "execution_count": 20, - "id": "51e79e00", + "execution_count": 22, + "id": "244aa4e5", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ + "1 0 23 • rolldown * +\n", + "0 23 1 • * +\n", + "0 23 1 • mul +\n", + " 0 23 • +\n", + " 0 23 • add\n", + " 23 • \n", + "\n", "23" ] } ], "source": [ - "clear\n", - "\n", - "1 0 23 rolldown * +" + "clear 1 0 23 [rolldown * +] trace" ] }, { "cell_type": "code", - "execution_count": 21, - "id": "1d19061d", + "execution_count": 23, + "id": "b0b1afb0", "metadata": {}, "outputs": [ { @@ -546,14 +620,17 @@ } ], "source": [ - "clear\n", + "clear 1 0 23\n", "\n", - "1 0 23 [popop base *] [rolldown * +] clop popdd" + "[popop base *]\n", + "[rolldown * +]\n", + "clop\n", + "popdd" ] }, { "cell_type": "markdown", - "id": "9d2d7c33", + "id": "7497680b", "metadata": {}, "source": [ "#### `prep` and `from_bigint'`" @@ -561,8 +638,8 @@ }, { "cell_type": "code", - "execution_count": 22, - "id": "bb5b7bfe", + "execution_count": 24, + "id": "d15747ee", "metadata": {}, "outputs": [ { @@ -583,28 +660,8 @@ }, { "cell_type": "code", - "execution_count": 23, - "id": "4f7a2a4c", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2147483648 23" - ] - } - ], - "source": [ - "clear\n", - "\n", - "1 0 23 F" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "4fce526f", + "execution_count": 25, + "id": "c77d58c5", "metadata": {}, "outputs": [ { @@ -616,15 +673,15 @@ } ], "source": [ - "clear\n", + "clear 1 0 [1312754386 1501085485 57659106 105448366 58]\n", "\n", - "1 0 [1312754386 1501085485 57659106 105448366 58] from_bigint'" + "from_bigint'" ] }, { "cell_type": "code", - "execution_count": 25, - "id": "d20cf6cc", + "execution_count": 26, + "id": "b2b076c9", "metadata": {}, "outputs": [ { @@ -641,8 +698,8 @@ }, { "cell_type": "code", - "execution_count": 26, - "id": "cd3e41bb", + "execution_count": 27, + "id": "778ecfb2", "metadata": {}, "outputs": [ { @@ -659,8 +716,8 @@ }, { "cell_type": "code", - "execution_count": 27, - "id": "5ab43d77", + "execution_count": 28, + "id": "682ed5e2", "metadata": {}, "outputs": [ { @@ -677,8 +734,8 @@ }, { "cell_type": "code", - "execution_count": 28, - "id": "d1fd81d0", + "execution_count": 29, + "id": "5bb0973a", "metadata": {}, "outputs": [ { @@ -693,10 +750,18 @@ "to_bigint prep from_bigint'" ] }, + { + "cell_type": "markdown", + "id": "fd9f5fe1", + "metadata": {}, + "source": [ + "What about that sign bit?" + ] + }, { "cell_type": "code", - "execution_count": 29, - "id": "6cdb257c", + "execution_count": 30, + "id": "4736f46a", "metadata": {}, "outputs": [ { @@ -713,8 +778,8 @@ }, { "cell_type": "code", - "execution_count": 30, - "id": "e72fdf6c", + "execution_count": 31, + "id": "8615243e", "metadata": {}, "outputs": [ { @@ -731,16 +796,16 @@ }, { "cell_type": "markdown", - "id": "d0947b57", + "id": "c8057aca", "metadata": {}, "source": [ - "What about that sign bit?" + "That's no good, we lose the sign. Time to deal with that." ] }, { "cell_type": "code", - "execution_count": 31, - "id": "7f88bea1", + "execution_count": 32, + "id": "e2f8d6cb", "metadata": {}, "outputs": [ { @@ -755,10 +820,18 @@ "to_bigint" ] }, + { + "cell_type": "markdown", + "id": "e24c6a90", + "metadata": {}, + "source": [ + "We want to get the sign bit and the Python int," + ] + }, { "cell_type": "code", - "execution_count": 32, - "id": "d75da0ab", + "execution_count": 33, + "id": "e09c99c2", "metadata": {}, "outputs": [ { @@ -773,10 +846,18 @@ "[first] [prep from_bigint'] cleave" ] }, + { + "cell_type": "markdown", + "id": "cbe4e915", + "metadata": {}, + "source": [ + "and then use the sign bit to negate the Python int if the bigint was negative:" + ] + }, { "cell_type": "code", - "execution_count": 33, - "id": "93356c0d", + "execution_count": 34, + "id": "cc990e1e", "metadata": {}, "outputs": [ { @@ -793,9 +874,11 @@ }, { "cell_type": "markdown", - "id": "47f7b425", + "id": "cfc29643", "metadata": {}, "source": [ + "This gives:\n", + "\n", " foo == [first] [prep from_bigint'] cleave\n", " bar == swap [] [neg] branch\n", " from_bigint == foo bar" @@ -803,7 +886,7 @@ }, { "cell_type": "markdown", - "id": "2f940d9c", + "id": "8bffedec", "metadata": {}, "source": [ "#### `from_bigint`" @@ -811,8 +894,8 @@ }, { "cell_type": "code", - "execution_count": 34, - "id": "60a3c265", + "execution_count": 35, + "id": "fb5d4a30", "metadata": {}, "outputs": [ { @@ -830,8 +913,8 @@ }, { "cell_type": "code", - "execution_count": 35, - "id": "f2f60ef2", + "execution_count": 36, + "id": "b897a3bd", "metadata": {}, "outputs": [ { @@ -848,8 +931,8 @@ }, { "cell_type": "code", - "execution_count": 36, - "id": "fb95c019", + "execution_count": 37, + "id": "d1efc1ae", "metadata": {}, "outputs": [ { @@ -861,9 +944,23 @@ } ], "source": [ - "neg to_bigint\n", - "\n", - "from_bigint" + "neg to_bigint from_bigint" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "feca2542", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" ] }, { @@ -883,7 +980,7 @@ }, { "cell_type": "markdown", - "id": "7b0df08e", + "id": "9a639b8f", "metadata": {}, "source": [ "### The predicate\n", @@ -895,14 +992,52 @@ "\n", "There are two base cases: two empty lists or one empty list, the recursive branch is taken only if both lists are non-empty.\n", "\n", - " bool [a ...] [b ...] P\n", - "\n", - "The first thing to do is convert them to Booleans:" + " bool [a ...] [b ...] P" + ] + }, + { + "cell_type": "markdown", + "id": "7b0df08e", + "metadata": {}, + "source": [ + "The first thing to do is convert them to Booleans. Let's make a little truth table to work with:" ] }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 39, + "id": "9945efb8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[[a] [b]] [[a] []] [[] [b]] [[] []]]" + ] + } + ], + "source": [ + "clear\n", + "[\n", + "[[a] [b]]\n", + "[[a] []]\n", + "[[] [b]]\n", + "[[] []]\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "a84b971e", + "metadata": {}, + "source": [ + "Then we can `map` our predicate over this list to be sure ti does what we want:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, "id": "b2d4f3b2", "metadata": {}, "outputs": [ @@ -915,20 +1050,20 @@ } ], "source": [ - "clear\n", - "[\n", - "[[a] [b]]\n", - "[[a] []]\n", - "[[] [b]]\n", - "[[] []]\n", - "]\n", - "[[[bool] ii] infra] \n", - "map" + "[[[bool] ii] infra] map" + ] + }, + { + "cell_type": "markdown", + "id": "634d2203", + "metadata": {}, + "source": [ + "We want to *and* the bools and invert the result:" ] }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 41, "id": "d87fa971", "metadata": {}, "outputs": [ @@ -941,15 +1076,9 @@ } ], "source": [ - "clear\n", - "[\n", - "[[a] [b]]\n", - "[[a] []]\n", - "[[] [b]]\n", - "[[] []]\n", - "]\n", - "[[[bool] ii & not] infra] \n", - "map" + "clear [[[a] [b]] [[a] []] [[] [b]] [[] []]] \n", + "\n", + "[[[bool] ii & not] infra] map" ] }, { @@ -957,6 +1086,8 @@ "id": "6ce10b89", "metadata": {}, "source": [ + "So the predicate function we want here is:\n", + "\n", " P == [bool] ii & not" ] }, @@ -966,18 +1097,23 @@ "metadata": {}, "source": [ "### The base cases\n", - "We have to decide between three cases, but because addition is commutative we can lump together the first two cases:\n", + "On the non-recursive branch of the `genrec` we have to decide between three cases, but because addition is commutative we can lump together the first two:\n", "\n", " bool [] [b ...] THEN\n", " bool [a ...] [] THEN\n", + " \n", " bool [] [] THEN\n", "\n", - " THEN ≡ [P'] [THEN'] [ELSE] ifte" + "So we have an `ifte` expression:\n", + "\n", + " THEN ≡ [P'] [THEN'] [ELSE] ifte\n", + "\n", + "Let's define the predicate:" ] }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 42, "id": "491c0846", "metadata": {}, "outputs": [ @@ -996,8 +1132,8 @@ "[[] [b]]\n", "[[] []]\n", "]\n", - "[[[bool] ii |] infra] \n", - "map" + "\n", + "[[[bool] ii |] infra] map" ] }, { @@ -1028,7 +1164,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 43, "id": "bd51792f", "metadata": {}, "outputs": [ @@ -1043,9 +1179,17 @@ "[ditch-empty-list [bool] [popd] [pop] ifte] inscribe" ] }, + { + "cell_type": "markdown", + "id": "65947d5d", + "metadata": {}, + "source": [ + "Try it out:" + ] + }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 44, "id": "fbf134e2", "metadata": {}, "outputs": [ @@ -1063,7 +1207,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 45, "id": "90db3359", "metadata": {}, "outputs": [ @@ -1111,7 +1255,7 @@ }, { "cell_type": "markdown", - "id": "3c349c8f", + "id": "223cee8e", "metadata": {}, "source": [ "#### `bool_to_int`" @@ -1119,7 +1263,33 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 46, + "id": "8370ffc0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear\n", + "\n", + "[bool_to_int [0] [1] branch] inscribe" + ] + }, + { + "cell_type": "markdown", + "id": "a1d1ae27", + "metadata": {}, + "source": [ + "Try it out:" + ] + }, + { + "cell_type": "code", + "execution_count": 47, "id": "e86b7b90", "metadata": {}, "outputs": [ @@ -1132,17 +1302,21 @@ } ], "source": [ - "clear\n", - "\n", - "[bool_to_int [0] [1] branch] inscribe\n", - "\n", "false bool_to_int\n", "true bool_to_int" ] }, + { + "cell_type": "markdown", + "id": "a59f645b", + "metadata": {}, + "source": [ + "We can use this function to convert the carry flag to an integer and then add it to the sum of the two digits:" + ] + }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 48, "id": "cd70e18e", "metadata": {}, "outputs": [ @@ -1162,7 +1336,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 49, "id": "a27ff9f0", "metadata": {}, "outputs": [ @@ -1185,12 +1359,14 @@ "id": "89b971c9", "metadata": {}, "source": [ - "So the first part of `F` is `[bool_to_int] dipd + +` to get the total, then we need to" + "So the first part of `F` is `[bool_to_int] dipd + +` to get the total, then we need to do\n", + "\n", + "`base mod` to get the new digit and `base >=` to get the new carry flag:" ] }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 50, "id": "74a59ab5", "metadata": {}, "outputs": [ @@ -1210,7 +1386,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 51, "id": "3a8f5078", "metadata": {}, "outputs": [ @@ -1245,7 +1421,7 @@ }, { "cell_type": "markdown", - "id": "642e3b74", + "id": "b2b3a39e", "metadata": {}, "source": [ "#### `add-with-carry`" @@ -1253,7 +1429,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 52, "id": "c05bcd8e", "metadata": {}, "outputs": [ @@ -1270,9 +1446,17 @@ "[add-with-carry _add-with-carry0 _add-with-carry1] inscribe" ] }, + { + "cell_type": "markdown", + "id": "0b9effb9", + "metadata": {}, + "source": [ + "Try it out:" + ] + }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 53, "id": "03649114", "metadata": {}, "outputs": [ @@ -1292,7 +1476,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 54, "id": "9dca0fbc", "metadata": {}, "outputs": [ @@ -1312,7 +1496,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 55, "id": "c8f01d7d", "metadata": {}, "outputs": [ @@ -1332,7 +1516,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 56, "id": "a1e77990", "metadata": {}, "outputs": [ @@ -1352,7 +1536,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 57, "id": "b46a62ba", "metadata": {}, "outputs": [ @@ -1417,16 +1601,34 @@ }, { "cell_type": "markdown", - "id": "d5898c6a", + "id": "5fc15f8a", "metadata": {}, "source": [ - "If it's empty...\n", + "If it's empty... (omitting the \"\\_actd_\" prefix for clarity)\n", "\n", " true [] R0.else [add-carry-to-digits] R1\n", " ----------------------------------------------\n", - " 1 false [] [add-carry-to-digits] i cons\n", + " 1 false [] [add-carry-to-digits] i cons" + ] + }, + { + "cell_type": "markdown", + "id": "dbb26dfa", + "metadata": {}, + "source": [ + "Note that this implies:\n", "\n", - "and\n", + " R1 == i cons\n", + "\n", + "We have `1 false []` (rather than some other arrangement) to be compatible (same types and order) with the result of the other branch, which we now derive." + ] + }, + { + "cell_type": "markdown", + "id": "d5898c6a", + "metadata": {}, + "source": [ + "If the list of digits isn't empty...\n", "\n", " true [a ...] R0.then [add-carry-to-digits] i cons\n", " ----------------------------------------------------------------\n", @@ -1451,7 +1653,7 @@ }, { "cell_type": "markdown", - "id": "533e54ab", + "id": "32967775", "metadata": {}, "source": [ "#### `add-carry-to-digits`" @@ -1459,7 +1661,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 58, "id": "c8f1aa44", "metadata": {}, "outputs": [ @@ -1479,9 +1681,17 @@ "[_actd_R1 i cons] inscribe" ] }, + { + "cell_type": "markdown", + "id": "eb5a91c8", + "metadata": {}, + "source": [ + "Try it out:" + ] + }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 59, "id": "1fa115d9", "metadata": {}, "outputs": [ @@ -1501,7 +1711,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 60, "id": "79d9c526", "metadata": {}, "outputs": [ @@ -1521,7 +1731,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 61, "id": "bb030214", "metadata": {}, "outputs": [ @@ -1541,7 +1751,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 62, "id": "5e0a05af", "metadata": {}, "outputs": [ @@ -1561,7 +1771,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 63, "id": "7c730d6d", "metadata": {}, "outputs": [ @@ -1577,6 +1787,22 @@ "add-carry-to-digits" ] }, + { + "cell_type": "code", + "execution_count": 64, + "id": "8aee87f9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" + ] + }, { "cell_type": "markdown", "id": "cf558731", @@ -1592,7 +1818,31 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 65, + "id": "170d4947", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "[carry [] [1 swons] branch] inscribe" + ] + }, + { + "cell_type": "markdown", + "id": "2b7c6397", + "metadata": {}, + "source": [ + "Try it out:" + ] + }, + { + "cell_type": "code", + "execution_count": 66, "id": "5e5ddf74", "metadata": {}, "outputs": [ @@ -1607,12 +1857,12 @@ "source": [ "clear\n", "\n", - "true [] [] pop swap [] [1 swons] branch" + "true [] [] pop swap carry" ] }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 67, "id": "c57d1fb2", "metadata": {}, "outputs": [ @@ -1627,12 +1877,12 @@ "source": [ "clear\n", "\n", - "false [] [] pop swap [] [1 swons] branch" + "false [] [] pop swap carry" ] }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 68, "id": "59b1338c", "metadata": {}, "outputs": [ @@ -1653,21 +1903,23 @@ "source": [ "The story so far...\n", "\n", - " add-digits ≡ initial-carry add-digits'\n", + " add-digits == initial-carry add-digits'\n", " \n", - " add-digits' ≡ [P] [THEN] [R0] [R1] genrec\n", + " add-digits' == [P] [THEN] [R0] [R1] genrec\n", "\n", - " initial-carry ≡ false rollup\n", + " initial-carry == false rollup\n", "\n", - " P ≡ [bool] ii & not\n", + " P == [bool] ii & not\n", "\n", - " THEN ≡ [P'] [THEN'] [ELSE] ifte\n", + " THEN == [P'] [THEN'] [ELSE] ifte\n", "\n", - " P' ≡ [bool] ii |\n", + " P' == [bool] ii |\n", "\n", - " THEN' ≡ ditch-empty-list add-carry-to-digits\n", + " THEN' == ditch-empty-list add-carry-to-digits\n", + " \n", + " carry == [] [1 swons] branch\n", "\n", - " ELSE ≡ pop swap [] [1 swons] branch\n", + " ELSE == pop swap carry\n", "\n", "We just need to knock out those recursive functions `R0` and `R1` and we're done." ] @@ -1692,7 +1944,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 69, "id": "78dec757", "metadata": {}, "outputs": [ @@ -1721,7 +1973,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 70, "id": "5b4bd2fe", "metadata": {}, "outputs": [ @@ -1743,7 +1995,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 71, "id": "f284ea19", "metadata": {}, "outputs": [ @@ -1785,7 +2037,7 @@ }, { "cell_type": "markdown", - "id": "ef20a308", + "id": "25d9bf30", "metadata": {}, "source": [ "#### `add-digits`" @@ -1793,7 +2045,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 72, "id": "8adffacb", "metadata": {}, "outputs": [ @@ -1818,7 +2070,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 73, "id": "5b2e5738", "metadata": {}, "outputs": [ @@ -1836,7 +2088,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 74, "id": "cd7ac0b9", "metadata": {}, "outputs": [ @@ -1854,7 +2106,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 75, "id": "50f2e62c", "metadata": {}, "outputs": [ @@ -1872,7 +2124,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 76, "id": "54ca630f", "metadata": {}, "outputs": [ @@ -1890,7 +2142,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 77, "id": "27bb4638", "metadata": {}, "outputs": [ @@ -1908,7 +2160,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 78, "id": "4d2d446a", "metadata": {}, "outputs": [ @@ -1934,7 +2186,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 79, "id": "c187ffff", "metadata": {}, "outputs": [ @@ -1970,8 +2222,8 @@ }, { "cell_type": "code", - "execution_count": 74, - "id": "75ca9562", + "execution_count": 80, + "id": "f2987cd7", "metadata": {}, "outputs": [ { @@ -1990,8 +2242,8 @@ }, { "cell_type": "code", - "execution_count": 75, - "id": "e95920cc", + "execution_count": 81, + "id": "393623c3", "metadata": {}, "outputs": [ { @@ -2008,8 +2260,8 @@ }, { "cell_type": "code", - "execution_count": 76, - "id": "24f0ab0c", + "execution_count": 82, + "id": "20ce6f9f", "metadata": {}, "outputs": [ { @@ -2028,8 +2280,8 @@ }, { "cell_type": "code", - "execution_count": 77, - "id": "7d8cf685", + "execution_count": 83, + "id": "b355996b", "metadata": {}, "outputs": [ { @@ -2046,8 +2298,8 @@ }, { "cell_type": "code", - "execution_count": 78, - "id": "e3f2133d", + "execution_count": 84, + "id": "3d4a27b1", "metadata": {}, "outputs": [ { @@ -2064,8 +2316,8 @@ }, { "cell_type": "code", - "execution_count": 79, - "id": "97bc172d", + "execution_count": 85, + "id": "ac5990b4", "metadata": {}, "outputs": [ { @@ -2082,8 +2334,8 @@ }, { "cell_type": "code", - "execution_count": 80, - "id": "16f056bc", + "execution_count": 86, + "id": "11b3384e", "metadata": {}, "outputs": [ { @@ -2100,7 +2352,7 @@ }, { "cell_type": "markdown", - "id": "45839ac3", + "id": "ab08817b", "metadata": {}, "source": [ "#### `add-bigints`" @@ -2108,8 +2360,8 @@ }, { "cell_type": "code", - "execution_count": 81, - "id": "ca884af6", + "execution_count": 87, + "id": "e75d711e", "metadata": {}, "outputs": [ { @@ -2128,8 +2380,8 @@ }, { "cell_type": "code", - "execution_count": 82, - "id": "4bebb355", + "execution_count": 88, + "id": "e69471ba", "metadata": {}, "outputs": [ { @@ -2146,8 +2398,8 @@ }, { "cell_type": "code", - "execution_count": 83, - "id": "12a79bb6", + "execution_count": 89, + "id": "29963839", "metadata": {}, "outputs": [ { @@ -2162,6 +2414,2089 @@ "add-bigints" ] }, + { + "cell_type": "markdown", + "id": "a9894e7e", + "metadata": {}, + "source": [ + "## Subtraction of Like Signs `sub-digits`\n", + "\n", + "Subtraction is similar to addition in that it's a simple recursive algorithm that works digit-by-digit. It has the same four cases as well, we can reuse `P` and `P'`.\n", + "\n", + " initial-carry == false rollup\n", + " sub-digits' == [P] [sub.THEN] [sub.R0] [sub.R1] genrec\n", + " sub-digits == initial-carry add-digits'\n", + " sub.THEN == [P'] [sub.THEN'] [sub.ELSE] ifte" + ] + }, + { + "cell_type": "markdown", + "id": "980a57b2", + "metadata": {}, + "source": [ + "### Refactoring For The Win\n", + "\n", + "\n", + "We noted above that the algorithm for subtraction is similar to that for addition. Maybe we can reuse *more* than just `P` and `P'`?\n", + "\n", + " initial-carry == false rollup\n", + " sub-digits' == [P] [sub.THEN] [sub.R0] [sub.R1] genrec\n", + " sub-digits == initial-carry add-digits'\n", + " sub.THEN == [P'] [sub.THEN'] [sub.ELSE] ifte" + ] + }, + { + "cell_type": "markdown", + "id": "445c7b48", + "metadata": {}, + "source": [ + "In fact, I think we could refactor (prematurely, two cases is one too few) something like this?\n", + "\n", + " [sub.THEN'] [sub.ELSE] [sub.R0] [sub.R1] foo\n", + " ---------------------------------------------------------------------\n", + " [P] [[P'] [sub.THEN'] [sub.ELSE] ifte] [sub.R0] [sub.R1] genrec" + ] + }, + { + "cell_type": "markdown", + "id": "730b04ef", + "metadata": {}, + "source": [ + "or just\n", + "\n", + " [THEN] [ELSE] [R0] [R1] foo\n", + " ----------------------------------------------------\n", + " [P] [[P'] [THEN] [ELSE] ifte] [R0] [R1] genrec\n", + "\n", + "eh?" + ] + }, + { + "cell_type": "markdown", + "id": "06e9be73", + "metadata": {}, + "source": [ + "`foo` is something like:\n", + "\n", + " F == [ifte] ccons [P'] swons\n", + " G == [F] dipdd\n", + "\n", + " [THEN] [ELSE] [R0] [R1] [F] dipdd foo'\n", + " [THEN] [ELSE] F [R0] [R1] foo'\n", + " [THEN] [ELSE] [ifte] ccons [P'] swons [R0] [R1] foo'\n", + " [[THEN] [ELSE] ifte] [P'] swons [R0] [R1] foo'\n", + " [[P'] [THEN] [ELSE] ifte] [R0] [R1] foo'" + ] + }, + { + "cell_type": "markdown", + "id": "5e3e91ea", + "metadata": {}, + "source": [ + "That leaves `[P]`...\n", + "\n", + " F == [ifte] ccons [P'] swons [P] swap\n", + " G == [F] dipdd\n", + "\n", + " [THEN] [ELSE] [ifte] ccons [P'] swons [P] swap [R0] [R1] foo'\n", + " [[THEN] [ELSE] ifte] [P'] swons [P] swap [R0] [R1] foo'\n", + " [[P'] [THEN] [ELSE] ifte] [P] swap [R0] [R1] foo'\n", + " [P] [[P'] [THEN] [ELSE] ifte] [R0] [R1] genrec" + ] + }, + { + "cell_type": "markdown", + "id": "82d2c37b", + "metadata": {}, + "source": [ + "Ergo:\n", + "\n", + " F == [ifte] ccons [P'] swons [P] swap\n", + " foo == [F] dipdd genrec\n", + " combine-two-lists == [i cons] foo" + ] + }, + { + "cell_type": "markdown", + "id": "30fa6616", + "metadata": {}, + "source": [ + "-and-\n", + "\n", + " add-digits' == [one-empty-list]\n", + " [both-empty]\n", + " [both-full]\n", + " combine-two-lists\n", + "\n", + " one-empty-list == ditch-empty-list add-carry-to-digits\n", + " both-empty == pop swap carry\n", + " both-full == uncons-two [add-with-carry] dipd\n", + "\n", + "This illustrates how refactoring creates denser yet more readable code." + ] + }, + { + "cell_type": "markdown", + "id": "d9308c19", + "metadata": {}, + "source": [ + "This doesn't go quite far enough, I think.\n", + "\n", + " R0 == uncons-two [add-with-carry] dipd\n", + "\n", + "I think `R0` will pretty much always do:\n", + "\n", + " uncons-two [combine-two-values] dipd\n", + "\n", + "And so it should be refactored further to something like:\n", + "\n", + " [F] R0\n", + " -------------------------\n", + " uncons-two [F] dipd\n", + "\n", + "And then `add-digits'` becomes just:\n", + "\n", + "\n", + " add-digits' == [one-empty-list]\n", + " [both-empty]\n", + " [add-with-carry]\n", + " combine-two-lists\n" + ] + }, + { + "cell_type": "markdown", + "id": "7ad6e389", + "metadata": {}, + "source": [ + "If we factor `ditch-empty-list` out of `one-empty-list`, and `pop` from `both-empty`:\n", + "\n", + " add-digits' == [add-carry-to-digits]\n", + " [swap carry]\n", + " [add-with-carry]\n", + " combine-two-lists\n" + ] + }, + { + "cell_type": "markdown", + "id": "d1a07253", + "metadata": {}, + "source": [ + "Let's figure out the new form.\n", + "\n", + "\n", + " [ONE-EMPTY] [BOTH-EMPTY] [COMBINE-VALUES] foo\n", + " ---------------------------------------------------\n", + " [P]\n", + " [\n", + " [P']\n", + " [ditch-empty-list ONE-EMPTY]\n", + " [pop BOTH-EMPTY]\n", + " ifte\n", + " ]\n", + " [uncons-two [COMBINE-VALUES] dipd]\n", + " [i cons] genrec\n", + "\n", + "eh?\n", + "\n", + "Let's not over think it.\n", + "\n", + " [ONE-EMPTY] [ditch-empty-list] swoncat [BOTH-EMPTY] [pop] swoncat [COMBINE-VALUES] \n", + " [ditch-empty-list ONE-EMPTY] [pop BOTH-EMPTY] [COMBINE-VALUES]\n", + "\n", + "With:\n", + "\n", + " [C] [A] [B] sandwich\n", + " --------------------------\n", + " [A [C] B]\n", + "\n", + "\n", + " [ditch-empty-list ONE-EMPTY] [pop BOTH-EMPTY] [COMBINE-VALUES] [uncons-two] [dipd] sandwich\n", + " [ditch-empty-list ONE-EMPTY] [pop BOTH-EMPTY] [uncons-two [COMBINE-VALUES] dipd]\n", + "\n", + "So to get from \n", + "\n", + " [A] [B] [C]\n", + "\n", + "to:\n", + "\n", + " [ditch-empty-list A] [pop B] [uncons-two [C] dipd]\n", + "\n", + "we use:\n", + "\n", + " [[[ditch-empty-list] swoncat] dip [pop] swoncat] dip [uncons-two] [dipd] sandwich\n", + "\n", + "It's gnarly, but simple:\n", + "\n", + " _foo0 == [[[ditch-empty-list] swoncat] dip [pop] swoncat] dip [uncons-two] [dipd] sandwich\n", + "\n", + "\n", + " [A] [B] [C] _foo0\n", + " [ditch-empty-list A] [pop B] [uncons-two [C] dipd]\n", + " [A'] [B'] [C']\n", + "\n", + "\n", + " [P] [[P'] [A'] [B'] ifte] [C'] [i cons] genrec\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "id": "bca290a2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[true 578 1]" + ] + } + ], + "source": [ + "[sandwich swap [cons] dip swoncat] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "id": "995209f5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[B] [A] [C]" + ] + } + ], + "source": [ + "clear [B] [A] [C]" + ] + }, + { + "cell_type": "code", + "execution_count": 92, + "id": "79be30a9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[A [B] C]" + ] + } + ], + "source": [ + "sandwich" + ] + }, + { + "cell_type": "code", + "execution_count": 93, + "id": "3f4f41a4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[A] [B] [C]" + ] + } + ], + "source": [ + "clear [A] [B] [C]" + ] + }, + { + "cell_type": "code", + "execution_count": 94, + "id": "1a24786c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[ditch-empty-list A] [pop B] [uncons-two [C] dipd]" + ] + } + ], + "source": [ + "[[[ditch-empty-list] swoncat] dip [pop] swoncat] dip [uncons-two] [dipd] sandwich" + ] + }, + { + "cell_type": "code", + "execution_count": 95, + "id": "ac572a3d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[A'] [B'] [C']" + ] + } + ], + "source": [ + "clear [A'] [B'] [C']" + ] + }, + { + "cell_type": "code", + "execution_count": 96, + "id": "6680dbed", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[P] [[P'] [A'] [B'] ifte] [C']" + ] + } + ], + "source": [ + "[\n", + "[ifte] ccons\n", + "[P'] swons \n", + "[P] swap \n", + "] dip" + ] + }, + { + "cell_type": "code", + "execution_count": 97, + "id": "0560f261", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[A] [B] [C]" + ] + } + ], + "source": [ + "clear [A] [B] [C]" + ] + }, + { + "cell_type": "code", + "execution_count": 98, + "id": "1501ec7a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[P] [[P'] [ditch-empty-list A] [pop B] ifte] [uncons-two [C] dipd]" + ] + } + ], + "source": [ + "[\n", + " [\n", + " [ditch-empty-list] swoncat\n", + " ]\n", + " dip\n", + " [pop] swoncat\n", + "]\n", + "dip\n", + "\n", + "[uncons-two] [dipd] sandwich\n", + "\n", + "[\n", + " [ifte] ccons\n", + " [P'] swons \n", + " [P] swap \n", + "]\n", + "dip" + ] + }, + { + "cell_type": "code", + "execution_count": 99, + "id": "b15cc92c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[add-carry-to-digits] [swap carry] [add-with-carry]" + ] + } + ], + "source": [ + "clear\n", + "[add-carry-to-digits]\n", + "[swap carry]\n", + "[add-with-carry]" + ] + }, + { + "cell_type": "code", + "execution_count": 100, + "id": "9373f2d1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[P] [[P'] [ditch-empty-list add-carry-to-digits] [pop swap carry] ifte] [uncons-two [add-with-carry] dipd]" + ] + } + ], + "source": [ + "[\n", + " [\n", + " [ditch-empty-list] swoncat\n", + " ]\n", + " dip\n", + " [pop] swoncat\n", + "]\n", + "dip\n", + "\n", + "[uncons-two] [dipd] sandwich\n", + "\n", + "[\n", + " [ifte] ccons\n", + " [P'] swons \n", + " [P] swap \n", + "]\n", + "dip" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ae01bac", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 101, + "id": "853d1e66", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false [3 2 1] [4 3 2]" + ] + } + ], + "source": [ + "clear\n", + "false [3 2 1] [4 3 2]" + ] + }, + { + "cell_type": "code", + "execution_count": 102, + "id": "138ddd1a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false [3 2 1] [4 3 2] [add-carry-to-digits] [swap carry] [add-with-carry]" + ] + } + ], + "source": [ + "[add-carry-to-digits]\n", + "[swap carry]\n", + "[add-with-carry]" + ] + }, + { + "cell_type": "code", + "execution_count": 103, + "id": "79d83a50", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false [3 2 1] [4 3 2] [P] [[P'] [ditch-empty-list add-carry-to-digits] [pop swap carry] ifte] [uncons-two [add-with-carry] dipd]" + ] + } + ], + "source": [ + "[\n", + " [\n", + " [ditch-empty-list] swoncat\n", + " ]\n", + " dip\n", + " [pop] swoncat\n", + "]\n", + "dip\n", + "\n", + "[uncons-two] [dipd] sandwich\n", + "\n", + "[\n", + " [ifte] ccons\n", + " [P'] swons \n", + " [P] swap \n", + "]\n", + "dip" + ] + }, + { + "cell_type": "code", + "execution_count": 104, + "id": "1acaaa2d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[7 5 3]" + ] + } + ], + "source": [ + "[i cons] genrec" + ] + }, + { + "cell_type": "markdown", + "id": "182465de", + "metadata": {}, + "source": [ + "So that's nice.\n", + "\n", + "In order to avoid the overhead of rebuilding the whole thing each time we could pre-compute the function and store it in the dictionary." + ] + }, + { + "cell_type": "markdown", + "id": "f51d940d", + "metadata": {}, + "source": [ + "\n", + " combine-two-lists == build-two-list-combiner [i cons] genrec\n", + " \n", + " build-two-list-combiner == [[[ditch-empty-list] swoncat] dip [pop] swoncat] dip [uncons-two] [dipd] sandwich [[ifte] ccons [P'] swons [P] swap ] dip\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 105, + "id": "23939b0b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear\n", + "[build-two-list-combiner [[[ditch-empty-list] swoncat] dip [pop] swoncat] dip [uncons-two] [dipd] sandwich [[ifte] ccons [P'] swons [P] swap ] dip] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 106, + "id": "4bd81381", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[P] [[P'] [ditch-empty-list add-carry-to-digits] [pop swap carry] ifte] [uncons-two [add-with-carry] dipd]" + ] + } + ], + "source": [ + "[add-carry-to-digits]\n", + "[swap carry]\n", + "[add-with-carry]\n", + "build-two-list-combiner" + ] + }, + { + "cell_type": "markdown", + "id": "2398f155", + "metadata": {}, + "source": [ + "Now grab the definition, add the `[i cons] genrec` and symbol (name) and inscribe it:" + ] + }, + { + "cell_type": "code", + "execution_count": 107, + "id": "458515b0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "[[i cons] genrec] ccons cons [add-digits'] swoncat inscribe" + ] + }, + { + "cell_type": "markdown", + "id": "d3bb85e0", + "metadata": {}, + "source": [ + "Try it out..." + ] + }, + { + "cell_type": "code", + "execution_count": 108, + "id": "dc6ee9a2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[7 5 3]" + ] + } + ], + "source": [ + "false [3 2 1] [4 3 2] add-digits'" + ] + }, + { + "cell_type": "code", + "execution_count": 109, + "id": "b90a2400", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false [7 5 3] [2147483647]" + ] + } + ], + "source": [ + "false swap base -- unit" + ] + }, + { + "cell_type": "code", + "execution_count": 110, + "id": "a9cb79ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[6 6 3]" + ] + } + ], + "source": [ + "add-digits'" + ] + }, + { + "cell_type": "code", + "execution_count": 111, + "id": "6bc5604e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" + ] + }, + { + "cell_type": "markdown", + "id": "13336d40", + "metadata": {}, + "source": [ + "### Subtracting\n", + "\n", + "Okay, we're almost ready to implement subtraction, but there's a wrinkle! When we subtract a smaller (absolute) value from a larger (absolute) value there's no problem:\n", + "\n", + " 10 - 5 = 5\n", + "\n", + "But I don't know the algorithm to subtract a larger number from a smaller one:\n", + "\n", + " 5 - 10 = ???\n", + "\n", + "The answer is -5, of course, but what's the algorithm? How to make the computer figure that out? We make use of the simple algebraic identity:\n", + "\n", + " a - b = -(b - a)\n", + "\n", + "So if we want to subtract a larger number `a` from a smaller one `b` we can instead subtract the smaller from the larger and invert the sign:\n", + "\n", + " 5 - 10 = -(10 - 5)\n", + "\n", + "To do this we need a function `gt-digits` that will tell us which of two digit lists represents the larger integer." + ] + }, + { + "cell_type": "markdown", + "id": "003c5dad", + "metadata": {}, + "source": [ + "#### `gt-digits`\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "ec4ea273", + "metadata": {}, + "source": [ + "I just realized I don't have a list length function yet!" + ] + }, + { + "cell_type": "code", + "execution_count": 112, + "id": "9c50bd24", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "[length [bool not] [pop 0] [rest] [i ++] genrec] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 113, + "id": "4c1dbabd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0" + ] + } + ], + "source": [ + "clear\n", + "[] length" + ] + }, + { + "cell_type": "code", + "execution_count": 114, + "id": "ef683ade", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4" + ] + } + ], + "source": [ + "clear\n", + "[this is a list] length" + ] + }, + { + "cell_type": "code", + "execution_count": 115, + "id": "ff52920a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[1 2 3] [4 5] 3 2" + ] + } + ], + "source": [ + "clear\n", + "[1 2 3] [4 5] over over [length] app2" + ] + }, + { + "cell_type": "code", + "execution_count": 116, + "id": "be946f53", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[4 5] [1 2 3]" + ] + } + ], + "source": [ + "[swap][6][7]cmp" + ] + }, + { + "cell_type": "markdown", + "id": "a4bd496a", + "metadata": {}, + "source": [ + "what about a function that iterates through two lists until one or the other ends, or they end at the same time (same length) and we walk back through comparing the digits?" + ] + }, + { + "cell_type": "code", + "execution_count": 117, + "id": "056e3072", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" + ] + }, + { + "cell_type": "code", + "execution_count": 118, + "id": "02d4e38e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "[1 2 3] [4 5 6] [bool]ii &" + ] + }, + { + "cell_type": "code", + "execution_count": 119, + "id": "5683eb84", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1 4] [2 5] [3 6]]" + ] + } + ], + "source": [ + "clear\n", + "[1 2 3] [4 5 6]\n", + "[[bool] ii | not]\n", + "[pop]\n", + "[uncons-two]\n", + "[i [unit cons] dip cons]\n", + "genrec" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "id": "b8a9166f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1 4] [2 5] [3 6]]" + ] + } + ], + "source": [ + "clear\n", + "[1 2 3] [4 5 6]\n", + "[[bool] ii | not]\n", + "[pop]\n", + "[uncons-two]\n", + "[i [unit cons] dip cons]\n", + "genrec" + ] + }, + { + "cell_type": "code", + "execution_count": 121, + "id": "de01b93b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" + ] + }, + { + "cell_type": "markdown", + "id": "bb580b17", + "metadata": {}, + "source": [ + "So I guess that's `zip`?\n", + "\n", + "But we want something a little different.\n", + "\n", + "It's a weird function: compare lengths, if they are the same length then compare contents pairwise from the end.\n", + "\n", + "if the first list is empty and the second list isn't then the whole function should return false\n", + "\n", + "if the first list is non-empty and the second list is empty then the whole function should return true\n", + "\n", + "if both lists are non-empty we uncons some digits for later comparison? Where to put them? Leave them on the stack? What about short-circuits?\n", + "\n", + "if both lists are empty we start comparing uncons'd pairs until we find an un-equal pair or run out of pairs.\n", + "\n", + "if we run out of pairs before we find an unequal pair then the function returns true (the numbers are identical, we should try to shortcut the actual subtraction here, but let's just get it working first, eh?)\n", + "\n", + "if we find an unequal pair we return a>b and discard the rest of the pairs. Or maybe this all happens in some sort of `infra first` situation?\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "b4c0376a", + "metadata": {}, + "source": [ + "So the predicate will be `[bool] ii & not`, if one list is longer than the other we are done.\n", + "We postulate a third list to contain the pairs:\n", + "\n", + " [] [3 2 1] [4 5 6] [P] [BASE] [R0] [R1] genrec\n", + "\n", + "The recursive branch seems simpler to figure out:\n", + "\n", + " [] [3 2 1] [4 5 6] R0 [F] R1\n", + "\n", + " uncons-two [unit cons swons] dipd [F] i\n", + "\n", + " [] [3 2 1] [4 5 6] [P] [BASE] [uncons-two [unit cons swons] dipd] tailrec" + ] + }, + { + "cell_type": "code", + "execution_count": 122, + "id": "50c2669a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + " [xR1 uncons-two [unit cons swons] dipd] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 123, + "id": "1bbf3465", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] [3 2 1] [4 5 6]" + ] + } + ], + "source": [ + "clear\n", + "\n", + "[] [3 2 1] [4 5 6]" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "id": "5a9347f6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1 6] [2 5] [3 4]] [] []" + ] + } + ], + "source": [ + "xR1 xR1 xR1" + ] + }, + { + "cell_type": "code", + "execution_count": 125, + "id": "69b18847", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear\n", + "[xP [bool] ii & not] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 126, + "id": "0b29370d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[2 4] [3 5]] [1] []" + ] + } + ], + "source": [ + "clear\n", + "\n", + "[] [3 2 1] [5 4] [xP] [] [xR1] tailrec" + ] + }, + { + "cell_type": "code", + "execution_count": 127, + "id": "32ce148e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[2 5] [3 4]] [] [1]" + ] + } + ], + "source": [ + "clear\n", + "\n", + "[] [3 2] [4 5 1] [xP] [] [xR1] tailrec" + ] + }, + { + "cell_type": "code", + "execution_count": 128, + "id": "588f44dd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[1 3] [2 4] [3 5]] [] []" + ] + } + ], + "source": [ + "clear\n", + "\n", + "[] [3 2 1] [5 4 3] [xP] [] [xR1] tailrec" + ] + }, + { + "cell_type": "markdown", + "id": "e7bcbb67", + "metadata": {}, + "source": [ + "Now comes the tricky part, that base case:\n", + "\n", + "we have three lists. The first is a possibly-empty list of pairs to compare.\n", + "\n", + "THe second two are the tails of the original lists.\n", + "\n", + "if the first list is non-empty then the second list must be empty so the whole function should return true\n", + "\n", + "if the first list is empty and the second list isn't then the whole function should return false\n", + "\n", + "\n", + "if both lists are empty we start comparing uncons'd pairs until we find an un-equal pair or run out of pairs.\n", + "\n", + "\n", + "\n", + " [bool] # if the first list is non-empty\n", + " [popop pop true]\n", + " [\n", + " [pop bool] # the second list is non-empty (the first list is empty)\n", + " [popop pop false]\n", + " [\n", + " # both lists are empty\n", + " popop\n", + " compare-pairs\n", + " ]\n", + " ifte\n", + " ]\n", + " ifte\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 129, + "id": "6c6d096e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "clear\n", + "[][][1]\n", + "\n", + "[bool]\n", + "[popop pop true]\n", + "[\n", + " [pop bool]\n", + " [popop pop false]\n", + " [popop 23 swons]\n", + " ifte\n", + "]\n", + "ifte" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "id": "04e77a5d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false" + ] + } + ], + "source": [ + "clear\n", + "[][1][]\n", + "\n", + "[bool]\n", + "[popop pop true]\n", + "[\n", + " [pop bool]\n", + " [popop pop false]\n", + " [popop 23 swons]\n", + " ifte\n", + "]\n", + "ifte" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "id": "08fb5519", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[23 1]" + ] + } + ], + "source": [ + "clear\n", + "[1][][]\n", + "\n", + "[bool]\n", + "[popop pop true]\n", + "[\n", + " [pop bool]\n", + " [popop pop false]\n", + " [popop 23 swons]\n", + " ifte\n", + "]\n", + "ifte" + ] + }, + { + "cell_type": "markdown", + "id": "91b70814", + "metadata": {}, + "source": [ + "#### `compare-pairs`\n", + "\n", + "This should be a pretty simple recursive function\n", + "\n", + " [P] [THEN] [R0] [R1] genrec\n", + "\n", + "If the list is empty we return `false`\n", + "\n", + " P == bool not\n", + " THEN == pop false\n", + "\n", + "On the recursive branch we have an `ifte` expression:\n", + "\n", + " pairs R0 [compare-pairs] R1\n", + " ---------------------------------------------------\n", + " pairs [P.rec] [THEN.rec] [compare-pairs] ifte\n", + "\n", + "We must compare the pair from the top of the list:\n", + "\n", + " P.rec == first [>] infrst" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "id": "4dbd1321", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "clear\n", + "\n", + "[[1 3] [2 4] [3 5]] first [>] infrst" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "id": "21eb7f92", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[true true true]" + ] + } + ], + "source": [ + "clear\n", + "\n", + "[[1 3] [2 4] [3 5]] [[>] infrst] map" + ] + }, + { + "cell_type": "markdown", + "id": "ed30c362", + "metadata": {}, + "source": [ + " THEN.rec == pop true" + ] + }, + { + "cell_type": "code", + "execution_count": 134, + "id": "06e0ffcb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear\n", + "\n", + "[compare-pairs\n", + " [bool not]\n", + " [pop false]\n", + " [\n", + " [first [>] infrst]\n", + " [pop true]\n", + " ]\n", + " [ifte]\n", + " genrec\n", + "] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 135, + "id": "b2428f49", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "clear [[1 3] [2 4] [3 5]] compare-pairs" + ] + }, + { + "cell_type": "code", + "execution_count": 136, + "id": "22a0f5a2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "clear [[1 3] [3 3] [3 5]] compare-pairs" + ] + }, + { + "cell_type": "markdown", + "id": "305505ea", + "metadata": {}, + "source": [ + "Whoops! I forgot to remove the already-checked pair from the list of pairs!" + ] + }, + { + "cell_type": "code", + "execution_count": 137, + "id": "2d72984f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear\n", + "\n", + "[compare-pairs\n", + " [bool not]\n", + " [pop false]\n", + " [\n", + " [first [<] infrst]\n", + " [pop true]\n", + " ]\n", + " [[rest] swoncat ifte]\n", + " genrec\n", + "] inscribe" + ] + }, + { + "cell_type": "markdown", + "id": "dee91d11", + "metadata": {}, + "source": [ + "This is clunky and inefficient but it works." + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "id": "65c7868b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "clear [[1 0] [2 2] [3 3]] compare-pairs" + ] + }, + { + "cell_type": "code", + "execution_count": 139, + "id": "402e132c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false" + ] + } + ], + "source": [ + "clear [[1 1] [2 2] [3 3]] compare-pairs" + ] + }, + { + "cell_type": "code", + "execution_count": 140, + "id": "dcc99a68", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false" + ] + } + ], + "source": [ + "clear [[1 2] [2 2] [3 3]] compare-pairs" + ] + }, + { + "cell_type": "code", + "execution_count": 141, + "id": "bfb5e62c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" + ] + }, + { + "cell_type": "code", + "execution_count": 142, + "id": "cbafd48e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "clear [[1 1] [2 1] [3 3]] compare-pairs" + ] + }, + { + "cell_type": "code", + "execution_count": 143, + "id": "54e1955a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false" + ] + } + ], + "source": [ + "clear [[1 1] [2 2] [3 3]] compare-pairs" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "id": "2a9308d7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false" + ] + } + ], + "source": [ + "clear [[1 1] [2 3] [3 3]] compare-pairs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6c1030f9", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 145, + "id": "62b77ed8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "clear\n", + "[[1 1] [2 1] [3 3]] [] []\n", + "\n", + "[bool]\n", + "[popop pop true]\n", + "[\n", + " [pop bool]\n", + " [popop pop false]\n", + " [popop compare-pairs]\n", + " ifte\n", + "]\n", + "ifte" + ] + }, + { + "cell_type": "code", + "execution_count": 146, + "id": "3a638a41", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "[BASE\n", + " [bool]\n", + " [popop pop true]\n", + " [\n", + " [pop bool]\n", + " [popop pop false]\n", + " [popop compare-pairs]\n", + " ifte\n", + " ]\n", + " ifte\n", + "] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "id": "d6ce2f51", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] [3 2 1] [4 5 6]" + ] + } + ], + "source": [ + "clear\n", + "\n", + "[] [3 2 1] [4 5 6]" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "id": "ac840996", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false" + ] + } + ], + "source": [ + "[xP] [BASE] [xR1] tailrec" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "id": "16bc3266", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] [4 5 6] [3 2 1]" + ] + } + ], + "source": [ + "clear\n", + "\n", + "[] [3 2 1] [4 5 6] swap" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "id": "7d72e659", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "[xP] [BASE] [xR1] tailrec" + ] + }, + { + "cell_type": "code", + "execution_count": 151, + "id": "0f979905", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[] [3 2 1] [3 2 1]" + ] + } + ], + "source": [ + "clear\n", + "\n", + "[] [3 2 1] dup" + ] + }, + { + "cell_type": "code", + "execution_count": 152, + "id": "3869793c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false" + ] + } + ], + "source": [ + "[xP] [BASE] [xR1] tailrec" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "id": "2cc01414", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "id": "8684e3b9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "[gt-bigint <<{} [xP] [BASE] [xR1] tailrec] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "id": "e28a82a2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false" + ] + } + ], + "source": [ + "clear [3 2 1] [4 5 6] gt-bigint" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "id": "1045f1b4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "true" + ] + } + ], + "source": [ + "clear [3 2 1] [4 5 6] swap gt-bigint" + ] + }, + { + "cell_type": "code", + "execution_count": 159, + "id": "27f5aa22", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false" + ] + } + ], + "source": [ + "clear [3 2 1] dup gt-bigint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12fdc27a", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 160, + "id": "3c808285", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[3 2 1] [4 5 6]" + ] + } + ], + "source": [ + "clear [3 2 1] [4 5 6] [gt-bigint] [swap] [] ifte" + ] + }, + { + "cell_type": "code", + "execution_count": 161, + "id": "01cf41eb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[3 2 1] [4 5 6]" + ] + } + ], + "source": [ + "clear [4 5 6] [3 2 1] [gt-bigint] [swap] [] ifte" + ] + }, + { + "cell_type": "markdown", + "id": "f0404217", + "metadata": {}, + "source": [ + "And so it goes.\n", + "\n", + "Now we can subtract, we just have to remember to invert the sign bit if we swap the digit lists.\n", + "\n", + "Maybe something like:\n", + "\n", + " check-gt == [gt-bigint] [swap true rollup] [false rollup] ifte\n", + "\n", + "To keep the decision around as a Boolean flag? We can `xor` it with the sign bit?" + ] + }, + { + "cell_type": "code", + "execution_count": 171, + "id": "2f87357e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [] + } + ], + "source": [ + "clear" + ] + }, + { + "cell_type": "markdown", + "id": "04f07811", + "metadata": {}, + "source": [ + "### Subtraction, at last...\n", + "\n", + "So now that we can compare digit lists to see if one is larger than the other we can subtract (inverting the sign if necessary) much like we did addition:\n", + "\n", + " sub-bigints == [same-sign] [sub-like-bigints] [1 0 /] ifte\n", + "\n", + " sub-like-bigints == [uncons] dip rest sub-digits cons\n", + " ^\n", + " |\n", + "\n", + "At this point we would have the sign bit then the two digit lists.\n", + "\n", + " sign [c b a] [z y x]\n", + "\n", + "We want to use `check-gt` here:\n", + "\n", + " sign [c b a] [z y x] check-gt\n", + " sign swapped? [c b a] [z y x] check-gt\n", + "\n", + "It seems we should just flip the sign bit if we swap, eh?\n", + "\n", + " check-gt == [gt-bigint] [swap [not] dipd] [] ifte\n", + "\n", + "Now we subtract the digits:\n", + "\n", + " sign [c b a] [z y x] sub-digits cons\n", + "\n", + "So:\n", + "\n", + " sub-like-bigints == [uncons] dip rest check-gt sub-digits cons\n", + "\n", + " sub-digits == initial-carry sub-digits'\n", + " sub-digits' ==\n", + " [sub-carry-from-digits]\n", + " [swap sub-carry]\n", + " [sub-with-carry]\n", + " build-two-list-combiner\n", + " [i cons] genrec\n", + "\n", + "We just need to define the pieces.\n" + ] + }, + { + "cell_type": "markdown", + "id": "ffb9f421", + "metadata": {}, + "source": [ + " sub-carry-from-digits\n", + "\n", + "Should be easy to make modeled on\n", + "\n", + " add-carry-to-digits\n", + "\n", + "I'll do it in the morning, and if my logic is correct and I haven't bungled typing it in, the whole thing should just work! OMG? Tomorrow..." + ] + }, + { + "cell_type": "markdown", + "id": "dbd7e544", + "metadata": {}, + "source": [ + " sub-carry == pop\n", + "\n", + "We know we will never be subtracting a larger (absolute) number from a smaller (absolute) number (they might be equal) so the carry flag will never be true *at the end of a digit list subtraction.*" + ] + }, + { + "cell_type": "markdown", + "id": "c2634788", + "metadata": {}, + "source": [ + " carry a b sub-with-carry\n", + " ------------------------------\n", + " (a-b-carry) new-carry\n", + "\n", + " _sub-with-carry0 ≡ [bool_to_int] dipd - -\n", + " _sub-with-carry1 ≡ [base + base mod] [0 <] clop\n", + "\n", + " sub-with-carry ≡ _sub-with-carry0 _sub-with-carry1\n" + ] + }, + { + "cell_type": "code", + "execution_count": 182, + "id": "5e1a2bd3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "-10" + ] + } + ], + "source": [ + "[_sub-with-carry0 rolldown bool_to_int [-] ii] inscribe\n", + "[_sub-with-carry1 [base + base mod] [0 <] cleave] inscribe\n", + "[sub-with-carry _sub-with-carry0 _sub-with-carry1] inscribe" + ] + }, + { + "cell_type": "code", + "execution_count": 193, + "id": "11537a57", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "false 23 2147483647" + ] + } + ], + "source": [ + "clear false 23 base --" + ] + }, + { + "cell_type": "code", + "execution_count": 194, + "id": "1c5e8fae", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "24 true" + ] + } + ], + "source": [ + "sub-with-carry" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "490548dc", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "89687215", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a60f97f8", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5d4412e6", + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "markdown", "id": "f2b707c1", @@ -2197,6 +4532,37 @@ " add-like-bigints [uncons] dip rest add-digits cons\n", " add-bigints [same-sign] [add-like-bigints] [1 0 /] ifte" ] + }, + { + "cell_type": "markdown", + "id": "55944c2a", + "metadata": {}, + "source": [ + "### notes\n", + "\n", + "So far I have three formats for Joy source:\n", + "\n", + "- `def.txt` is a list of definitions (UTF-8), one per line, with no special marks (like above.)\n", + "- `foo ≡ bar baz...` lines in the `joy.py` embedded definition text, because why not? (Sometimes I use `==` instead of `≡` mostly because some tools can't handle the Unicode glyph. Like converting this notebook to PDF via LaTeX just omitted them.)\n", + "- `[name body] inscribe` Joy source code that literally defines new words in the dictionary at runtime. A text of those commands can be fed to the interpreter to customize it without any special processing (like the other two formats require.)\n", + "\n", + "So far I prefer the `def.txt` style but that makes it tricky to embed them automatically into the `joy.py` file.\n", + "\n", + "#### Refactoring\n", + "\n", + "We have `i cons` but that's pretty tight already, eh?\n", + "\n", + "However, `[i cons] genrec` is an interesting combinator. It's almost `tailrec` with that `i` combinator for the recursion, but then `cons` means it's a list-builder (an *anamorphism* if you go for that sort of thing.)\n", + "\n", + " simple-list-builder == [i cons] genrec\n", + "\n", + "And maybe:\n", + "\n", + " boolii == [bool] ii\n", + "\n", + " both? == boolii &\n", + " one-of? == boolii |\n" + ] } ], "metadata": {