1150 lines
46 KiB
HTML
1150 lines
46 KiB
HTML
<!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">
|
||
<script src="/Joy.js"></script>
|
||
</head>
|
||
<body>
|
||
<div id="joy_interpreter"></div>
|
||
<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 >] [get-digit] while
|
||
</code></pre>
|
||
<p>Let's try it:</p>
|
||
<pre><code>joy? 1234567890123456789012345678901234567890
|
||
1234567890123456789012345678901234567890
|
||
|
||
joy? [0 >] [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 >] [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 >] [get-digit] while pop]
|
||
[1234567890123456789012345678901234567890] [[0 >] [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 <=]
|
||
</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 <=] [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 <=] [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 <=] [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 <=] [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 >=</code>
|
||
to get the new carry flag. Factoring give us:</p>
|
||
<pre><code>base [mod] [>=] 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] [>=] 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 -> 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] [>=] 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] [>=] 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>
|
||
<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</h2>
|
||
<p>Subtraction is similar to addition in that it's a simple recursive algorithm that works digit-by-digit.
|
||
It has the same three cases as well, so we can reuse the <code>list-combiner-genrec</code> "macro" that
|
||
we specified (but did not yet derive) a moment ago.</p>
|
||
<pre><code> sub-digits == initial-carry sub-digits'
|
||
sub-digits' == [both-empty] [one-empty] [both-non-empty] list-combiner-genrec
|
||
</code></pre>
|
||
<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?</p>
|
||
<p>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>
|
||
<h3>≡ <code>length</code></h3>
|
||
<p>Gentle reader, it was at this time that I realized I don't have a list length function yet!</p>
|
||
<pre><code>[length [pop ++] step_zero] inscribe
|
||
</code></pre>
|
||
<h3>Comparing Lists of Integers</h3>
|
||
<p>We only need to compare the digits of the numbers if one list of digits is longer than the other.
|
||
We could use <code>length</code> on both lists and then <code>cmp</code>:</p>
|
||
<pre><code>a b [G] [E] [L] cmp
|
||
</code></pre>
|
||
<p>If the top list is longer than the second list the function should return <code>true</code>,
|
||
and if the top list is shorter than the second list the function should return <code>false</code>,</p>
|
||
<pre><code>dup2 [length] ii [true] [E] [false] cmp
|
||
</code></pre>
|
||
<p>If both lists are non-empty we have to compare digits starting with the ends.</p>
|
||
<pre><code>E == zip reverse compare-digits
|
||
</code></pre>
|
||
<p>But this is inefficient! The <code>length</code> function will traverse each list once,
|
||
then the <code>zip</code> function will traverse both lists and build a new list of pairs,
|
||
then the <code>reverse</code> function will traverse that list and rebuild it,
|
||
then the <code>compare-digits</code> will traverse that list looking for unequal pairs...
|
||
It's a lot of work that we don't really want or need to do.</p>
|
||
<h3>A More Efficient Comparison</h3>
|
||
<p>What we really want is a function that iterates through both lists together
|
||
and:</p>
|
||
<ul>
|
||
<li>If the top list is empty and the second list isn't then the whole function should return <code>false</code>.</li>
|
||
<li>If the top list is non-empty and the second list is empty then the whole function should return <code>true</code>.</li>
|
||
<li>If both lists are empty we start checking pairs of digits (that we got from the recursive case.)</li>
|
||
<li>If both lists are non-empty we <code>uncons-two</code> digits for later comparison and recur.</li>
|
||
</ul>
|
||
<p>Let's start designing the function.</p>
|
||
<pre><code> [...] [...] F
|
||
-------------------
|
||
bool
|
||
</code></pre>
|
||
<p>We will need a list on which to put pairs</p>
|
||
<pre><code>F == <<{} F′
|
||
|
||
[] [...] [...] F′
|
||
----------------------
|
||
bool
|
||
</code></pre>
|
||
<p>It's a recursive function:</p>
|
||
<pre><code>F′ == [P] [THEN] [R0] [R1] genrec
|
||
</code></pre>
|
||
<p>The predicate tests whether both of the two input lists are non-empty:</p>
|
||
<pre><code>P = null-two \/
|
||
</code></pre>
|
||
<p>(We defined this as <code>either-or-both-null</code> above.)</p>
|
||
<p>Let's look at the recursive case first:</p>
|
||
<pre><code> [...] [b ...] [a ...] R0 [F] R1
|
||
-------------------------------------------
|
||
[[b a] ...] [...] [...] F
|
||
</code></pre>
|
||
<p>So <code>R0</code> transfers items from the source list to the pairs list,
|
||
let's call it <code>shift-pair</code>:</p>
|
||
<pre><code> [...] [b ...] [a ...] shift-pair
|
||
--------------------------------------
|
||
[[b a] ...] [...] [...]
|
||
</code></pre>
|
||
<p>I'll leave that as an exercise for the reader for now.</p>
|
||
<p><code>R1</code> is just <code>i</code> (this is a <code>tailrec</code> function.)</p>
|
||
<pre><code>F == <<{} [either-or-both-null] [THEN] [shift-pair] tailrec
|
||
</code></pre>
|
||
<p>Now let's derive <code>THEN</code>, there are three cases:</p>
|
||
<pre><code>[pairs...] [] [] THEN
|
||
[pairs...] [b ...] [] THEN
|
||
[pairs...] [] [a ...] THEN
|
||
</code></pre>
|
||
<p>We can model this as a pair of <code>ifte</code> expressions, one nested in the other:</p>
|
||
<pre><code>[P] [THEN′] [[P′] [THEN′′] [ELSE′] ifte] ifte
|
||
</code></pre>
|
||
<p>But in the event we won't need the inner <code>ifte</code>, see below.</p>
|
||
<p>The first predicate should check if both lists are empty:</p>
|
||
<pre><code>P == null-two /\
|
||
</code></pre>
|
||
<p>(We defined this as <code>both-null</code> above.)</p>
|
||
<p>If both lists are empty we check the pairs:</p>
|
||
<pre><code>THEN′ == popop compare-pairs
|
||
</code></pre>
|
||
<p>Otherwise if the top list is empty we return <code>false</code>, otherwise <code>true</code>,
|
||
and since this is a destructive operation we don't have to use <code>ifte</code> here:</p>
|
||
<pre><code>THEN == [both-null] [popop compare-pairs] [popopd null] ifte
|
||
|
||
F == <<{} [either-or-both-null] [THEN] [shift-pair] tailrec
|
||
</code></pre>
|
||
<p>Now we just have to write <code>compare-pairs</code> (and <code>shift-pair</code>.)</p>
|
||
<h3>≡ <code>shift-pair</code></h3>
|
||
<pre><code>[pair-up unit cons] inscribe
|
||
|
||
[shift-pair uncons-two [pair-up swons] dipd] inscribe
|
||
</code></pre>
|
||
<h3>Compare Pairs</h3>
|
||
<p>This function takes a list of pairs of digits (ints) and compares
|
||
them until it finds an unequal pair or runs out of pairs.</p>
|
||
<p>We are implementing "greater than" (b > a) so if we run out of digits
|
||
that means the two numbers were equal, and so we return <code>false</code>:</p>
|
||
<pre><code>F == [null] [pop false] [R0] [R1] genrec
|
||
</code></pre>
|
||
<p>That leaves the recursive branch:</p>
|
||
<pre><code>[[b a] ...] R0 [F] R1
|
||
</code></pre>
|
||
<p>I figure we're going to want some sort of <code>ifte</code>. (But this turns out to
|
||
be a mistake!)</p>
|
||
<pre><code>[[b a] ...] [P] [THEN] [F] ifte
|
||
</code></pre>
|
||
<p>if b > a we can stop and report <code>true</code>, otherwise we discard the pair and recur.</p>
|
||
<pre><code>P == first i >
|
||
|
||
THEN == pop true
|
||
</code></pre>
|
||
<p>Note that that fails to discard the pair!</p>
|
||
<pre><code>[[b a] ...] [first i >] [pop true] [F] ifte
|
||
</code></pre>
|
||
<p>If b <= a this would just re-run <code>F</code> with the same list!</p>
|
||
<p>Oops! D'oh! I didn't think it through properly.</p>
|
||
<p>We need to distinguish all three case (> = <) so we want to use <code>cmp</code>:</p>
|
||
<pre><code>[[b a] ...] unswons i [G] [F] [L] cmp
|
||
</code></pre>
|
||
<p>Becomes:</p>
|
||
<pre><code>[...] b a [G] [F] [L] cmp
|
||
</code></pre>
|
||
<p>Note that we recur on equality (that is our <code>E</code> function is just <code>F</code> itself).</p>
|
||
<p>If we the digits are not equal we can quit the loop with the answer:</p>
|
||
<pre><code>[...] b a [pop true] [F] [pop false] cmp
|
||
</code></pre>
|
||
<p>So:</p>
|
||
<pre><code>R0 == unswons i [pop true]
|
||
|
||
R1 == [pop false] cmp
|
||
</code></pre>
|
||
<h3>≡ <code>compare-pairs</code></h3>
|
||
<pre><code>[compare-pairs.R0 unswons i [pop true]] inscribe
|
||
[compare-pairs.R1 [pop false] cmp] inscribe
|
||
[compare-pairs [null] [pop false] [compare-pairs.R0] [compare-pairs.R1] genrec] inscribe
|
||
</code></pre>
|
||
<h3>≡ <code>gt-digits</code></h3>
|
||
<pre><code>[gt-digits.THEN [both-null] [popop compare-pairs] [popopd null] ifte] inscribe
|
||
[gt-digits <<{} [either-or-both-null] [gt-digits.THEN] [shift-pair] tailrec] inscribe
|
||
</code></pre>
|
||
<h3>Almost Ready to Subtract</h3>
|
||
<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-digits] [swap true] [false] 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>Let's start with two numbers on the stack, with the same sign:</p>
|
||
<pre><code>[bool int int int] [bool int int int]
|
||
</code></pre>
|
||
<p>Then we keep one of the sign Booleans around and discard the other:</p>
|
||
<pre><code>[bool int int int] [bool int int int] [uncons] dip rest
|
||
[bool int int int] uncons [bool int int int] rest
|
||
bool [int int int] [bool int int int] rest
|
||
bool [int int int] [int int int]
|
||
</code></pre>
|
||
<p>So what we really want to do is <code>swap</code> and <code>not</code>:</p>
|
||
<pre><code>check-gt == [gt-digits] [swap [not] dipd] [] ifte
|
||
</code></pre>
|
||
<h3>≡ <code>extract-sign</code></h3>
|
||
<pre><code>[extract-sign [uncons] dip rest] inscribe
|
||
</code></pre>
|
||
<h3>≡ <code>check-gt</code></h3>
|
||
<pre><code>[check-gt [gt-bignum] [swap [not] dipd] [] ifte] inscribe
|
||
</code></pre>
|
||
<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 == extract-sign check-gt sub-digits cons
|
||
|
||
sub-digits == initial-carry sub-digits'
|
||
|
||
initial-carry == false rollup
|
||
|
||
|
||
both-empty == pop swap [] [1 swons] branch
|
||
one-empty == ditch-empty-list sub-carry-from-digits
|
||
both-non-empty == uncons-two [sub-with-carry] dipd
|
||
|
||
sub-digits′ == [both-empty] [one-empty] [both-non-empty] list-combiner-genrec
|
||
</code></pre>
|
||
<p>Which would expand into:</p>
|
||
<pre><code>sub-digits′ == [either-or-both-null]
|
||
[[both-null] [both-empty] [one-empty] ifte]
|
||
[both-non-empty]
|
||
[i cons]
|
||
genrec
|
||
|
||
sub-digits′ == [either-or-both-null] [[both-null] [both-empty] [ditch-empty-list sub-carry-from-digits] ifte] [uncons-two [sub-with-carry] dipd] [i cons] genrec
|
||
</code></pre>
|
||
<p>We just need to define the pieces.</p>
|
||
<h3>≡ <code>sub-with-carry</code></h3>
|
||
<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-carry.0 - swap [] [--] branch] inscribe
|
||
[sub-with-carry.1 [base + base mod] [0 <] cleave] inscribe
|
||
[sub-with-carry sub-with-carry.0 sub-with-carry.1] inscribe
|
||
</code></pre>
|
||
<h3><code>sub-carry-from-digits</code></h3>
|
||
<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] [R0] [i cons] genrec
|
||
</code></pre>
|
||
<p>That leaves the recursive branch:</p>
|
||
<pre><code>true [n ...] R0 [sub-carry-from-digits] i cons
|
||
</code></pre>
|
||
<p>-or-</p>
|
||
<pre><code>true [] R0 [sub-carry-from-digits] i cons
|
||
</code></pre>
|
||
<p><strong>Except</strong> that this latter case 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.</p>
|
||
<pre><code> true [a ...] R0 [sub-carry-from-digits] i cons
|
||
----------------------------------------------------------------
|
||
true 0 a sub-with-carry [...] [sub-carry-from-digits] i cons
|
||
------------------------------------------------------------------
|
||
(a-1) carry [...] [sub-carry-from-digits] i cons
|
||
</code></pre>
|
||
<p>It would work like this:</p>
|
||
<pre><code>true [a ...] 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 [...]
|
||
|
||
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>R0 == uncons 0 swap [sub-with-carry] dip
|
||
</code></pre>
|
||
<h3>≡ <code>sub-carry-from-digits</code></h3>
|
||
<pre><code>[sub-carry-from-digits.R0 uncons 0 swap [sub-with-carry] dip] inscribe
|
||
[sub-carry-from-digits [pop not] [popd] [sub-carry-from-digits.R0] [i cons] genrec] inscribe
|
||
</code></pre>
|
||
<p>Try it out:</p>
|
||
<pre><code>joy? clear false [3 2 1] sub-carry-from-digits
|
||
[3 2 1]
|
||
|
||
joy? clear true [0 1] sub-carry-from-digits
|
||
[9 0]
|
||
|
||
joy? clear true [3 2 1] sub-carry-from-digits
|
||
[2 2 1]
|
||
|
||
joy? clear true [0 0 1] sub-carry-from-digits
|
||
[9 9 0]
|
||
</code></pre>
|
||
<p>But what about those leading zeroes?</p>
|
||
<h3>≡ <code>cons-but-not-leading-zeroes</code> and <code>sub-carry-from-digits</code></h3>
|
||
<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] inscribe
|
||
[sub-carry-from-digits [pop not] [popd] [sub-carry-from-digits.R0] [i cons-but-not-leading-zeroes] genrec] inscribe
|
||
</code></pre>
|
||
<p>Good enough:</p>
|
||
<pre><code>joy? clear true [0 1] sub-carry-from-digits
|
||
[9]
|
||
|
||
joy? clear true [0 0 1] sub-carry-from-digits
|
||
[9 9]
|
||
</code></pre>
|
||
<h1>======================================================</h1>
|
||
<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 <=] [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] [>=] 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 & 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 [>=] infrst] [pop true]] [[rest] swoncat ifte] genrec]
|
||
[xR1 uncons-two [unit cons swons] dipd]
|
||
[xP [bool] ii & not]
|
||
[BASE [bool] [popop pop true] [[pop bool] [popop pop false] [popop compare-pairs] ifte] ifte]
|
||
[gt-bignum <<{} [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 <] 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 &
|
||
one-of? == boolii |
|
||
</code></pre>
|
||
<script>var joy_interpreter = Elm.Main.init({node: document.getElementById('joy_interpreter')});</script>
|
||
</body>
|
||
</html>
|