Computing with Pattern Substitution

algorithms cs

Table of Contents

In "The Art of Computer Programming", Volume 1, Section 1.1, Knuth introduces a formalism for describing algorithms based on pattern-matching and string substitutions.

There each algorithm is made up of computing_with_pattern_substitution_9adf191e16b3c6866624c9fb597702ca8d34280c.svg rules computing_with_pattern_substitution_86b0c21333fc09e64ed11a9bf1a49179a9eb9697.svg and works on a state of the form computing_with_pattern_substitution_9d98ae1ec3c33de15f190c83d636eb46c7d4cd7f.svg . 1

computing_with_pattern_substitution_98bd2aec529c7167576571e4c9c94e7f00ec5a6f.svg and computing_with_pattern_substitution_2f317e72a92b61917afbaf7a3f1dbe4e200dc785.svg are strings over an alphabet computing_with_pattern_substitution_3e9ab3478b9acc1162e589434454cc00b381e885.svg (elements of computing_with_pattern_substitution_0415c42fd27c2d22a4b5e4f1a362f06b556375d0.svg ),
computing_with_pattern_substitution_85a9958d09a5718f4a4947fefe191850858baf64.svg and computing_with_pattern_substitution_f2d4486d23f10014b88e763e96d2bf6c414cc847.svg are numbers in computing_with_pattern_substitution_1e10c3fb5324a8304f456454eb917f61f61060b6.svg .

At each step of the computation, we check if computing_with_pattern_substitution_2f317e72a92b61917afbaf7a3f1dbe4e200dc785.svg contains the pattern computing_with_pattern_substitution_05d3289402005f4874b6455adced71b9560002c5.svg . If so, its first occurrence is replaced with computing_with_pattern_substitution_4f928f33fa2260a8ea2f09f72d2e1ceade3d7f69.svg and computing_with_pattern_substitution_f2d4486d23f10014b88e763e96d2bf6c414cc847.svg of the state is set to computing_with_pattern_substitution_44e6d9bb285bab42dd612ce619ac7541a3a0b84a.svg . Otherwise computing_with_pattern_substitution_fc37bf4b9aebc2b645cdc35cdd14e771d24907d8.svg remains unchanged and computing_with_pattern_substitution_f2d4486d23f10014b88e763e96d2bf6c414cc847.svg is set to computing_with_pattern_substitution_44131652455294805f06a27c5a4c9d5def2cbcfe.svg .

When computing_with_pattern_substitution_6ad420dc6dc90436df1f01a59bdd74b282b2caab.svg the computation terminates, computing_with_pattern_substitution_2f317e72a92b61917afbaf7a3f1dbe4e200dc785.svg is the result.

First Example

Input: computing_with_pattern_substitution_e24934fd5f7387d2d274154a5a9a332222636b0b.svg ( computing_with_pattern_substitution_497f8ee5c66209d02425c9dbbfca25b70dbac458.svg times the character computing_with_pattern_substitution_7c41eff3e5dffe9e398dbb4ed0123ec7f2fcee4e.svg )
Task: Determine if computing_with_pattern_substitution_497f8ee5c66209d02425c9dbbfca25b70dbac458.svg is even.
Output: computing_with_pattern_substitution_edb7426443de38728e393cc472bdcdbdc2bc9593.svg if computing_with_pattern_substitution_497f8ee5c66209d02425c9dbbfca25b70dbac458.svg is even, computing_with_pattern_substitution_c0bb36f605a6b78a6919efcde15c078cad9111f1.svg otherwise.

This can be accomplished using the following three rules with computing_with_pattern_substitution_c4ea7cad7612fbaf19bfe79936acd949759cb786.svg ( computing_with_pattern_substitution_60ec9a3315a23235f68e0e0fa3c16b028af2515b.svg is the empty string):

  • computing_with_pattern_substitution_4e88cf9430aaa4164b078f3eaae5d3ea1d969dbf.svg
  • computing_with_pattern_substitution_6283ec33f8c6622b73c2b8ac8fd5ef47b705b05f.svg
  • computing_with_pattern_substitution_56fd790dab955df78d4758d9aaa75389cd29c54b.svg

Start by removing two computing_with_pattern_substitution_7c3fa29efd5d4d82841a03f7ce86938f4a7aa88d.svg s at a time until it is not possible anymore.
If a single computing_with_pattern_substitution_7c41eff3e5dffe9e398dbb4ed0123ec7f2fcee4e.svg remains output computing_with_pattern_substitution_c0bb36f605a6b78a6919efcde15c078cad9111f1.svg , otherwise output computing_with_pattern_substitution_edb7426443de38728e393cc472bdcdbdc2bc9593.svg .

  1. computing_with_pattern_substitution_ca4da20151ef04f1d04da43fcb10bad92789284d.svg
  2. computing_with_pattern_substitution_319f00de30000eef336af818c4f73d6a122ee0b4.svg

Interpreter

Writing algorithms in this style is tedious because of the need to keep track of the rule indices, using labels instead would make it much easier to write and refactor programs.

I've come up with a simple format

label1
  pattern1 substitution1 label_else1 label_then1
label2
  pattern2 substitution2 label_else2 label_then2
...

pattern and substitution can be strings or _ (the empty string computing_with_pattern_substitution_60ec9a3315a23235f68e0e0fa3c16b028af2515b.svg ) and label_else/then can be one of the other labels or end , a placeholder for computing_with_pattern_substitution_912f4e618488fc4c1098c8c48490a756f8bd8c48.svg .

The rules from before could be rewritten as:

remove_aa
  aa _ check_remaining remove_aa
check_remaining
  a odd output_even end
output_even
  _ even end end

Below is a simple parser that converts this format to rules with indices as computing_with_pattern_substitution_44131652455294805f06a27c5a4c9d5def2cbcfe.svg and computing_with_pattern_substitution_44e6d9bb285bab42dd612ce619ac7541a3a0b84a.svg (plus some bonus features like comments). 2

Rule = Struct.new(:label, :pattern, :substitution, :j_else, :j_then)

def parse(program)
  # Remove comments and empty lines
  pairs = program.lines
    .reject { |l| l.match(/^\s*#.*/) }
    .reject { |l| l.match(/^\s*$/) }
    .map(&:rstrip)
    .each_slice(2)

  # Create a mapping from label to rule index
  labels = Hash.new { |_hash, key| raise "Unknown label #{key}" }
  labels['end'] = -1
  pairs.each_with_index { |(label, _body), i| labels[label] = i }

  # Convert the (label, body) pairs to rules
  pairs.map do |label, body|
    pattern, substitution, l_else, l_then = body.lstrip.split(' ')
    substitution = '' if substitution == '_'
    pattern = '' if pattern == '_'

    Rule.new(label, pattern, substitution, labels[l_else], labels[l_then])
  end
end

The code below is a direct translation of the formal description from earlier with optional logging of each step of the computation.

def run(state, rules, logging = false)
  j, steps = 0, 0

  # Get the length of the longest rule for nicer formatting
  max_length = rules.map{ |r| r.label.length }.max
  until j == -1 do
    rule = rules[j]

    puts "#{rule.label.ljust(max_length, ' ')} | #{state}" if logging

    # Check if `state` contains the `pattern`
    if (index = state.index(rule.pattern))
      state.sub!(rule.pattern, rule.substitution)
      j = rule.j_then
    else
      j = rule.j_else
    end
    steps += 1
  end

  puts "#{'end'.ljust(max_length, ' ')} | #{state}" if logging
  puts "Steps: #{steps}" if logging
  state
end

Let's make sure it works:

def encode_input(n)
  'a' * n
end

run(encode_input(5), parse(program), true)
remove_aa       | aaaaa
remove_aa       | aaa
remove_aa       | a
check_remaining | a
end             | odd
Steps: 4

Greatest Common Divisor

One of the exercises in the book is to think of a set of rules
that calculates computing_with_pattern_substitution_21281d78d4e19a0de78b58eb7d3b481094e26de2.svg given the input computing_with_pattern_substitution_91fca5bec645a340bd97f8e59841a764895b525d.svg using a modified version of Euclid's algorithm .

  1. Set computing_with_pattern_substitution_f44fe83ae90f1c452b9ff68315f3c319fa36c3d3.svg 3
  2. If computing_with_pattern_substitution_bb15dd9045eb39a84f5d446bc08e1b9f2cd41f9c.svg , computing_with_pattern_substitution_497f8ee5c66209d02425c9dbbfca25b70dbac458.svg is the result
  3. Set computing_with_pattern_substitution_f8f473ecb32d160632cc55c7d1c428de87485a5e.svg and go to step 1

After a few hours I've come up with the following solution:

Duplicate the input

copy_as
  a ce copy_bs copy_as
copy_bs
  b df move_cs_left copy_bs
move_cs_left
  ec ce move_ds_left move_cs_left
move_ds_left
  fd df move_ds_left2 move_ds_left
move_ds_left2
  ed de calc_abs_diff move_ds_left2

This converts the state to computing_with_pattern_substitution_44e50374844bf21f7f5acf0877058c66e9521a07.svg and then moves the $c$s and $d$s around to yield computing_with_pattern_substitution_f701c0fb23c2507424dfaafe1b3da23e75bcd78a.svg .

Calculate computing_with_pattern_substitution_25eca24a72ae47e875ae773b419b45741060900e.svg

calc_abs_diff
  cd _ test_r_zero_c calc_abs_diff
test_r_zero_c
  c c test_r_zero_d calc_min
test_r_zero_d
  d d return_r_remove_es calc_min
return_r_remove_es
  e _ return_r_convert_fa return_r_remove_es
return_r_convert_fa
  f a end return_r_convert_fa

Delete $cd$s until either computing_with_pattern_substitution_0347747450cf618700b2456a5949c50db804a943.svg or computing_with_pattern_substitution_f86c100a90c4027f1e638ffc2297e48533578309.svg with computing_with_pattern_substitution_99bf65f193fe5eaa68ed9f37d35f200c7e23b33e.svg remains.

If both test_r_zero_c and test_r_zero_d fail, computing_with_pattern_substitution_99bf65f193fe5eaa68ed9f37d35f200c7e23b33e.svg is zero and we need to return computing_with_pattern_substitution_2fa256daa390e2d0c5223609e612341fc6e4e997.svg as the result by removing all $e$s and changing all $f$s to $a$s.

After this step, the state is computing_with_pattern_substitution_3773b85b71ec192e1a867843849862500a65433f.svg or computing_with_pattern_substitution_78a1f21168f0b6ab3e5d0a42f85fa922720e1d12.svg .

Calculate computing_with_pattern_substitution_3af04f59cc63ac0a764713cc8a5c51c07722ffb3.svg

If the result of the previous calculation has the form computing_with_pattern_substitution_f86c100a90c4027f1e638ffc2297e48533578309.svg computing_with_pattern_substitution_497f8ee5c66209d02425c9dbbfca25b70dbac458.svg must be greater than $m$
(there were more $d$s than $c$s), remove the $f$s and convert the $e$s to $a$s. Otherwise, remove the $e$s and convert the $f$s to $a$s.

calc_min
  d d remove_es remove_fs
remove_es
  e _ convert_fa remove_es
convert_fa
  f a convert_cb convert_fa
remove_fs
  f _ convert_ea remove_fs
convert_ea
  e a convert_cb convert_ea

After this step, the state is computing_with_pattern_substitution_bea564c844673dc5bdc5288de1c94b7f20bad5ae.svg or computing_with_pattern_substitution_f738626a45038adcd4091a340f33fbf3b69b2226.svg .

Next iteration

First change the computing_with_pattern_substitution_0347747450cf618700b2456a5949c50db804a943.svg or computing_with_pattern_substitution_f86c100a90c4027f1e638ffc2297e48533578309.svg to computing_with_pattern_substitution_2cd4b357ec346c59923305c4b679e47718b0bc2b.svg , then move the $a$s to the front.

convert_cb
  c b convert_db convert_cb
convert_db
  d b move_as_left convert_db
move_as_left
  ba ab copy_as move_as_left

After this step, the state is computing_with_pattern_substitution_142fac6abcdd78a656706f99d8174e3ea67a55f1.svg ( computing_with_pattern_substitution_91fca5bec645a340bd97f8e59841a764895b525d.svg with computing_with_pattern_substitution_8b4b65e269accd15cf74779fa538bad9f2f174a6.svg and computing_with_pattern_substitution_8c036acd870486032981ce29febfa5aefffc0852.svg ) and we can jump back to the start ( copy_as ).

Output

A log of the 75 steps needed to compute computing_with_pattern_substitution_37f5afccfa44a9b76fbe53af8b0095bebcee701c.svg :

copy_as             | aabbbb
copy_as             | ceabbbb
copy_as             | cecebbbb
copy_bs             | cecebbbb
copy_bs             | cecedfdfdfdf
move_cs_left        | cecedfdfdfdf
move_cs_left        | cceedfdfdfdf
move_ds_left        | cceedfdfdfdf
move_ds_left        | cceeddffdfdf
move_ds_left        | cceeddfdffdf
move_ds_left        | cceedddfffdf
move_ds_left        | cceedddffdff
move_ds_left        | cceedddfdfff
move_ds_left        | cceeddddffff
move_ds_left2       | cceeddddffff
move_ds_left2       | ccededddffff
move_ds_left2       | ccdeedddffff
move_ds_left2       | ccdededdffff
move_ds_left2       | ccddeeddffff
move_ds_left2       | ccddededffff
move_ds_left2       | ccdddeedffff
move_ds_left2       | ccdddedeffff
move_ds_left2       | ccddddeeffff
calc_abs_diff       | ccddddeeffff
calc_abs_diff       | cdddeeffff
calc_abs_diff       | ddeeffff
test_r_zero_c       | ddeeffff
test_r_zero_d       | ddeeffff
calc_min            | ddeeffff
remove_fs           | ddeeffff
remove_fs           | ddeefff
remove_fs           | ddeeff
remove_fs           | ddeef
remove_fs           | ddee
convert_ea          | ddee
convert_ea          | ddae
convert_ea          | ddaa
convert_cb          | ddaa
convert_db          | ddaa
convert_db          | bdaa
convert_db          | bbaa
move_as_left        | bbaa
move_as_left        | baba
move_as_left        | abba
move_as_left        | abab
move_as_left        | aabb
copy_as             | aabb
copy_as             | ceabb
copy_as             | cecebb
copy_bs             | cecebb
copy_bs             | cecedfb
copy_bs             | cecedfdf
move_cs_left        | cecedfdf
move_cs_left        | cceedfdf
move_ds_left        | cceedfdf
move_ds_left        | cceeddff
move_ds_left2       | cceeddff
move_ds_left2       | ccededff
move_ds_left2       | ccdeedff
move_ds_left2       | ccdedeff
move_ds_left2       | ccddeeff
calc_abs_diff       | ccddeeff
calc_abs_diff       | cdeeff
calc_abs_diff       | eeff
test_r_zero_c       | eeff
test_r_zero_d       | eeff
return_r_remove_es  | eeff
return_r_remove_es  | eff
return_r_remove_es  | ff
return_r_convert_fa | ff
return_r_convert_fa | af
return_r_convert_fa | aa
end                 | aa

As expected the result is computing_with_pattern_substitution_6cf8e86d6f26e179862ea1d0094649787248d3cf.svg (or rather computing_with_pattern_substitution_74c3843775edd58c6443e456adcfb69a9d5746ad.svg ).

A Better Solution

To my surprise, Knuth's solution to this problem has only five rules. What is he doing differently?

One key observation is that the step calc_abs_diff to calculate computing_with_pattern_substitution_25eca24a72ae47e875ae773b419b45741060900e.svg is executed exactly computing_with_pattern_substitution_3af04f59cc63ac0a764713cc8a5c51c07722ffb3.svg times, with some careful coding both values can be calculated simultaneously.

Furthermore, computing_with_pattern_substitution_beea64ad42ac00dafc402bc45b6033ba61795d6c.svg , so there is no need to keep a copy of the original values around.

Implementation

Start by replacing $ab$s with computing_with_pattern_substitution_711de6e78f845f235dd9e7641c4f1513a6c00506.svg and moving the computing_with_pattern_substitution_711de6e78f845f235dd9e7641c4f1513a6c00506.svg to the left.

start
  ab c convert_ab move_c_left
move_c_left
  ac ca start move_c_left

After this, the state is either computing_with_pattern_substitution_c020b0a4c914d111e6fa6219c75487dae9e7eb77.svg or computing_with_pattern_substitution_df6595ceb2c22aa9f7afab71f396c9665ecc2f88.svg .

convert_ab
  a b convert_ca convert_ab
convert_ca
  c a test_r_zero convert_ca
test_r_zero
  b b end start

Then rename the $a$s to $b$s and the $c$s to $a$s. 4

Now the state is computing_with_pattern_substitution_947e1740390103e9002804aa131d2b5e3e2c94e0.svg .
If there are no $b$s in the state, computing_with_pattern_substitution_f965339707fde0e35ba06592f26f63ef61c8dcbe.svg must be zero, and we can jump to end because computing_with_pattern_substitution_b1fd11fe965290df7619e2c47cb541038d81e4da.svg is the correct result computing_with_pattern_substitution_497f8ee5c66209d02425c9dbbfca25b70dbac458.svg .

In this improved version computing_with_pattern_substitution_37f5afccfa44a9b76fbe53af8b0095bebcee701c.svg only needs 22 steps:

start       | aabbbb
move_c_left | acbbb
move_c_left | cabbb
start       | cabbb
move_c_left | ccbb
start       | ccbb
convert_ab  | ccbb
convert_ca  | ccbb
convert_ca  | acbb
convert_ca  | aabb
test_r_zero | aabb
start       | aabb
move_c_left | acb
move_c_left | cab
start       | cab
move_c_left | cc
start       | cc
convert_ab  | cc
convert_ca  | cc
convert_ca  | ac
convert_ca  | aa
test_r_zero | aa
end         | aa

One Step Further: Primality Testing

Let's try to solve one more problem: given an input computing_with_pattern_substitution_e24934fd5f7387d2d274154a5a9a332222636b0b.svg , determine whether computing_with_pattern_substitution_497f8ee5c66209d02425c9dbbfca25b70dbac458.svg is a prime number or not.

There are many (and easier) ways of doing this, but to show how algorithms in this formalism can be combined to solve more complicated problems, I'll use the computing_with_pattern_substitution_d9957af070e83c10051c6460086b0479b34c6008.svg procedure from the previous section.

A number computing_with_pattern_substitution_497f8ee5c66209d02425c9dbbfca25b70dbac458.svg is prime if each number computing_with_pattern_substitution_4396aaa1b7142888f64e51b21dd27a81ba225c59.svg from computing_with_pattern_substitution_6cf8e86d6f26e179862ea1d0094649787248d3cf.svg to computing_with_pattern_substitution_7f8ba2cdf5207d812f77e4b18f62ec1abcf47664.svg is coprime to it ( computing_with_pattern_substitution_4acf2127017b69fb2a683b66358f403efa1b2156.svg ). In our formalism the check computing_with_pattern_substitution_c6cdcc7aeba2377cc77932aeffdbe90d478590c7.svg is hard to do, instead we can count down from computing_with_pattern_substitution_d4e2fa6fc158c3b78c524d1fea2682438a0e4f1b.svg .

  1. Count down a value computing_with_pattern_substitution_4396aaa1b7142888f64e51b21dd27a81ba225c59.svg from computing_with_pattern_substitution_9af24f9e1fd87d2102e8e15da52563ebac2484b4.svg to computing_with_pattern_substitution_8fc2434f4c4785d9b41c1b65bb44608802ca451e.svg
  2. If computing_with_pattern_substitution_48558e0be7f860a7a6a59a48ecc73bcb24ca777f.svg return computing_with_pattern_substitution_737357a0c749fe8d92c52fae349e508433906f06.svg
  3. At each step, if computing_with_pattern_substitution_d2d55b11dc1228bb45883a92ab1c5774e29b895c.svg return computing_with_pattern_substitution_f3f5db41f469ce72d281ab0ab546bcf1d9992848.svg , otherwise continue with step 1

Implementation

First, we need to handle the special case computing_with_pattern_substitution_bf902f4efbd439216f02b98c9345217dd916c5f8.svg and copy the $a$s to create the counter computing_with_pattern_substitution_4396aaa1b7142888f64e51b21dd27a81ba225c59.svg .

check1
  aa aa np_clear_c copy_input
copy_input
  a cd move_d_right copy_input
move_d_right
  dc cd subtract1 move_d_right

Then we decrement computing_with_pattern_substitution_4396aaa1b7142888f64e51b21dd27a81ba225c59.svg (it starts at computing_with_pattern_substitution_7f8ba2cdf5207d812f77e4b18f62ec1abcf47664.svg ) and output computing_with_pattern_substitution_737357a0c749fe8d92c52fae349e508433906f06.svg if the result is one.

subtract1
  dd d check_done check_done
check_done
  dd dd p_clear_c copy_c

p_clear_c and its counterpart np_clear_c clear the state and output either computing_with_pattern_substitution_737357a0c749fe8d92c52fae349e508433906f06.svg or computing_with_pattern_substitution_f3f5db41f469ce72d281ab0ab546bcf1d9992848.svg .

p_clear_c
  c _ p_clear_d p_clear_c
p_clear_d
  d _ p_clear_a p_clear_d
p_clear_a
  a _ p_output p_clear_a
p_output
  _ prime end end

np_clear_c
  c _ np_clear_d np_clear_c
np_clear_d
  d _ np_clear_a np_clear_d
np_clear_a
  a _ np_output np_clear_a
np_output
  _ notprime end end

Because the computing_with_pattern_substitution_d9957af070e83c10051c6460086b0479b34c6008.svg procedure destroys the state, we need to make a copy of computing_with_pattern_substitution_4396aaa1b7142888f64e51b21dd27a81ba225c59.svg and computing_with_pattern_substitution_497f8ee5c66209d02425c9dbbfca25b70dbac458.svg .

This is done by converting the state computing_with_pattern_substitution_8a961022a30943a56a3642cc3aa7ea83d15b326f.svg to computing_with_pattern_substitution_6b6233cfd1648b68ae426ad777c683d0c460ab32.svg , moving the $a$s and $b$s to the center and then renaming computing_with_pattern_substitution_9f6043c45097537581bf745ab75dbe3e2a0a56c1.svg back to computing_with_pattern_substitution_4e57c168dad688bc3e01afef6544a5aee0ecc7dd.svg . 5

copy_c
  c Ca copy_d copy_c
copy_d
  d bD move_a_right copy_d
move_a_right
  aC Ca move_b_left move_a_right
move_b_left
  Db bD convert_Cc move_b_left
convert_Cc
  C c convert_Dd convert_Cc
convert_Dd
  D d start_gcd convert_Dd

Now the state has the form computing_with_pattern_substitution_1fc06736673a8793de32ec6c32e6ff671156ade5.svg and we can call the computing_with_pattern_substitution_d9957af070e83c10051c6460086b0479b34c6008.svg procedure from the previous section. 6

start_gcd
  ab e convert_ab move_e_left
move_e_left
  ae ea start_gcd move_e_left
convert_ab
  a b convert_ea convert_ab
convert_ea
  e a test_r_zero convert_ea
test_r_zero
  b b test_coprime start_gcd

If we can't find the pattern computing_with_pattern_substitution_3f61bdcb428dbfd233bf5283a9c5a04b980a46d5.svg afterwards, the numbers are coprime, and we can continue with computing_with_pattern_substitution_748010312c627733395753c635db1b42ad32e338.svg , otherwise computing_with_pattern_substitution_4396aaa1b7142888f64e51b21dd27a81ba225c59.svg is not a prime.

test_coprime
  aa aa next np_clear_c
next
  a _ subtract1 subtract1

Even for small numbers this needs a lot of steps and I've removed some boring sections from the output:

check1       | aaaa
copy_input   | aaaa
copy_input   | cdaaa
copy_input   | cdcdaa
copy_input   | cdcdcda
copy_input   | cdcdcdcd
move_d_right | cdcdcdcd
...
move_d_right | ccccdddd
subtract1    | ccccdddd
check_done   | ccccddd
copy_c       | ccccddd
...
convert_Dd   | ccccaaaabbbddd
start_gcd    | ccccaaaabbbddd
move_e_left  | ccccaaaebbddd
move_e_left  | ccccaaeabbddd
move_e_left  | ccccaeaabbddd
move_e_left  | cccceaaabbddd
start_gcd    | cccceaaabbddd
move_e_left  | cccceaaebddd
move_e_left  | cccceaeabddd
move_e_left  | cccceeaabddd
start_gcd    | cccceeaabddd
move_e_left  | cccceeaeddd
move_e_left  | cccceeeaddd
start_gcd    | cccceeeaddd
convert_ab   | cccceeeaddd
convert_ab   | cccceeebddd
convert_ea   | cccceeebddd
convert_ea   | ccccaeebddd
convert_ea   | ccccaaebddd
convert_ea   | ccccaaabddd
test_r_zero  | ccccaaabddd
start_gcd    | ccccaaabddd
move_e_left  | ccccaaeddd
move_e_left  | ccccaeaddd
move_e_left  | cccceaaddd
start_gcd    | cccceaaddd
convert_ab   | cccceaaddd
convert_ab   | ccccebaddd
convert_ab   | ccccebbddd
convert_ea   | ccccebbddd
convert_ea   | ccccabbddd
test_r_zero  | ccccabbddd
start_gcd    | ccccabbddd
move_e_left  | ccccebddd
start_gcd    | ccccebddd
convert_ab   | ccccebddd
convert_ea   | ccccebddd
convert_ea   | ccccabddd
test_r_zero  | ccccabddd
start_gcd    | ccccabddd
move_e_left  | cccceddd
start_gcd    | cccceddd
convert_ab   | cccceddd
convert_ea   | cccceddd
convert_ea   | ccccaddd
test_r_zero  | ccccaddd
test_coprime | ccccaddd
next         | ccccaddd
subtract1    | ccccddd
check_done   | ccccdd
copy_c       | ccccdd
...
convert_Dd   | ccccaaaabbdd
start_gcd    | ccccaaaabbdd
move_e_left  | ccccaaaebdd
move_e_left  | ccccaaeabdd
move_e_left  | ccccaeaabdd
move_e_left  | cccceaaabdd
start_gcd    | cccceaaabdd
move_e_left  | cccceaaedd
move_e_left  | cccceaeadd
move_e_left  | cccceeaadd
start_gcd    | cccceeaadd
convert_ab   | cccceeaadd
convert_ab   | cccceebadd
convert_ab   | cccceebbdd
convert_ea   | cccceebbdd
convert_ea   | ccccaebbdd
convert_ea   | ccccaabbdd
test_r_zero  | ccccaabbdd
start_gcd    | ccccaabbdd
move_e_left  | ccccaebdd
move_e_left  | cccceabdd
start_gcd    | cccceabdd
move_e_left  | cccceedd
start_gcd    | cccceedd
convert_ab   | cccceedd
convert_ea   | cccceedd
convert_ea   | ccccaedd
convert_ea   | ccccaadd
test_r_zero  | ccccaadd
test_coprime | ccccaadd
np_clear_c   | ccccaadd
...
np_output    |
end          | notprime

For larger numbers and primes the results are correct, too, but I won't include the logs here.

Footnotes

1

In the book the parts are named differently

2

computing_with_pattern_substitution_ee422372d010f0b86a9df1b6af13199958564268.svg is used instead of computing_with_pattern_substitution_912f4e618488fc4c1098c8c48490a756f8bd8c48.svg to signify the end of the computation, this way we don't need to know how many rules there are

3

computing_with_pattern_substitution_c3fcbbd3c1ec1c7892e3441c68232cc133520330.svg is the absolute value, e.g. computing_with_pattern_substitution_98bc21b42d301e1f737ce275a7c8bde656a05386.svg and computing_with_pattern_substitution_86bd554787b3c50f93cd647065ab7b0227e27c1c.svg

4

convert_ab is only needed in the case computing_with_pattern_substitution_1e187d7791d9fe231f1f4c61a8542bc7ddd2614c.svg

5

Renaming them in the first place is necessary to avoid an endless loop in the rule

6

Altered slightly to use computing_with_pattern_substitution_572b573fb52767f34b23eb15711d4208096392d8.svg instead of computing_with_pattern_substitution_711de6e78f845f235dd9e7641c4f1513a6c00506.svg

Backlinks


If you have an idea how this page could be improved or a comment send me a mail.