diff --git a/regex/base10-inc.sed b/regex/base10-inc.sed index d1f32b6..c924d97 100644 --- a/regex/base10-inc.sed +++ b/regex/base10-inc.sed @@ -23,9 +23,9 @@ # for replacements are only sufficient for M≤3 when K=1; what do we have to # do when M>3 to replace only the digits we are interested in?) # -# It is possible to subtract using the same concept. Further, since we can -# implement addition and subtraction, we can also implement multiplication -# and division. +# It is possible to subtract using the same concept (see `base10-mul.sed'). +# Further, since we can implement addition and subtraction, we can also +# implement multiplication and division. # # TO USE: Provide a 0-padded 3-digit base-10 number. Each time this script # is run, the number will be incremented by one. Exits with exit code 1 if diff --git a/regex/base10-mul.sed b/regex/base10-mul.sed new file mode 100644 index 0000000..690e859 --- /dev/null +++ b/regex/base10-mul.sed @@ -0,0 +1,173 @@ +# Single step in multiplying two base-10 numbers using only regexes +# +# Copyright (C) 2018 Mike Gerwitz +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# This script demonstrates how to multiply two 0-padded 3-digit base-10 +# numbers using nothing more than a series of regular expressions. It +# involves adding and subtracting from a series of registers---each +# invocation of the script performs a single addition step toward the final +# result. +# +# For a lighter introduction, first look at `base10-inc.sed', which shows +# how to implement a basic increment on a single 3-digit base-10 number. +# +# This script uses four 3-digit registers delimited by a single space (for +# legibility; no delimiters are necessary since the registers are +# fixed-width). They are laid out as follows (extra spaces added for +# illustration purposes): +# +# 001 002 003 004 +# 1st operand/ 2nd operand copy of accumulator src +# accumulator 1st operand (decrementing) +# +# The first and second registers hold the first and second operands (the two +# numbers to multiply). The first register doubles as the accumulator---it +# will hold the result of the calculation at each step. The third register +# is always a copy of the first operand; this is necessary since the first +# register is always changing. (Alternatively we could have made the third +# register the accumulator, but that makes the regexes a little messier.) +# The fourth register is the value that the accumulator takes _from_---it +# initially is a copy of register three and is decremented at each +# step. The accumulator is incremented at each step. +# +# Once register four reaches zero, it is reset to the value of register +# three and register two is decremented. Once register two reaches `001', +# the calculation is finished and all but the first register (holding the +# accumulated result) are discarded. +# +# This script is designed to be run in a loop, allowing each step of the +# process to be observed, saved, and manipulated. This could easily be +# changed to use sed's branching features to loop and produce the result in +# a single invocation, but that makes introspection difficult (and the +# output a whole lot less interesting). +# +# TO USE: Provide two 0-padded base-10 integers delimited by a space, or a +# previous state of the system (its output). For example, to perform 5*3, +# provide this initial input: +# +# 005 003 +# +# The output of the first invocation will be: +# +# 006 003 005 004 +# +# To invoke with the animation script, you might do: +# +# $ ./animate base10-mul.sed <( echo 005 003 ) +# +# The process is further detailed below. Have fun! +## + + +# Input must begin with two 3-digit base-10 numbers. We'll ignore trailing +# data for now, since we will be using that as working memory. +/^[0-9]\{3\} [0-9]\{3\}/!q1 + +# If we do not have more than two numbers, then this is the first time we're +# being run and we need to set up our working memory. The leftmost number +# (the first operand) is going to serve as our accumulator, so we will +# duplicate that number. We also need to hold a copy of that operand to +# decrement as we increment the accumulator. So we start with three copies +# of the first operand in the form of `A B A A'. +s/^\([0-9]\{3\}\) .\{3\}$/& \1 \1/ + + +# As a special case, if the second operand is `000', then the result +# is `000'. Set ourselves up so that the next check will terminate. +s/^\(...\) 000/000 001/ + + +# If we have no more iterations to perform (if the second operand in +# register two is `001'), then we are done! If that's the case, clean up +# our output to display only the final result. The script will +# automatically exit with a non-zero status the next iteration because of +# the guard at the top of this script. The trailing spaces ensure that +# leftover output from previous iterations is erased in the animation +# script. +s/^\(...\) 001.*/\1 / +/^... \+$/b + + + +# We will be taking numbers from the last register. Increment the +# accumulator. +s/^\(..\)9/\1A/; s/^\(..\)8/\19/; s/^\(..\)7/\18/; +s/^\(..\)6/\17/; s/^\(..\)5/\16/; s/^\(..\)4/\15/; +s/^\(..\)3/\14/; s/^\(..\)2/\13/; s/^\(..\)1/\12/; +s/^\(..\)0/\11/; + +# Accumulator 10s. +s/^\(.\)9A/\1A0/; s/^\(.\)8A/\190/; s/^\(.\)7A/\180/; +s/^\(.\)6A/\170/; s/^\(.\)5A/\160/; s/^\(.\)4A/\150/; +s/^\(.\)3A/\140/; s/^\(.\)2A/\130/; s/^\(.\)1A/\120/; +s/^\(.\)0A/\110/; + +# Accumulator 100s +s/^9A/A0/; s/^8A/90/; s/^7A/80/; +s/^6A/70/; s/^5A/60/; s/^4A/50/; +s/^3A/40/; s/^2A/30/; s/^1A/20/; +s/^0A/10/; + + + +# We now need to _decrement_ the last register (since the accumulator took a +# single number from it). This is very similar to incrementing, but instead +# of setting a carry flag on overflow, we set it on an _underflow_. +s/\(..\)0$/\1A/; s/\(..\)1$/\10/; s/\(..\)2$/\11/; +s/\(..\)3$/\12/; s/\(..\)4$/\13/; s/\(..\)5$/\14/; +s/\(..\)6$/\15/; s/\(..\)7$/\16/; s/\(..\)8$/\17/; +s/\(..\)9$/\18/; + +# Accumulator 10s. +s/\(.\)0A$/\1A9/; s/\(.\)1A$/\109/; s/\(.\)2A$/\119/; +s/\(.\)3A$/\129/; s/\(.\)4A$/\139/; s/\(.\)5A$/\149/; +s/\(.\)6A$/\159/; s/\(.\)7A$/\169/; s/\(.\)8A$/\179/; +s/\(.\)9A$/\189/; + +# Accumulator 100s. Note that we do not support going below 0. +s/1A\(.\)$/09\1/; s/2A\(.\)$/19\1/; s/3A\(.\)$/29\1/; +s/4A\(.\)$/39\1/; s/5A\(.\)$/49\1/; s/6A\(.\)$/59\1/; +s/7A\(.\)$/69\1/; s/8A\(.\)$/79\1/; s/9A\(.\)$/89\1/; + + + +# If we end up with `000', then we have performed one multiplication +# step. Decrement the second operand (in the second register) to reflect +# this progress. +s/^\(... ..\)0\(.* 000\)/\1A\2/; s/^\(... ..\)1\(.* 000\)/\10\2/; +s/^\(... ..\)2\(.* 000\)/\11\2/; s/^\(... ..\)3\(.* 000\)/\12\2/; +s/^\(... ..\)4\(.* 000\)/\13\2/; s/^\(... ..\)5\(.* 000\)/\14\2/; +s/^\(... ..\)6\(.* 000\)/\15\2/; s/^\(... ..\)7\(.* 000\)/\16\2/; +s/^\(... ..\)8\(.* 000\)/\17\2/; s/^\(... ..\)9\(.* 000\)/\18\2/; + +# Second register 10s. +s/^\(... .\)0A\(.* 000\)/\1A9\2/; s/^\(... .\)1A\(.* 000\)/\109\2/; +s/^\(... .\)2A\(.* 000\)/\119\2/; s/^\(... .\)3A\(.* 000\)/\129\2/; +s/^\(... .\)4A\(.* 000\)/\139\2/; s/^\(... .\)5A\(.* 000\)/\149\2/; +s/^\(... .\)6A\(.* 000\)/\159\2/; s/^\(... .\)7A\(.* 000\)/\169\2/; +s/^\(... .\)8A\(.* 000\)/\179\2/; s/^\(... .\)9A\(.* 000\)/\189\2/; + +# Second register 100s. Note that we do not support going below 0. +s/^\(... \)1A\(.* 000\)/\109\2/; s/^\(... \)2A\(.* 000\)/\119\2/; +s/^\(... \)3A\(.* 000\)/\129\2/; s/^\(... \)4A\(.* 000\)/\139\2/; +s/^\(... \)5A\(.* 000\)/\149\2/; s/^\(... \)6A\(.* 000\)/\159\2/; +s/^\(... \)7A\(.* 000\)/\169\2/; s/^\(... \)8A\(.* 000\)/\179\2/; +s/^\(... \)9A\(.* 000\)/\189\2/; + + +# Otherwise, we must then prepare for the next round by copying the third +# register (the original first operand) back into the fourth. +s/\(...\) 000$/\1 \1/