Thun/docs/html/notebooks/BigInts.html

1639 lines
55 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>BigNums in Joy</title>
<link rel="stylesheet" href="/css/fonts.css">
<link rel="stylesheet" href="/css/site.css">
</head>
<body>
<h1>BigNums in Joy</h1>
<p>Most of the implementations of Thun support
<a href="https://en.wikipedia.org/wiki/BigNum">BigNums</a>, either built-in or as
libraries, but some host languages and systems do not. In those cases it
would be well to have a pure-Joy implementation.</p>
<p>We can model bignums as a pair of a Boolean value for the sign and a list
of integers for the digits. The bool will be the first item on a list
followed by zero or more integer digits, with the Least Significant digit
at the top (closest to the head of the list.) E.g.:</p>
<pre><code>[true 1]
</code></pre>
<p>Our <em>base</em> for the digits will be dictated by the size of the integers
supported by the host system. Let's imagine we're using 32-bit signed
ints, so our base will be not 10, but 2³¹. (We're ignoring the sign
bit.)</p>
<pre><code>joy? 2 31 pow
2147483648
</code></pre>
<p>So our digits are not 0..9, but 0..2147483647</p>
<h3><code>base</code></h3>
<p>We can <code>inscribe</code> a constant function <code>base</code> to keep this value handy.</p>
<pre><code>2147483648
joy? unit [base] swoncat
[base 2147483648]
joy? inscribe
</code></pre>
<p>It's a little "wrong" to use the
dictionary to store values like this, however, this is how Forth does it
and if your design is good it works fine. Just be careful, and wash
your hands afterward.</p>
<p>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 <code>base</code> to 10.</p>
<pre><code>[base 10] inscribe
</code></pre>
<h2>Converting Between Host BigNums and Joy BigNums</h2>
<p>We will work with one of the Joy interpreters that has bignums already so
we can convert "native" ints to our Joy bignums and vice versa. This
will be helpful to check our work. Later we can deal with converting to
and from strings (which this Joy doesn't have anyway, so it's probably
fine to defer.)</p>
<p>To get the sign bool we can just use <code>!-</code> ("not negative") and to get the
list of digits we repeatedly <code>divmod</code> the number by our <code>base</code>:</p>
<h3><code>moddiv</code></h3>
<p>We will want the results in the opposite order, so let's define a little
helper function to do that:</p>
<pre><code>[moddiv divmod swap] inscribe
</code></pre>
<h3><code>get-digit</code></h3>
<pre><code>[get-digit base moddiv] inscribe
</code></pre>
<p>We keep it up until we get to zero. This suggests a <code>while</code> loop:</p>
<pre><code>[0 &gt;] [get-digit] while
</code></pre>
<p>Let's try it:</p>
<pre><code>joy? 1234567890123456789012345678901234567890
1234567890123456789012345678901234567890
joy? [0 &gt;] [get-digit] while
1312754386 1501085485 57659106 105448366 58 0
</code></pre>
<p>We need to <code>pop</code> at the end to ditch that zero.</p>
<pre><code>[0 &gt;] [get-digit] while pop
</code></pre>
<p>But we want these numbers in a list. The naive way using <code>infra</code>
generates them in the reverse order of what we would like.</p>
<pre><code>joy? [1234567890123456789012345678901234567890]
[1234567890123456789012345678901234567890]
joy? [[0 &gt;] [get-digit] while pop]
[1234567890123456789012345678901234567890] [[0 &gt;] [get-digit] while pop]
joy? infra
[58 105448366 57659106 1501085485 1312754386]
</code></pre>
<p>We could just reverse the list, but it's more efficient to build the
result list in the order we want. We construct a simple recursive
function. (TODO: link to the recursion combinators notebook.)</p>
<p>The predicate will check that our number is yet positive:</p>
<pre><code>[0 &lt;=]
</code></pre>
<p>When we find the zero we will discard it and start a list:</p>
<pre><code>[pop []]
</code></pre>
<p>But until we do find the zero, get digits:</p>
<pre><code>[get-digit]
</code></pre>
<p>Once we have found all the digits and ditched the zero and put our
initial empty list on the stack we <code>cons</code> up the digits we have found:</p>
<pre><code>[i cons] genrec
</code></pre>
<p>Let's try it:</p>
<pre><code>joy? 1234567890123456789012345678901234567890
1234567890123456789012345678901234567890
joy? [0 &lt;=] [pop []] [get-digit] [i cons] genrec
[1312754386 1501085485 57659106 105448366 58]
</code></pre>
<p>Okay.</p>
<h3>Representing Zero</h3>
<p>This will return the empty list for zero:</p>
<pre><code>joy? 0 [0 &lt;=] [pop []] [get-digit] [i cons] genrec
[]
</code></pre>
<p>I think this is better than returning <code>[0]</code> because that amounts to a
single leading zero.</p>
<pre><code>[true] is "0"
[true 0] is "00"
</code></pre>
<p>Eh?</p>
<h3><code>digitalize</code></h3>
<p>Let's <code>inscribe</code> this function under the name <code>digitalize</code>:</p>
<pre><code>[digitalize [0 &lt;=] [pop []] [get-digit] [i cons] genrec] inscribe
</code></pre>
<p>Putting it all together we have <code>!-</code> for the sign and <code>abs digitalize</code>
for the digits, followed by <code>cons</code>:</p>
<pre><code>[!-] [abs digitalize] cleave cons
</code></pre>
<h3><code>to-bignum</code></h3>
<pre><code>[to-bignum [!-] [abs digitalize] cleave cons] inscribe
</code></pre>
<h3>Converting from Joy BigNums to Host BigNums</h3>
<p>To convert a bignum into a host integer we need to keep a "power" value
on the stack, setting it up and discarding it at the end, as well as an
accumulator value starting at zero. We will deal with the sign bit later.</p>
<pre><code>rest 1 0 rolldown
</code></pre>
<p>So the problem is to derive:</p>
<pre><code> 1 0 [digits...] [F] step
------------------------------
result
</code></pre>
<p>Where <code>F</code> is:</p>
<pre><code> power acc digit F
---------------------------------------
(power*base) (acc + (power*digit)
</code></pre>
<p>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:</p>
<pre><code>[G] [H] clop popdd
</code></pre>
<p>(Then I noticed that <code>power *</code> is a sub-function of both <code>G</code> and <code>H</code>, but
let's not overthink it, eh?)</p>
<p>So for the first result (the next power) we want:</p>
<pre><code>G == popop base *
</code></pre>
<p>And for the result:</p>
<pre><code>H == rolldown * +
</code></pre>
<h3><code>add-digit</code></h3>
<p>Let's call this <code>add-digit</code>:</p>
<pre><code>[add-digit [popop base *] [rolldown * +] clop popdd] inscribe
</code></pre>
<p>Try it out:</p>
<pre><code>[true 1312754386 1501085485 57659106 105448366 58]
joy? rest 1 0 rolldown
1 0 [1312754386 1501085485 57659106 105448366 58]
joy? [add-digit] step
45671926166590716193865151022383844364247891968 1234567890123456789012345678901234567890
joy? popd
1234567890123456789012345678901234567890
</code></pre>
<h3><code>from-bignum</code></h3>
<pre><code>[from-bignum rest 1 0 rolldown [add-digit] step popd] inscribe
</code></pre>
<p>Try it out:</p>
<pre><code>joy? 1234567890123456789012345678901234567890 to-bignum
[true 1312754386 1501085485 57659106 105448366 58]
joy? from-bignum
1234567890123456789012345678901234567890
</code></pre>
<p>Not bad.</p>
<h3>What about that sign bit?</h3>
<p>Time to deal with that.</p>
<p>Consider a Joy bignum:</p>
<pre><code>[true 1312754386 1501085485 57659106 105448366 58]
</code></pre>
<p>To get the sign bit would just be <code>first</code>.</p>
<pre><code>[true 1312754386 1501085485 57659106 105448366 58]
joy? [from-bignum] [first] cleave
1234567890123456789012345678901234567890 true
</code></pre>
<p>Then use the sign flag to negate the int if the bignum was negative:</p>
<pre><code>[neg] [] branch
</code></pre>
<h3><code>from-bignum</code></h3>
<p>This gives:</p>
<pre><code>[from-bignum [from-bignum] [first] cleave [neg] [] branch] inscribe
</code></pre>
<h2>Our Source Code So Far</h2>
<pre><code>[base 2147483648] inscribe
[moddiv divmod swap] inscribe
[get-digit base moddiv] inscribe
[digitalize [0 &lt;=] [pop []] [get-digit] [i cons] genrec] inscribe
[to-bignum [!-] [abs digitalize] cleave cons] inscribe
[add-digit [popop base *] [rolldown * +] clop popdd] inscribe
[from-bignum.prep rest 1 0 rolldown] inscribe
[from-bignum from-bignum.prep [add-digit] step popd] inscribe
[from-bignum [from-bignum] [first] cleave [neg] [] branch] inscribe
</code></pre>
<h2>Addition of Like Signs</h2>
<h3><code>add-digits</code></h3>
<p>Let's figure out how to add two lists of digits. We will assume that the
signs are the same (both lists of digits represent numbers of the same
sign, both positive or both negative.) We're going to want a recursive
function, of course, but it's not quite a standard <em>hylomorphism</em> for (at
least) two reasons:</p>
<ul>
<li>We're tearing down two lists simultaneously.</li>
<li>They might not be the same length.</li>
</ul>
<p>There are two base cases: two empty lists or one empty list, the
recursive branch is taken only if both lists are non-empty.</p>
<p>We will also need an inital <code>false</code> value for a carry flag. This implies
the following structure:</p>
<pre><code>false rollup [add-digits.P] [add-digits.THEN] [add-digits.R0] [add-digits.R1] genrec
</code></pre>
<h3>The predicate</h3>
<p>The situation will be like this, a Boolean flag followed by two lists of
digits:</p>
<pre><code>bool [a ...] [b ...] add-digits.P
</code></pre>
<p>The predicate must evaluate to <code>false</code> <em>iff</em> both lists are non-<code>null</code>:</p>
<pre><code>add-digits.P == [null] ii \/
</code></pre>
<h3>The base cases</h3>
<p>On the non-recursive branch of the <code>genrec</code> we have to decide between
three cases, but because addition is commutative we can lump together the
first two:</p>
<pre><code>bool [] [b ...] add-digits.THEN
bool [a ...] [] add-digits.THEN
bool [] [] add-digits.THEN
</code></pre>
<p>So we have an <code>ifte</code> expression:</p>
<pre><code>add-digits.THEN == [add-digits.THEN.P] [add-digits.THEN.THEN] [add-digits.THEN.ELSE] ifte
</code></pre>
<p>Let's define the predicate:</p>
<pre><code>add-digits.THEN.P == [null] ii /\
</code></pre>
<p>So <code>add-digits.THEN.THEN</code> deals with the case of both lists being empty,
and the <code>add-digits.THEN.ELSE</code> branch deals with one list of digits being
longer than the other.</p>
<h3>One list empty</h3>
<p>In the cases where one of the two lists (but not both) is empty:</p>
<pre><code>carry [a ...] [] add-digits.THEN.ELSE
carry [] [b ...] add-digits.THEN.ELSE
</code></pre>
<p>We first get rid of the empty list:</p>
<pre><code>[null] [pop] [popd] ifte
</code></pre>
<h3><code>ditch-empty-list</code></h3>
<pre><code>[ditch-empty-list [null] [pop] [popd] ifte] inscribe
add-digits.THEN.ELSE == ditch-empty-list add-digits.THEN.ELSE
</code></pre>
<p>Now we have:</p>
<pre><code>carry [n ...] add-digits.THEN.ELSE
</code></pre>
<p>This is just <code>add-carry-to-digits</code> which we will derive in a moment, but
first a side-quest...</p>
<h3><code>add-with-carry</code></h3>
<p>To get ahead of ourselves a bit, we will want some function
<code>add-with-carry</code> that accepts a bool and two ints and leaves behind a new
int and a new Boolean carry flag. With some abuse of notation we can
treat bools as ints (type punning as in Python) and write:</p>
<pre><code> carry a b add-with-carry
---------------------------------
(a+b+carry) carry
</code></pre>
<p>(I find it interesting that this function accepts the carry from below
the int args but returns it above the result. Hmm...)</p>
<h3><code>bool-to-int</code></h3>
<pre><code>[bool-to-int [0] [1] branch] inscribe
</code></pre>
<p>We can use this function to convert the carry flag to an integer and then
add it to the sum of the two digits:</p>
<pre><code>[bool-to-int] dipd + +
</code></pre>
<p>So the first part of <code>add-with-carry</code> is <code>[bool-to-int] dipd + +</code> to get
the total, then we need to do <code>base mod</code> to get the new digit and <code>base &gt;=</code>
to get the new carry flag. Factoring give us:</p>
<pre><code>base [mod] [&gt;=] clop
</code></pre>
<p>Put it all together and we have:</p>
<pre><code>[add-with-carry.0 [bool-to-int] dipd + +] inscribe
[add-with-carry.1 base [mod] [&gt;=] clop] inscribe
[add-with-carry add-with-carry.0 add-with-carry.1] inscribe
</code></pre>
<h3>Now back to <code>add-carry-to-digits</code></h3>
<p>This should be a very simple recursive function. It accepts a Boolean
<code>carry</code> flag and a non-empty list of digits (the list is only going to be
non-empty on the first iteration, after that we have to check it
ourselves because we may have emptied it of digits and still have a
<code>true</code> <code>carry</code> flag) and it returns a list of digits, consuming the carry
flag.</p>
<pre><code>add-carry-to-digits == [actd.P] [actd.THEN] [actd.R0] [actd.R1] genrec
</code></pre>
<p>The predicate is the carry flag itself inverted:</p>
<pre><code>actd.P == pop not
</code></pre>
<p>The base case simply discards the carry flag:</p>
<pre><code>actd.THEN == popd
</code></pre>
<p>So:</p>
<pre><code>add-carry-to-digits == [pop not] [popd] [actd.R0] [actd.R1] genrec
</code></pre>
<p>That leaves the recursive branch:</p>
<pre><code>true [n ...] actd.R0 [add-carry-to-digits] actd.R1
</code></pre>
<p>-or-</p>
<pre><code>true [] actd.R0 [add-carry-to-digits] actd.R1
</code></pre>
<p>We know that the Boolean value is <code>true</code>. We also know that the list will
be non-empty, but only on the first iteration of the <code>genrec</code>. It may be
that the list is empty on a later iteration.</p>
<p>The <code>actd.R0</code> function should check the list.</p>
<pre><code>actd.R0 == [null] [actd.R0.THEN] [actd.R0.ELSE] ifte
</code></pre>
<h3>If it's empty...</h3>
<pre><code> true [] actd.R0.THEN [add-carry-to-digits] actd.R1
--------------------------------------------------------
1 false [] [add-carry-to-digits] i cons
</code></pre>
<p>What we're seeing here is that <code>actd.R0.THEN</code> leaves the empty list of
digits on the stack, converts the carry flag to <code>false</code> and leave 1 on
the stack to be picked up by <code>actd.R1</code> and <code>cons</code>'d onto the list of
digits (e.g.: 999 -&gt; 1000, it's the new 1.)</p>
<p>This implies:</p>
<pre><code>actd.R1 == i cons
</code></pre>
<p>And:</p>
<pre><code>actd.R0.THEN == popd 1 false rolldown
</code></pre>
<p>We have the results in this order <code>1 false []</code> rather than some other
arrangement to be compatible (same types and order) with the result of
the other branch, which we now derive.</p>
<h3>If the list of digits isn't empty...</h3>
<p>With <code>actd.R1 == i cons</code> as above we have:</p>
<pre><code>true [a ...] actd.R0.ELSE [add-carry-to-digits] i cons
</code></pre>
<p>We want to get out that <code>a</code> value and use <code>add-with-carry</code> here:</p>
<pre><code> true 0 a add-with-carry [...] [add-carry-to-digits] i cons
----------------------------------------------------------------
(a+1) carry [...] [add-carry-to-digits] i cons
</code></pre>
<p>This leaves behind the new digit (a+1) for <code>actd.R1</code> and the new carry
flag for the next iteration.</p>
<p>So here is the specification of <code>actd.R0.ELSE</code>:</p>
<pre><code> true [a ...] actd.R0.ELSE
-----------------------------------
true 0 a add-with-carry [...]
</code></pre>
<p>It accepts a Boolean value and a non-empty list on the stack and is
responsible for <code>uncons</code>'ing <code>a</code> and <code>add-with-carry</code> and the initial 0:</p>
<pre><code> true [a ...] . 0 swap
true 0 [a ...] . uncons
true 0 a [...] . [add-with-carry] dip
true 0 a add-with-carry [...] .
</code></pre>
<h3><code>actd.R0.ELSE</code></h3>
<pre><code>[actd.R0.ELSE 0 swap uncons [add-with-carry] dip] inscribe
</code></pre>
<p>Putting it all together:</p>
<pre><code>[bool-to-int [0] [1] branch] inscribe
[ditch-empty-list [null] [pop] [popd] ifte] inscribe
[add-with-carry.0 [bool-to-int] dipd + +] inscribe
[add-with-carry.1 base [mod] [&gt;=] clop] inscribe
[add-with-carry add-with-carry.0 add-with-carry.1] inscribe
[actd.R0.THEN popd 1 false rolldown] inscribe
[actd.R0.ELSE 0 swap uncons [add-with-carry] dip] inscribe
[actd.R0 [null] [actd.R0.THEN] [actd.R0.ELSE] ifte] inscribe
[add-carry-to-digits [pop not] [popd] [actd.R0] [i cons] genrec] inscribe
</code></pre>
<p>We can set <code>base</code> to 10 to see it in action with familiar decimal digits:</p>
<pre><code>joy? [base 10] inscribe
</code></pre>
<p>Let's add a carry to 999:</p>
<pre><code>joy? true [9 9 9]
true [9 9 9]
joy? add-carry-to-digits
[0 0 0 1]
</code></pre>
<p>Not bad! Recall that our digits are stored in with the Most Significant
Digit at the bottom of the list.</p>
<p>Let's add another carry:</p>
<pre><code>joy? true swap
true [0 0 0 1]
joy? add-carry-to-digits
[1 0 0 1]
</code></pre>
<p>What if we make the just the first digit into 9?</p>
<pre><code>joy? 9 swons
[9 1 0 0 1]
joy? true swap
true [9 1 0 0 1]
joy? add-carry-to-digits
[0 2 0 0 1]
</code></pre>
<p>Excellent!</p>
<p>And adding <code>false</code> does nothing, yes?</p>
<pre><code>joy? false swap
false [0 2 0 0 1]
joy? add-carry-to-digits
[0 2 0 0 1]
</code></pre>
<p>Wonderful!</p>
<p>So that handles the cases where one of the two lists (but not both) is
empty.</p>
<pre><code>add-digits.THEN.ELSE == ditch-empty-list add-carry-to-digits
</code></pre>
<h3>Both lists empty</h3>
<p>If both lists are empty we discard one list and check the carry to
determine our result as described above:</p>
<pre><code>bool [] [] add-digits.THEN.THEN
</code></pre>
<p>Simple enough:</p>
<pre><code>bool [] [] . pop
bool [] . swap
[] bool . [] [1 swons] branch
</code></pre>
<p>True branch:</p>
<pre><code>[] true . [] [1 swons] branch
[] .
</code></pre>
<p>False branch:</p>
<pre><code>[] false . [] [1 swons] branch
[] . 1 swons
[1] .
</code></pre>
<p>So:</p>
<pre><code>add-digits.THEN.THEN == pop swap [] [1 swons] branch
</code></pre>
<p>Here are the definitions, ready to <code>inscribe</code>:</p>
<pre><code>[add-digits.THEN.THEN pop swap [] [1 swons] branch] inscribe
[add-digits.THEN.ELSE ditch-empty-list add-carry-to-digits] inscribe
[add-digits.THEN [[null] ii /\] [add-digits.THEN.THEN] [add-digits.THEN.ELSE] ifte] inscribe
</code></pre>
<h2>And recur...</h2>
<p>Now we go back and derive the recursive branch that is taken only if both
lists are non-empty.</p>
<pre><code>bool [a ...] [b ...] add-digits.R0 [add-digits] add-digits.R1
</code></pre>
<p>We just need to knock out those recursive branch functions
<code>add-digits.R0</code> and <code>add-digits.R1</code> and we're done.</p>
<p>First we will want to <code>uncons</code> the digits. Let's write a function that
just does that:</p>
<pre><code>[uncons] ii swapd
</code></pre>
<p>Try it:</p>
<pre><code>joy? [1 2 3] [4 5 6]
[1 2 3] [4 5 6]
joy? [uncons] ii swapd
1 4 [2 3] [5 6]
</code></pre>
<h3><code>uncons-two</code></h3>
<p>We could call this <code>uncons-two</code>:</p>
<pre><code>[uncons-two [uncons] ii swapd] inscribe
</code></pre>
<p>This brings us to:</p>
<pre><code>bool a b [...] [...] add-digits.R0 [add-digits] add-digits.R1
</code></pre>
<p>It's at this point that we'll want to employ the <code>add-with-carry</code>
function:</p>
<pre><code>bool a b [...] [...] [add-with-carry] dipd add-digits.R0″ [add-digits'] add-digits.R1
bool a b add-with-carry [...] [...] add-digits.R0″ [add-digits'] add-digits.R1
(a+b) bool [...] [...] add-digits.R0″ [add-digits'] add-digits.R1
</code></pre>
<p>If we postulate a <code>cons</code> in our <code>add-digits.R1</code> function...</p>
<pre><code>(a+b) bool [...] [...] add-digits.R0″ [add-digits'] i cons
</code></pre>
<p>Then it seems like we're done? <code>add-digits.R0″</code> is nothing?</p>
<pre><code>add-digits.R0 == uncons-two [add-with-carry] dipd
add-digits.R1 == i cons
</code></pre>
<h3><code>add-digits</code></h3>
<pre><code>add-digits == false rollup [add-digits.P] [add-digits.THEN] [add-digits.R0] [i cons] genrec
</code></pre>
<p>The source code so far is now:</p>
<pre><code>[bool-to-int [0] [1] branch] inscribe
[ditch-empty-list [null] [pop] [popd] ifte] inscribe
[uncons-two [uncons] ii swapd] inscribe
[add-with-carry.0 [bool-to-int] dipd + +] inscribe
[add-with-carry.1 base [mod] [&gt;=] clop] inscribe
[add-with-carry add-with-carry.0 add-with-carry.1] inscribe
[actd.R0.THEN popd 1 false rolldown] inscribe
[actd.R0.ELSE 0 swap uncons [add-with-carry] dip] inscribe
[actd.R0 [null] [actd.R0.THEN] [actd.R0.ELSE] ifte] inscribe
[add-carry-to-digits [pop not] [popd] [actd.R0] [i cons] genrec] inscribe
[add-digits.R0 uncons-two [add-with-carry] dipd] inscribe
[add-digits.THEN.THEN pop swap [] [1 swons] branch] inscribe
[add-digits.THEN.ELSE ditch-empty-list add-carry-to-digits] inscribe
[add-digits.THEN [[null] ii /\] [add-digits.THEN.THEN] [add-digits.THEN.ELSE] ifte] inscribe
[add-digits [[null] ii \/] [add-digits.THEN] [add-digits.R0] [i cons] genrec] inscribe
[add-digits false rollup add-digits] inscribe
</code></pre>
<p>Let's set <code>base</code> to 10 and try it out:</p>
<pre><code>joy? [base 10] inscribe
joy? 12345 to-bignum
[true 5 4 3 2 1]
joy? rest
[5 4 3 2 1]
joy? 999 to-bignum
[5 4 3 2 1] [true 9 9 9]
joy? rest
[5 4 3 2 1] [9 9 9]
joy? add-digits
[4 4 3 3 1]
joy? true swons
[true 4 4 3 3 1]
joy? from-bignum
13344
joy? 12345 999 +
13344 13344
</code></pre>
<p>Neat!</p>
<h3><code>add-bignums</code></h3>
<p>There is one more thing we have to do to use this: we have to deal with
the signs.</p>
<pre><code>add-bignums [add-bignums.P] [add-bignums.THEN] [add-bignums.ELSE] ifte
</code></pre>
<p>To check are they the same sign?</p>
<p>With:</p>
<pre><code>[xor [] [not] branch] inscribe
[nxor xor not] inscribe
</code></pre>
<p>We have:</p>
<pre><code>add-bignums.P == [first] ii nxor
</code></pre>
<p>If they are the same sign (both positive or both negative) we can use
<code>uncons</code> to keep one of the sign Boolean flags around and reuse it at the
end, and <code>rest</code> to discard the other, then <code>add-digits</code> to add the
digits, then <code>cons</code> that flag we saved onto the result digits list:</p>
<pre><code>add-bignums.THEN == [uncons] dip rest add-digits cons
</code></pre>
<p>If they are not both positive or both negative then we negate one of them
and subtract instead (adding unlikes is actually subtraction):</p>
<pre><code>add-bignums.ELSE == neg-bignum sub-bignums
</code></pre>
<p>So here we go:</p>
<pre><code>[same-sign [first] ii xor not] inscribe
[add-like-bignums [uncons] dip rest add-digits cons] inscribe
[add-bignums [same-sign] [add-like-bignums] [neg-bignum sub-bignums] ifte] inscribe
</code></pre>
<p>But we haven't implemented <code>neg-bignum</code> or <code>sub-bignums</code> yet...</p>
<p>We'll get to those in a moment, but first an interlude.</p>
<h2>Interlude: <code>list-combiner</code></h2>
<p>Let's review the form of our function <code>add-digits</code> (eliding the preamble
<code>false rollup</code>) and <code>add-digits.THEN</code>:</p>
<pre><code>add-digits == [add-digits.P] [add-digits.THEN] [add-digits.R0] [add-digits.R1] genrec
add-digits.THEN == [add-digits.THEN.P] [add-digits.THEN.THEN] [add-digits.THEN.ELSE] ifte
</code></pre>
<p>Recall also:</p>
<pre><code> add-digits.P == [null] ii \/
add-digits.THEN.P == [null] ii /\
</code></pre>
<p>Generalizing the names:</p>
<pre><code> F == [P] [THEN] [R0] [R1] genrec
THEN == [THEN.P] [THEN.THEN] [THEN.ELSE] ifte
</code></pre>
<p>With auxiliary definitions:</p>
<pre><code>null-two == [null] ii
both-null == null-two /\
either-or-both-null == null-two \/
</code></pre>
<p>Rename predicates:</p>
<pre><code> F == [either-or-both-null] [THEN] [R0] [R1] genrec
THEN == [both-null] [THEN.THEN] [THEN.ELSE] ifte
</code></pre>
<p>Substitute <code>THEN</code>:</p>
<pre><code> F == [either-or-both-null] [[both-null] [THEN.THEN] [THEN.ELSE] ifte] [R0] [R1] genrec
</code></pre>
<p>This is a little awkward, so let's pretend that we have a new combinator
<code>two-list-genrec</code> that accepts four quotes and does <code>F</code>:</p>
<pre><code>F == [THEN.THEN] [THEN.ELSE] [R0] [R1] two-list-genrec
</code></pre>
<p>So <code>THEN.THEN</code> handles the (non-recursive) case of both lists being
empty, <code>THEN.ELSE</code> handles the (non-recursive) case of one or the other
list being empty, and <code>R0 [F] R1</code> handles the (recursive) case of both
lists being non-empty.</p>
<p>Recall that our <code>R1</code> is just <code>i cons</code>, we can fold that in to the
definition of another new combinator that combines two lists into one:</p>
<pre><code>list-combiner-genrec == [i cons] two-list-genrec
</code></pre>
<p>So:</p>
<pre><code>F == [both-empty] [one-empty] [both-non-empty] list-combiner-genrec
</code></pre>
<p>Then for <code>add-digits</code> we would have:</p>
<pre><code> both-empty == pop swap [] [1 swons] branch
one-empty == ditch-empty-list add-carry-to-digits
both-non-empty == uncons-two [add-with-carry] dipd
add-digits == [both-empty] [one-empty] [both-non-empty] list-combiner-genrec
</code></pre>
<p>Which would expand into:</p>
<pre><code>add-digits == [either-or-both-null]
[[both-null] [both-empty] [one-empty] ifte]
[both-non-empty]
[i cons]
genrec
</code></pre>
<p>It's pretty straight forward to make a functions that converts the three
quotes into the expanded form (a kind of "macro") but you might want to
separate that from the actual <code>genrec</code> evaluation. It would be better to
run the "macro" once, append the <code>[genrec]</code> quote to the resulting form,
and <code>inscribe</code> that, rather than putting the "macro" into the definition.
That way you avoid re-evaluating the "macro" on each iteration.</p>
<p>The simplification of the expanded form to the simpler version by coining
the <code>list-combiner-genrec</code> function is the "semantic compression" aspect
of factoring. If you choose your seams and names well, the code is
(relatively) self-descriptive.</p>
<p>In any event, now that we know what's going on, we don't actually need
the "macro", we can just write out the expanded version directly.</p>
<p>Source code:</p>
<pre><code>[null-two [null] ii] inscribe
[both-null null-two /\] inscribe
[either-or-both-null null-two \/] inscribe
[add-digits.both-empty pop swap [] [1 swons] branch] inscribe
[add-digits.one-empty ditch-empty-list add-carry-to-digits] inscribe
[add-digits.both-non-empty uncons-two [add-with-carry] dipd] inscribe
[add-digits [either-or-both-null] [[both-null] [add-digits.both-empty] [add-digits.one-empty] ifte] [add-digits.both-non-empty] [i cons] genrec] inscribe
</code></pre>
<h1>===================================================================================</h1>
<h2><code>neg-bignum</code></h2>
<p>Well, that was fun! And we'll reuse it in a moment when we derive <code>sub-bignums</code>.
But for now, let's clear our palate with a nice simple function: <code>neg-bignum</code>.</p>
<p>To negate a Joy bignum you just invert the Boolean value at the head of the list.</p>
<pre><code>neg-bignum == [not] infra
</code></pre>
<h2>Subtraction of Like Signs <code>sub-digits</code></h2>
<p>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 <code>P</code> and <code>P'</code>.</p>
<pre><code>initial-carry == false rollup
sub-digits' == [P] [sub.THEN] [sub.R0] [sub.R1] genrec
sub-digits == initial-carry add-digits'
sub.THEN == [P'] [sub.THEN'] [sub.ELSE] ifte
</code></pre>
<h3>Refactoring For The Win</h3>
<p>We noted above that the algorithm for subtraction is similar to that for addition. Maybe we can reuse <em>more</em> than just <code>P</code> and <code>P'</code>? In fact, I think we could refactor (prematurely, two cases is one too few) something like this?</p>
<pre><code> [sub.THEN'] [sub.ELSE] [sub.R0] [sub.R1] foo
---------------------------------------------------------------------
[P] [[P'] [sub.THEN'] [sub.ELSE] ifte] [sub.R0] [sub.R1] genrec
</code></pre>
<p>or just</p>
<pre><code> [THEN] [ELSE] [R0] [R1] foo
----------------------------------------------------
[P] [[P'] [THEN] [ELSE] ifte] [R0] [R1] genrec
</code></pre>
<p>eh?</p>
<p><code>foo</code> is something like:</p>
<pre><code>F == [ifte] ccons [P'] swons
G == [F] dipdd
[THEN] [ELSE] [R0] [R1] [F] dipdd foo'
[THEN] [ELSE] F [R0] [R1] foo'
[THEN] [ELSE] [ifte] ccons [P'] swons [R0] [R1] foo'
[[THEN] [ELSE] ifte] [P'] swons [R0] [R1] foo'
[[P'] [THEN] [ELSE] ifte] [R0] [R1] foo'
</code></pre>
<p>That leaves <code>[P]</code>...</p>
<pre><code>F == [ifte] ccons [P'] swons [P] swap
G == [F] dipdd
[THEN] [ELSE] [ifte] ccons [P'] swons [P] swap [R0] [R1] foo'
[[THEN] [ELSE] ifte] [P'] swons [P] swap [R0] [R1] foo'
[[P'] [THEN] [ELSE] ifte] [P] swap [R0] [R1] foo'
[P] [[P'] [THEN] [ELSE] ifte] [R0] [R1] genrec
</code></pre>
<p>Ergo:</p>
<pre><code> F == [ifte] ccons [P'] swons [P] swap
foo == [F] dipdd genrec
combine-two-lists == [i cons] foo
</code></pre>
<p>-and-</p>
<pre><code>add-digits' == [one-empty-list]
[both-empty]
[both-full]
combine-two-lists
one-empty-list == ditch-empty-list add-carry-to-digits
both-empty == pop swap carry
both-full == uncons-two [add-with-carry] dipd
</code></pre>
<p>This illustrates how refactoring creates denser yet more readable code.</p>
<p>But this doesn't go quite far enough, I think.</p>
<pre><code>R0 == uncons-two [add-with-carry] dipd
</code></pre>
<p>I think <code>R0</code> will pretty much always do:</p>
<pre><code>uncons-two [combine-two-values] dipd
</code></pre>
<p>And so it should be refactored further to something like:</p>
<pre><code> [F] R0
-------------------------
uncons-two [F] dipd
</code></pre>
<p>And then <code>add-digits'</code> becomes just:</p>
<pre><code>add-digits' == [one-empty-list]
[both-empty]
[add-with-carry]
combine-two-lists
</code></pre>
<p>If we factor <code>ditch-empty-list</code> out of <code>one-empty-list</code>, and <code>pop</code> from <code>both-empty</code>:</p>
<pre><code>add-digits' == [add-carry-to-digits]
[swap carry]
[add-with-carry]
combine-two-lists
</code></pre>
<p>Let's figure out the new form.</p>
<pre><code> [ONE-EMPTY] [BOTH-EMPTY] [COMBINE-VALUES] foo
---------------------------------------------------
[P]
[
[P']
[ditch-empty-list ONE-EMPTY]
[pop BOTH-EMPTY]
ifte
]
[uncons-two [COMBINE-VALUES] dipd]
[i cons] genrec
</code></pre>
<p>eh?</p>
<p>Let's not over think it.</p>
<pre><code> [ONE-EMPTY] [ditch-empty-list] swoncat [BOTH-EMPTY] [pop] swoncat [COMBINE-VALUES]
[ditch-empty-list ONE-EMPTY] [pop BOTH-EMPTY] [COMBINE-VALUES]
</code></pre>
<p>With:</p>
<pre><code> [C] [A] [B] sandwich
--------------------------
[A [C] B]
</code></pre>
<p><code>Joy
[sandwich swap [cons] dip swoncat] inscribe</code></p>
<p><code>Joy
clear [B] [A] [C]</code></p>
<pre><code>[B] [A] [C]
</code></pre>
<p><code>Joy
sandwich</code></p>
<pre><code>[A [B] C]
</code></pre>
<p>So to get from </p>
<pre><code>[A] [B] [C]
</code></pre>
<p>to:</p>
<pre><code>[ditch-empty-list A] [pop B] [uncons-two [C] dipd]
</code></pre>
<p>we use:</p>
<pre><code>[[[ditch-empty-list] swoncat] dip [pop] swoncat] dip [uncons-two] [dipd] sandwich
</code></pre>
<p>It's gnarly, but simple:</p>
<p>```Joy
clear
[_foo0.0 [[ditch-empty-list] swoncat] dip] inscribe
[_foo0.1 [pop] swoncat] inscribe
[_foo0.3 [_foo0.0 _foo0.1] dip] inscribe
[_foo0.4 [uncons-two] [dipd] sandwich] inscribe
[_foo0 _foo0.3 _foo0.4] inscribe</p>
<p>[_foo1 [
[ifte] ccons
[P'] swons
[P] swap
] dip
] inscribe
```</p>
<p><code>Joy
[A] [B] [C] _foo0</code></p>
<pre><code>[ditch-empty-list A] [pop B] [uncons-two [C] dipd]
</code></pre>
<p><code>Joy
_foo1</code></p>
<pre><code>[P] [[P'] [ditch-empty-list A] [pop B] ifte] [uncons-two [C] dipd]
</code></pre>
<p><code>Joy
clear
[add-carry-to-digits]
[swap carry]
[add-with-carry]
_foo0 _foo1</code></p>
<pre><code>[P] [[P'] [ditch-empty-list add-carry-to-digits] [pop swap carry] ifte] [uncons-two [add-with-carry] dipd]
</code></pre>
<p>Compare the above with what we wanted:</p>
<pre><code>[P]
[
[P']
[ditch-empty-list ONE-EMPTY]
[pop BOTH-EMPTY]
ifte
]
[uncons-two [COMBINE-VALUES] dipd]
</code></pre>
<p>Allwe need to do is add:</p>
<pre><code>[i cons] genrec
</code></pre>
<p>```Joy
clear</p>
<p>[3 2 1] [6 5 4] initial-carry</p>
<p>[add-carry-to-digits]
[swap carry]
[add-with-carry]
_foo0 _foo1
```</p>
<pre><code>false [3 2 1] [6 5 4] [P] [[P'] [ditch-empty-list add-carry-to-digits] [pop swap carry] ifte] [uncons-two [add-with-carry] dipd]
</code></pre>
<p><code>Joy
[i cons] genrec</code></p>
<pre><code>[9 7 5]
</code></pre>
<p><code>Joy
clear
[build-two-list-combiner _foo0 _foo1 [i cons]] inscribe
[combine-two-lists [add-carry-to-digits] [swap carry] [add-with-carry] build-two-list-combiner] inscribe</code></p>
<p>```Joy
clear</p>
<p>[3 2 1] [6 5 4] initial-carry
combine-two-lists
```</p>
<pre><code>false [3 2 1] [6 5 4] [P] [[P'] [ditch-empty-list add-carry-to-digits] [pop swap carry] ifte] [uncons-two [add-with-carry] dipd] [i cons]
</code></pre>
<p><code>Joy
genrec</code></p>
<pre><code>[9 7 5]
</code></pre>
<p><code>Joy
[base 10] inscribe</code></p>
<pre><code>[9 7 5]
</code></pre>
<p>```Joy
clear</p>
<p>123456 to-bignum</p>
<p>```</p>
<pre><code>[true 6 5 4 3 2 1]
</code></pre>
<p><code>Joy
clear</code></p>
<p>So that's nice.</p>
<p>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.</p>
<p><code>Joy
[add-carry-to-digits]
[swap carry]
[add-with-carry]
build-two-list-combiner</code></p>
<pre><code>[P] [[P'] [ditch-empty-list add-carry-to-digits] [pop swap carry] ifte] [uncons-two [add-with-carry] dipd] [i cons]
</code></pre>
<p>Now grab the definition, add the <code>genrec</code> and symbol (name) and inscribe it:</p>
<p><code>Joy
[genrec] ccons ccons [add-digits'] swoncat</code></p>
<pre><code>[add-digits' [P] [[P'] [ditch-empty-list add-carry-to-digits] [pop swap carry] ifte] [uncons-two [add-with-carry] dipd] [i cons] genrec]
</code></pre>
<p><code>Joy
inscribe</code></p>
<p>Try it out...</p>
<p><code>Joy
false [3 2 1] [4 3 2] add-digits'</code></p>
<pre><code>[7 5 3]
</code></pre>
<p><code>Joy
false swap base -- unit</code></p>
<pre><code>false [7 5 3] [9]
</code></pre>
<p><code>Joy
add-digits'</code></p>
<pre><code>[6 6 3]
</code></pre>
<p><code>Joy
clear</code></p>
<h4>Demonstrate <code>add-bignums</code></h4>
<p><code>Joy
1234 999 [to-bignum] ii</code></p>
<pre><code>[true 4 3 2 1] [true 9 9 9]
</code></pre>
<p><code>Joy
add-bignums</code></p>
<pre><code>[true 3 3 2 2]
</code></pre>
<p><code>Joy
from-bignum</code></p>
<pre><code>2233
</code></pre>
<p><code>Joy
1234 999 +</code></p>
<pre><code>2233 2233
</code></pre>
<p><code>Joy
clear</code></p>
<h3>Subtracting</h3>
<p>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:</p>
<pre><code>10 - 5 = 5
</code></pre>
<p>But I don't know the algorithm to subtract a larger number from a smaller one:</p>
<pre><code>5 - 10 = ???
</code></pre>
<p>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:</p>
<pre><code>a - b = -(b - a)
</code></pre>
<p>So if we want to subtract a larger number <code>a</code> from a smaller one <code>b</code> we can instead subtract the smaller from the larger and invert the sign:</p>
<pre><code>5 - 10 = -(10 - 5)
</code></pre>
<p>To do this we need a function <code>gt-digits</code> that will tell us which of two digit lists represents the larger integer.</p>
<h4><code>gt-digits</code></h4>
<p>I just realized I don't have a list length function yet!</p>
<p><code>Joy
[length [pop ++] step_zero] inscribe</code></p>
<p><code>Joy
clear
[] length</code></p>
<pre><code>0
</code></pre>
<p><code>Joy
clear
[this is a list] length</code></p>
<pre><code>4
</code></pre>
<p><code>Joy
clear
[1 2 3] [4 5] over over [length] app2</code></p>
<pre><code>[1 2 3] [4 5] 3 2
</code></pre>
<p><code>Joy
[swap][6][7]cmp</code></p>
<pre><code>[4 5] [1 2 3]
</code></pre>
<p>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?</p>
<p><code>Joy
clear</code></p>
<p><code>Joy
[1 2 3] [4 5 6] [bool] ii &amp;</code></p>
<pre><code>true
</code></pre>
<p><code>Joy
clear
[1 2 3] [4 5 6]
[[bool] ii | not]
[pop]
[uncons-two]
[i [unit cons] dip cons]
genrec</code></p>
<pre><code>[[1 4] [2 5] [3 6]]
</code></pre>
<p><code>Joy
clear
[1 2 3] [4 5 6]
[[bool] ii | not]
[pop]
[uncons-two]
[i [unit cons] dip cons]
genrec</code></p>
<pre><code>[[1 4] [2 5] [3 6]]
</code></pre>
<p><code>Joy
clear</code></p>
<p>So I guess that's <code>zip</code>?</p>
<p>But we want something a little different.</p>
<p>It's a weird function: compare lengths, if they are the same length then compare contents pairwise from the end.</p>
<p>if the first list is empty and the second list isn't then the whole function should return false</p>
<p>if the first list is non-empty and the second list is empty then the whole function should return true</p>
<p>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?</p>
<p>if both lists are empty we start comparing uncons'd pairs until we find an un-equal pair or run out of pairs.</p>
<p>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?)</p>
<p>if we find an unequal pair we return a&gt;b and discard the rest of the pairs. Or maybe this all happens in some sort of <code>infra first</code> situation?</p>
<p>So the predicate will be <code>[bool] ii &amp; not</code>, if one list is longer than the other we are done.
We postulate a third list to contain the pairs:</p>
<pre><code>[] [3 2 1] [4 5 6] [P] [BASE] [R0] [R1] genrec
</code></pre>
<p>The recursive branch seems simpler to figure out:</p>
<pre><code>[] [3 2 1] [4 5 6] R0 [F] R1
uncons-two [unit cons swons] dipd [F] i
[] [3 2 1] [4 5 6] [P] [BASE] [uncons-two [unit cons swons] dipd] tailrec
</code></pre>
<p><code>Joy
[xR1 uncons-two [unit cons swons] dipd] inscribe</code></p>
<p>```Joy
clear</p>
<p>[] [3 2 1] [4 5 6]
```</p>
<pre><code>[] [3 2 1] [4 5 6]
</code></pre>
<p><code>Joy
xR1 xR1 xR1</code></p>
<pre><code>[[1 6] [2 5] [3 4]] [] []
</code></pre>
<p><code>Joy
clear
[xP [bool] ii &amp; not] inscribe</code></p>
<p>```Joy
clear</p>
<p>[] [3 2 1] [5 4] [xP] [] [xR1] tailrec
```</p>
<pre><code>[[2 4] [3 5]] [1] []
</code></pre>
<p>```Joy
clear</p>
<p>[] [3 2] [4 5 1] [xP] [] [xR1] tailrec
```</p>
<pre><code>[[2 5] [3 4]] [] [1]
</code></pre>
<p>```Joy
clear</p>
<p>[] [3 2 1] [5 4 3] [xP] [] [xR1] tailrec
```</p>
<pre><code>[[1 3] [2 4] [3 5]] [] []
</code></pre>
<p>Now comes the tricky part, that base case:</p>
<p>we have three lists. The first is a possibly-empty list of pairs to compare.</p>
<p>The second two are the tails of the original lists.</p>
<p>If the top list is non-empty then the second list must be empty so the whole function should return true</p>
<p>If the top list is empty and the second list isn't then the whole function should return false</p>
<p>If both lists are empty we start comparing uncons'd pairs until we find an un-equal pair or run out of pairs.</p>
<pre><code>[bool] # if the first list is non-empty
[popop pop true]
[
[pop bool] # the second list is non-empty (the first list is empty)
[popop pop false]
[
# both lists are empty
popop
compare-pairs
]
ifte
]
ifte
</code></pre>
<p>```Joy
clear
[][][1]</p>
<p>[bool]
[popop pop true]
[
[pop bool]
[popop pop false]
[popop 23 swons]
ifte
]
ifte
```</p>
<pre><code>true
</code></pre>
<p>```Joy
clear
[][1][]</p>
<p>[bool]
[popop pop true]
[
[pop bool]
[popop pop false]
[popop 23 swons]
ifte
]
ifte
```</p>
<pre><code>false
</code></pre>
<p>```Joy
clear
[1][][]</p>
<p>[bool]
[popop pop true]
[
[pop bool]
[popop pop false]
[popop 23 swons]
ifte
]
ifte
```</p>
<pre><code>[23 1]
</code></pre>
<h4><code>compare-pairs</code></h4>
<p>This should be a pretty simple recursive function</p>
<pre><code>[P] [THEN] [R0] [R1] genrec
</code></pre>
<p>If the list is empty we return <code>false</code></p>
<pre><code>P == bool not
THEN == pop false
</code></pre>
<p>On the recursive branch we have an <code>ifte</code> expression:</p>
<pre><code> pairs R0 [compare-pairs] R1
---------------------------------------------------
pairs [P.rec] [THEN.rec] [compare-pairs] ifte
</code></pre>
<p>We must compare the pair from the top of the list:</p>
<pre><code>P.rec == first [&gt;] infrst
</code></pre>
<p>```Joy
clear</p>
<p>[[1 3] [2 4] [3 5]] first [&gt;] infrst
```</p>
<pre><code>true
</code></pre>
<p>```Joy
clear</p>
<p>[[1 3] [2 4] [3 5]] [[&gt;] infrst] map
```</p>
<pre><code>[true true true]
THEN.rec == pop true
</code></pre>
<p>```Joy
clear</p>
<p>[compare-pairs
[bool not]
[pop false]
[
[first [&gt;] infrst]
[pop true]
]
[ifte]
genrec
] inscribe
```</p>
<p><code>Joy
clear [[1 3] [2 4] [3 5]] compare-pairs</code></p>
<pre><code>true
</code></pre>
<p><code>Joy
clear [[1 3] [3 3] [3 5]] compare-pairs</code></p>
<pre><code>true
</code></pre>
<p>Whoops! I forgot to remove the already-checked pair from the list of pairs! (Later on I discover that the logic is inverted here: <code>&gt;=</code> not <code>&lt;</code> d'oh!)</p>
<p>```Joy
clear</p>
<p>[compare-pairs
[bool not]
[pop false]
[
[first [&gt;=] infrst]
[pop true]
]
[[rest] swoncat ifte]
genrec
] inscribe
```</p>
<p>This is clunky and inefficient but it works.</p>
<p><code>Joy
clear [[1 0] [2 2] [3 3]] compare-pairs</code></p>
<pre><code>true
</code></pre>
<p><code>Joy
clear [[1 1] [2 2] [3 3]] compare-pairs</code></p>
<pre><code>true
</code></pre>
<p><code>Joy
clear [[1 2] [2 2] [3 3]] compare-pairs</code></p>
<pre><code>true
</code></pre>
<p><code>Joy
clear</code></p>
<p><code>Joy
clear [[1 1] [2 1] [3 3]] compare-pairs</code></p>
<pre><code>true
</code></pre>
<p><code>Joy
clear [[1 1] [2 2] [3 3]] compare-pairs</code></p>
<pre><code>true
</code></pre>
<p><code>Joy
clear [[1 1] [2 3] [3 3]] compare-pairs</code></p>
<pre><code>true
</code></pre>
<p>```Joy</p>
<p>```</p>
<p>```Joy
clear
[[1 1] [2 1] [3 3]] [] []</p>
<p>[bool]
[popop pop true]
[
[pop bool]
[popop pop false]
[popop compare-pairs]
ifte
]
ifte
```</p>
<pre><code>true
</code></pre>
<p><code>Joy
[BASE
[bool]
[popop pop true]
[
[pop bool]
[popop pop false]
[popop compare-pairs]
ifte
]
ifte
] inscribe</code></p>
<pre><code>true
</code></pre>
<p>```Joy
clear</p>
<p>[] [3 2 1] [4 5 6]
```</p>
<pre><code>[] [3 2 1] [4 5 6]
</code></pre>
<p><code>Joy
[xP] [BASE] [xR1] tailrec</code></p>
<pre><code>true
</code></pre>
<p>```Joy
clear</p>
<p>[] [3 2 1] [4 5 6] swap
```</p>
<pre><code>[] [4 5 6] [3 2 1]
</code></pre>
<p><code>Joy
[xP] [BASE] [xR1] tailrec</code></p>
<pre><code>false
</code></pre>
<p>```Joy
clear</p>
<p>[] [3 2 1] dup
```</p>
<pre><code>[] [3 2 1] [3 2 1]
</code></pre>
<p><code>Joy
[xP] [BASE] [xR1] tailrec</code></p>
<pre><code>true
</code></pre>
<p><code>Joy
clear</code></p>
<p><code>Joy
[gt-bignum &lt;&lt;{} [xP] [BASE] [xR1] tailrec] inscribe</code></p>
<p><code>Joy
clear [3 2 1] [4 5 6] gt-bignum</code></p>
<pre><code>true
</code></pre>
<p><code>Joy
clear [3 2 1] [4 5 6] swap gt-bignum</code></p>
<pre><code>false
</code></pre>
<p><code>Joy
clear [3 2 1] dup gt-bignum</code></p>
<pre><code>true
</code></pre>
<p>```Joy</p>
<p>```</p>
<p><code>Joy
clear [3 2 1] [4 5 6] [gt-bignum] [swap] [] ifte</code></p>
<pre><code>[4 5 6] [3 2 1]
</code></pre>
<p><code>Joy
clear [4 5 6] [3 2 1] [gt-bignum] [swap] [] ifte</code></p>
<pre><code>[4 5 6] [3 2 1]
</code></pre>
<p>And so it goes.</p>
<p>Now we can subtract, we just have to remember to invert the sign bit if we swap the digit lists.</p>
<p>Maybe something like:</p>
<pre><code>check-gt == [gt-bignum] [swap true rollup] [false rollup] ifte
</code></pre>
<p>To keep the decision around as a Boolean flag? We can <code>xor</code> it with the sign bit?</p>
<p><code>Joy
clear
[check-gt [gt-bignum] [swap [not] dipd] [] ifte] inscribe</code></p>
<p><code>Joy
false [4 5 6] [3 2 1]</code></p>
<pre><code>false [4 5 6] [3 2 1]
</code></pre>
<p><code>Joy
check-gt</code></p>
<pre><code>false [4 5 6] [3 2 1]
</code></pre>
<p><code>Joy
clear</code></p>
<h3>Subtraction, at last...</h3>
<p>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:</p>
<pre><code>sub-bignums == [same-sign] [sub-like-bignums] [1 0 /] ifte
sub-like-bignums == [uncons] dip rest sub-digits cons
^
|
</code></pre>
<p>At this point we would have the sign bit then the two digit lists.</p>
<pre><code>sign [c b a] [z y x]
</code></pre>
<p>We want to use <code>check-gt</code> here:</p>
<pre><code>sign [c b a] [z y x] check-gt
sign swapped? [c b a] [z y x] check-gt
</code></pre>
<p>It seems we should just flip the sign bit if we swap, eh?</p>
<pre><code>check-gt == [gt-bignum] [swap [not] dipd] [] ifte
</code></pre>
<p>Now we subtract the digits:</p>
<pre><code>sign [c b a] [z y x] sub-digits cons
</code></pre>
<p>So:</p>
<pre><code>sub-like-bignums == [uncons] dip rest check-gt sub-digits cons
sub-digits == initial-carry sub-digits'
sub-digits' ==
[sub-carry-from-digits]
[swap sub-carry]
[sub-with-carry]
build-two-list-combiner
genrec
</code></pre>
<p>We just need to define the pieces.</p>
<h4><code>sub-with-carry</code></h4>
<p>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 <em>at the end of a digit list subtraction.</em></p>
<pre><code> carry a b sub-with-carry
------------------------------
(a-b-carry) new-carry
_sub-with-carry0 ≡ [bool-to-int] dipd - -
_sub-with-carry1 ≡ [base + base mod] [0 &lt;] clop
sub-with-carry ≡ _sub-with-carry0 _sub-with-carry1
</code></pre>
<p><code>Joy
[_sub-with-carry0 rolldown bool-to-int [-] ii] inscribe
[_sub-with-carry1 [base + base mod] [0 &lt;] cleave] inscribe
[sub-with-carry _sub-with-carry0 _sub-with-carry1] inscribe</code></p>
<p><code>Joy
clear false 3 base --</code></p>
<pre><code>false 3 9
</code></pre>
<p><code>Joy
sub-with-carry</code></p>
<pre><code>4 true
</code></pre>
<p><code>Joy
clear</code></p>
<h4><code>sub-carry-from-digits</code></h4>
<p>Should be easy to make modeled on <code>add-carry-to-digits</code>, another very simple recursive function. The predicate, base case, and <code>R1</code> are the same:</p>
<pre><code>carry [n ...] sub-carry-from-digits
carry [n ...] [pop not] [popd] [_scfd_R0] [i cons] genrec
</code></pre>
<p>That leaves the recursive branch:</p>
<pre><code>true [n ...] _scfd_R0 [sub-carry-from-digits] i cons
</code></pre>
<p>-or-</p>
<pre><code>true [] _scfd_R0 [sub-carry-from-digits] i cons
</code></pre>
<p><strong>Except</strong> that this should should never happen when subtracting, because we already made sure that we're only ever subtracting a number less than or equal to the, uh, number we are subtracting from (TODO rewrite this trainwreck of a sentence).</p>
<pre><code> true [a ...] _scfd_R0 [sub-carry-from-digits] i cons
----------------------------------------------------------------
true 0 a add-with-carry [...] [sub-carry-from-digits] i cons
------------------------------------------------------------------
(a+1) carry [...] [sub-carry-from-digits] i cons
true [a ...] _scfd_R0
true [a ...] 0 swap uncons [sub-with-carry] dip
true 0 [a ...] uncons [sub-with-carry] dip
true 0 a [...] [sub-with-carry] dip
true 0 a sub-with-carry [...]
_scfd_R0 == 0 swap uncons [sub-with-carry] dip
</code></pre>
<p>But there's a problem! This winds up subtracting <code>a</code> from 0 rather than the other way around:</p>
<pre><code>_scfd_R0 == uncons 0 swap [sub-with-carry] dip
</code></pre>
<p><code>Joy
[sub-carry-from-digits
[pop not]
[popd]
[_scfd_R0]
[i cons]
genrec
] inscribe
[_scfd_R0 uncons 0 swap [sub-with-carry] dip] inscribe</code></p>
<p>Try it out:</p>
<p>```Joy
clear</p>
<p>false [3 2 1] sub-carry-from-digits
```</p>
<pre><code>[3 2 1]
</code></pre>
<p>```Joy
clear</p>
<p>true [0 1] sub-carry-from-digits
```</p>
<pre><code>[9 0]
</code></pre>
<p>```Joy
clear</p>
<p>true [3 2 1] sub-carry-from-digits
```</p>
<pre><code>[2 2 1]
</code></pre>
<p>```Joy
clear</p>
<p>true [0 0 1] sub-carry-from-digits
```</p>
<pre><code>[9 9 0]
</code></pre>
<p><code>Joy
clear</code></p>
<p>But what about those leading zeroes?</p>
<p>We could use a version of <code>cons</code> that refuses to put 0 onto an empty list?</p>
<pre><code>cons-but-not-leading-zeroes == [[bool] ii | not] [popd] [cons] ifte
</code></pre>
<p><code>Joy
[cons-but-not-leading-zeroes [[bool] ii | not] [popd] [cons] ifte] inscribe</code></p>
<p><code>Joy
[sub-carry-from-digits
[pop not]
[popd]
[_scfd_R0]
[i cons-but-not-leading-zeroes]
genrec
] inscribe</code></p>
<p><code>Joy
[_scfd_R0 uncons 0 swap [sub-with-carry] dip] inscribe</code></p>
<p>```Joy
clear</p>
<p>true [0 0 1] sub-carry-from-digits
```</p>
<pre><code>[9 9]
</code></pre>
<p><code>Joy
clear</code></p>
<h4><code>sub-carry</code></h4>
<pre><code>sub-carry == pop
</code></pre>
<p><code>Joy
[sub-like-bignums [uncons] dip rest check-gt sub-digits cons] inscribe
[sub-digits initial-carry sub-digits'] inscribe
[sub-digits'
[sub-carry-from-digits]
[swap pop]
[sub-with-carry]
build-two-list-combiner
genrec
] inscribe</code></p>
<p><code>Joy
clear
true [3 2 1] [6 5 4]</code></p>
<pre><code>true [3 2 1] [6 5 4]
</code></pre>
<p><code>Joy
check-gt initial-carry</code></p>
<pre><code>false false [6 5 4] [3 2 1]
</code></pre>
<p><code>Joy
sub-digits'</code></p>
<pre><code>false [3 3 3]
</code></pre>
<p><code>Joy
clear
12345 to-bignum 109 to-bignum</code></p>
<pre><code>[true 5 4 3 2 1] [true 9 0 1]
</code></pre>
<p><code>Joy
sub-like-bignums</code></p>
<pre><code>[true 6 3 2 2 1]
</code></pre>
<p><code>Joy
from-bignum</code></p>
<pre><code>12236
</code></pre>
<p><code>Joy
clear</code></p>
<h4><code>neg-bignum</code></h4>
<p><code>Joy
[neg-bignum [not] infra] inscribe</code></p>
<p><code>Joy
123</code></p>
<pre><code>123
</code></pre>
<p><code>Joy
to-bignum neg-bignum from-bignum</code></p>
<pre><code>-123
</code></pre>
<p><code>Joy
to-bignum neg-bignum from-bignum</code></p>
<pre><code>123
</code></pre>
<p><code>Joy
clear
[sub-bignums [same-sign] [sub-like-bignums] [neg-bignum add-like-bignums] ifte] inscribe
[add-bignums [same-sign] [add-like-bignums] [neg-bignum sub-like-bignums] ifte] inscribe</code></p>
<h2>Multiplication</h2>
<p>```Joy</p>
<p>```</p>
<h2>Appendix: Source Code</h2>
<pre><code>clear
[base 2147483648]
[ditch-empty-list [bool] [popd] [pop] ifte]
[bool-to-int [0] [1] branch]
[uncons-two [uncons] ii swapd]
[sandwich swap [cons] dip swoncat]
[digitalize [0 &lt;=] [pop []] [base divmod swap] [i cons] genrec]
[to-bignum [!-] [abs digitalize] cleave cons]
[prep rest 1 0 rolldown]
[from-bignum [next-digit] step popd]
[next-digit [increase-power] [accumulate-digit] clop popdd]
[increase-power popop base *]
[accumulate-digit rolldown * +]
[sign-int [first] [prep from-bignum] cleave]
[neg-if-necessary swap [neg] [] branch]
[from-bignum sign-int neg-if-necessary]
[add-with-carry _add-with-carry0 _add-with-carry1]
[_add-with-carry0 [bool-to-int] dipd + +]
[_add-with-carry1 base [mod] [&gt;=] clop]
[add-carry-to-digits [pop not] [popd] [actd.R0] [i cons] genrec]
[actd.R0 [bool] [actd.R0.then] [actd.R0.else] ifte]
[actd.R0.else popd 1 false rolldown]
[actd.R0.then 0 swap uncons [add-with-carry] dip]
[add-digits initial-carry add-digits']
[initial-carry false rollup]
[add-digits' [P] [THEN] [R0] [R1] genrec]
[P [bool] ii &amp; not]
[THEN [P'] [THEN'] [ELSE] ifte]
[R0 uncons-two [add-with-carry] dipd]
[R1 i cons]
[P' [bool] ii |]
[THEN' ditch-empty-list add-carry-to-digits]
[ELSE pop swap [] [1 swons] branch]
[same-sign [first] ii xor not]
[add-like-bignums [uncons] dip rest add-digits cons]
[add-bignums [same-sign] [add-like-bignums] [neg-bignum sub-like-bignums] ifte]
[build-two-list-combiner _btlc0 _btlc1 [i cons]]
[_btlc0.0 [[ditch-empty-list] swoncat] dip]
[_btlc0.1 [pop] swoncat]
[_btlc0.3 [_btlc0.0 _btlc0.1] dip]
[_btlc0.4 [uncons-two] [dipd] sandwich]
[_btlc0 _btlc0.3 _btlc0.4]
[_btlc1 [[ifte] ccons [P'] swons [P] swap] dip]
[carry [] [1 swons] branch]
[compare-pairs [bool not] [pop false] [[first [&gt;=] infrst] [pop true]] [[rest] swoncat ifte] genrec]
[xR1 uncons-two [unit cons swons] dipd]
[xP [bool] ii &amp; not]
[BASE [bool] [popop pop true] [[pop bool] [popop pop false] [popop compare-pairs] ifte] ifte]
[gt-bignum &lt;&lt;{} [xP] [BASE] [xR1] tailrec]
[check-gt [gt-bignum] [swap [not] dipd] [] ifte]
[sub-carry pop]
[sub-carry-from-digits [pop not] [popd] [_scfd_R0] [i cons-but-not-leading-zeroes] genrec] inscribe
[_scfd_R0 uncons 0 swap [sub-with-carry] dip] inscribe
[cons-but-not-leading-zeroes [P'] [cons] [popd] ifte]
[sub-with-carry _sub-with-carry0 _sub-with-carry1]
[_sub-with-carry0 rolldown bool-to-int [-] ii]
[_sub-with-carry1 [base + base mod] [0 &lt;] cleave]
[sub-like-bignums [uncons] dip rest check-gt sub-digits cons]
[sub-digits initial-carry sub-digits']
enstacken [inscribe] step
[add-carry-to-digits]
[swap carry]
[add-with-carry]
build-two-list-combiner
[genrec] ccons ccons
[add-digits'] swoncat
inscribe
[sub-carry-from-digits]
[swap sub-carry]
[sub-with-carry]
build-two-list-combiner
[genrec] ccons ccons
[sub-digits'] swoncat
inscribe
</code></pre>
<h3>notes</h3>
<p>So far I have three formats for Joy source:</p>
<ul>
<li><code>def.txt</code> is a list of definitions (UTF-8), one per line, with no special marks.</li>
<li><code>foo ≡ bar baz...</code> lines in the <code>joy.py</code> embedded definition text, because why not? (Sometimes I use <code>==</code> instead of <code></code> mostly because some tools can't handle the Unicode glyph. Like converting this notebook to PDF via LaTeX just omitted them.)</li>
<li><code>[name body] inscribe</code> 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.)</li>
</ul>
<p>So far I prefer the <code>def.txt</code> style but that makes it tricky to embed them automatically into the <code>joy.py</code> file.</p>
<h4>Refactoring</h4>
<p>We have <code>i cons</code> but that's pretty tight already, eh?</p>
<p>However, <code>[i cons] genrec</code> is an interesting combinator. It's almost <code>tailrec</code> with that <code>i</code> combinator for the recursion, but then <code>cons</code> means it's a list-builder (an <em>anamorphism</em> if you go for that sort of thing.)</p>
<pre><code>simple-list-builder == [i cons] genrec
</code></pre>
<p>And maybe:</p>
<pre><code>boolii == [bool] ii
both? == boolii &amp;
one-of? == boolii |
</code></pre>
</body>
</html>