from itertools import count, islice, takewhile
from math import gcd

def EKG_gen(start=2):
    """\
    Generate the next term of the EKG together with the minimum cache of
    numbers left in its production; (the "state" of the generator).
    Using math.gcd
    """
    c = count(start + 1)
    last, so_far = start, list(range(2, start))
    yield 1, []
    yield last, []
    while True:
        for index, sf in enumerate(so_far):
            if gcd(last, sf) > 1:
                last = so_far.pop(index)
                yield last, so_far[::]
                break
        else:
            so_far.append(next(c))

def find_convergence(ekgs=(5,7)):
    "Returns the convergence point or zero if not found within the limit"
    ekg = [EKG_gen(n) for n in ekgs]
    for e in ekg:
        next(e)    # skip initial 1 in each sequence
    return 2 + len(list(takewhile(lambda state: not all(state[0] == s for  s in state[1:]),
                                  zip(*ekg))))

if __name__ == '__main__':
    for start in 2, 5, 7, 9, 10:
        print(f"EKG({start}):", str([n[0] for n in islice(EKG_gen(start), 10)])[1: -1])
    print(f"\nEKG(5) and EKG(7) converge at term {find_convergence(ekgs=(5,7))}!")


sub infix:<shares-divisors-with> { ($^a gcd $^b) > 1 }

sub next-EKG ( *@s ) {
    return first {
        @s ∌ $_  and  @s.tail shares-divisors-with $_
    }, 2..*;
}

sub EKG ( Int $start ) {  1, $start, &next-EKG … *  }

sub converge-at ( @ints ) {
    my @ekgs = @ints.map: &EKG;

    return (2 .. *).first: -> $i {
        [==]  @ekgs.map(     *.[$i]     ) and
        [===] @ekgs.map( *.head($i).Set )
    }
}

say "EKG($_): ", .&EKG.head(10) for 2, 5, 7, 9, 10;

for [5, 7], [2, 5, 7, 9, 10] -> @ints {
    say "EKGs of (@ints[]) converge at term {$_+1}" with converge-at(@ints);
}


/*REXX program can  generate and display several  EKG  sequences  (with various starts).*/
parse arg nums start                             /*obtain optional arguments from the CL*/
if  nums=='' |  nums==","  then  nums= 50        /*Not specified?  Then use the default.*/
if start= '' | start= ","  then start=2 5 7 9 10 /* "      "         "   "   "     "    */

     do s=1  for words(start);   $=              /*step through the specified  STARTs.  */
     second= word(start, s);     say             /*obtain the second integer in the seq.*/

         do j=1  for nums
         if j<3  then do; #=1;  if j==2  then #=second;  end   /*handle 1st & 2nd number*/
                 else #= ekg(#)
         $= $ right(#,  max(2, length(#) ) )     /*append the EKG integer to the $ list.*/
         end   /*j*/                             /* [↑] the RIGHT BIF aligns the numbers*/
     say '(start'  right(second,  max(2, length(second) ) )"):"$      /*display EKG seq.*/
     end       /*s*/
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
add_:   do  while z//j == 0;    z=z%j;    _=_ j;    w=w+1;    end;         return strip(_)
/*──────────────────────────────────────────────────────────────────────────────────────*/
ekg: procedure expose $; parse arg x 1 z,,_
     w=0                                                        /*W:  number of factors.*/
             do k=1  to 11  by 2;     j=k;  if j==1  then j=2   /*divide by low primes. */
             if j==9  then iterate;   call add_                 /*skip ÷ 9; add to list.*/
             end   /*k*/
                                                                /*↓ skips multiples of 3*/
             do y=0  by 2;  j= j + 2 + y//4                     /*increment J by 2 or 4.*/
             parse var  j  ''  -1  r;  if r==5  then iterate    /*divisible by five ?   */
             if j*j>x | j>z  then leave                         /*passed the sqrt(x) ?  */
             _= add_()                                          /*add a factor to list. */
             end   /*y*/
     j=z;                    if z\==1  then _= add_()           /*Z¬=1? Then add──►list.*/
     if _=''  then _=x                                          /*Null? Then use prime. */
                 do   j=3;                          done=1
                   do k=1  for w
                   if j // word(_, k)==0  then do;  done=0;  leave;  end
                   end   /*k*/
                 if done  then iterate
                 if wordpos(j, $)==0  then return j             /*return an EKG integer.*/
                 end     /*j*/


class Seq(terms, callback) {
    method next {
        terms += callback(terms)
    }

    method nth(n) {
        while (terms.len < n) {
            self.next
        }
        terms[n-1]
    }

    method first(n) {
        while (terms.len < n) {
            self.next
        }
        terms.first(n)
    }
}

func next_EKG (s) {
    2..Inf -> first {|k|
        !(s.contains(k) || s[-1].is_coprime(k))
    }
}

func EKG (start) {
    Seq([1, start], next_EKG)
}

func converge_at(ints) {
    var ekgs = ints.map(EKG)

    2..Inf -> first {|k|
        (ekgs.map { .nth(k)        }.uniq.len == 1) &&
        (ekgs.map { .first(k).sort }.uniq.len == 1)
    }
}

for k in [2, 5, 7, 9, 10] {
    say "EKG(#{k}) = #{EKG(k).first(10)}"
}

for arr in [[5,7], [2, 5, 7, 9, 10]] {
    var c = converge_at(arr)
    say "EKGs of #{arr} converge at term #{c}"
}


fn gcd(aa int, bb int) int {
    mut a,mut b:=aa,bb
    for a != b {
        if a > b {
            a -= b
        } else {
            b -= a
        }
    }
    return a
}

fn are_same(ss []int, tt []int) bool {
    mut s,mut t:=ss.clone(),tt.clone()
    le := s.len
    if le != t.len {
        return false
    }
    s.sort()
    t.sort()
    for i in 0..le {
        if s[i] != t[i] {
            return false
        }
    }
    return true
}
const limit = 100
fn main() {
    starts := [2, 5, 7, 9, 10]
    mut ekg := [5][limit]int{}

    for s, start in starts {
        ekg[s][0] = 1
        ekg[s][1] = start
        for n in 2..limit {
            for i := 2; ; i++ {
                // a potential sequence member cannot already have been used
                // and must have a factor in common with previous member
                if !ekg[s][..n].contains(i) && gcd(ekg[s][n-1], i) > 1 {
                    ekg[s][n] = i
                    break
                }
            }
        }
        println("EKG(${start:2}): ${ekg[s][..30]}")
    }

    // now compare EKG5 and EKG7 for convergence
    for i in 2..limit {
        if ekg[1][i] == ekg[2][i] && are_same(ekg[1][..i], ekg[2][..i]) {
            println("\nEKG(5) and EKG(7) converge at term ${i+1}")
            return
        }
    }
    println("\nEKG5(5) and EKG(7) do not converge within $limit terms")
}


import "/sort" for Sort
import "/math" for Int
import "/fmt" for Fmt

var areSame = Fn.new { |s, t|
    var le = s.count
    if (le != t.count) return false
    Sort.quick(s)
    Sort.quick(t)
    for (i in 0...le) if (s[i] != t[i]) return false
    return true
}

var limit = 100
var starts = [2, 5, 7, 9, 10]
var ekg = List.filled(5, null)
for (i in 0..4) ekg[i] = List.filled(limit, 0)
var s = 0
for (start in starts) {
    ekg[s][0] = 1
    ekg[s][1] = start
    for (n in 2...limit) {
        var i = 2
        while (true) {
            // a potential sequence member cannot already have been used
            // and must have a factor in common with previous member
            if (!ekg[s].take(n).contains(i) && Int.gcd(ekg[s][n-1], i) > 1) {
                ekg[s][n] = i
                break
            }
            i = i + 1
        }
    }
    Fmt.print("EKG($2d): $2d", start, ekg[s].take(30).toList)
    s = s + 1
}

// now compare EKG5 and EKG7 for convergence
for (i in 2...limit) {
    if (ekg[1][i] == ekg[2][i] && areSame.call(ekg[1][0...i], ekg[2][0...i])) {
        System.print("\nEKG(5) and EKG(7) converge at term %(i+1).")
        return
    }
}
System.print("\nEKG5(5) and EKG(7) do not converge within %(limit) terms.")


int  N, A(1+30);

func Used; int M;       \Return 'true' if M is in array A
int  I;
[for I:= 1 to N-1 do
        if M = A(I) then return true;
return false;
];

func MinFactor; int Num; \Return minimum unused factor
int  Fac, Val, Min;
[Fac:= 2;
Min:= -1>>1;
repeat  if rem(Num/Fac) = 0 then \found a factor
                [Val:= Fac;
                loop    [if Used(Val) then Val:= Val+Fac
                        else    [if Val<Min then Min:= Val;
                                quit;
                                ];
                        ];
                Num:= Num/Fac;
                ]
        else    Fac:= Fac+1;
until   Fac > Num;
return Min;
];

proc EKG; int M;        \Calculate and show EKG sequence
[A(1):= 1;  A(2):= M;
for N:= 3 to 30 do
        A(N):= MinFactor(A(N-1));
Format(2, 0);
Text(0, "EKG(");  RlOut(0, float(M));  Text(0, "):");
Format(3, 0);
for N:= 1 to 30 do
        RlOut(0, float(A(N)));
CrLf(0);
];

int Tbl, I;
[Tbl:= [2, 5, 7, 9, 10];
for I:= 0 to 4 do EKG(Tbl(I));
]


fcn ekgW(N){	// --> iterator
   Walker.tweak(fcn(rp,buf,w){
      foreach n in (w){
	 if(rp.value.gcd(n)>1)
	    { rp.set(n); w.push(buf.xplode()); buf.clear(); return(n); }
	 buf.append(n);  // save small numbers not used yet
      }
   }.fp(Ref(N),List(),Walker.chain([2..N-1],[N+1..]))).push(1,N)
}


V SIZE = 32
V LINES = SIZE I/ 2
V RULE = 90

F ruleTest(x)
   R I :RULE [&] (1 << (7 [&] x)) != 0 {1} E 0

F bitVal(s, bit)
   R I (s >> bit) [&] 1 != 0 {1} E 0

F evolve(&s)
   V t = 0
   t [|]= ruleTest((bitVal(s, 0) << 2) [|] (bitVal(s, :SIZE - 1) << 1) [|] bitVal(s, :SIZE - 2)) << (:SIZE - 1)
   t [|]= ruleTest((bitVal(s, 1) << 2) [|] (bitVal(s, 0) << 1) [|] bitVal(s, :SIZE - 1))
   L(i) 1 .< :SIZE - 1
      t [|]= ruleTest((bitVal(s, i + 1) << 2) [|] (bitVal(s, i) << 1) [|] bitVal(s, i - 1)) << i
   s = t

F show(state)
   L(i) (:SIZE - 1 .. 0).step(-1)
      print(‘ *’[bitVal(state, i)], end' ‘’)
   print()

V state = 1 << LINES
print(‘Rule ’RULE)
L 1..LINES
   show(state)
   evolve(&state)


DEFINE ROW_LEN="320"
DEFINE MAX_COL="319"
DEFINE MAX_ROW="191"

PROC GenerateMask(BYTE rule BYTE ARRAY mask)
  BYTE i

  FOR i=0 TO 7
  DO
    mask(i)=rule&1
    rule==RSH 1
  OD
RETURN

PROC InitRow(BYTE ARRAY row)
  INT c

  FOR c=0 TO MAX_COL
  DO
    row(c)=0
  OD
  row(ROW_LEN RSH 1)=1
RETURN

PROC DrawRow(BYTE ARRAY row BYTE y)
  INT c

  FOR c=0 TO MAX_COL
  DO
    Color=row(c)
    Plot(c,y)
  OD
RETURN

PROC CalcNextRow(BYTE ARRAY currRow,nextRow,mask)
  INT c
  BYTE v

  v=currRow(MAX_COL) LSH 2
  v==%currRow(0) LSH 1
  v==%currRow(1)
  nextRow(0)=mask(v)
  FOR c=1 TO MAX_COL-1
  DO
    v==&3
    v==LSH 1
    v==%currRow(c+1)
    nextRow(c)=mask(v)
  OD
  v==&3
  v==LSH 1
  v==%currRow(0)
  nextRow(MAX_COL)=mask(v)
RETURN

PROC DrawRule(BYTE rule)
  BYTE ARRAY row1(ROW_LEN),row2(ROW_LEN),mask(8)
  BYTE ARRAY currRow,nextRow,tmp
  BYTE y

  GenerateMask(rule,mask)
  currRow=row1
  nextRow=row2
  InitRow(currRow)
  y=0
  WHILE y<=MAX_ROW
  DO
    DrawRow(currRow,y)
    CalcNextRow(currRow,nextRow,mask)
    tmp=currRow currRow=nextRow nextRow=tmp
    y==+1
  OD
RETURN

PROC Main()
  BYTE CH=$02FC,COLOR1=$02C5,COLOR2=$02C6

  Graphics(8+16)
  Color=1
  COLOR1=$0C
  COLOR2=$02

  DrawRule(30)

  DO UNTIL CH#$FF OD
  CH=$FF
RETURN


with Ada.Text_IO;
procedure Elementary_Cellular_Automaton is

   type t_Rule  is new Integer range 0..2**8-1;
   type t_State is array (Integer range <>) of Boolean;

   Cell_Image : constant array (Boolean) of Character := ('.', '#');

   function Image (State : in t_State) return String is
     (Cell_Image(State(State'First)) &
      (if State'Length <= 1 then ""
       else Image(State(State'First+1..State'Last))));

   -- More convenient representation of the rule
   type t_RuleA is array (Boolean, Boolean, Boolean) of Boolean;

   function Translate (Rule : in t_Rule) return t_RuleA is
      -- Better not use Pack attribute and Unchecked_Conversion
      -- because it would not be endianness independent...
      Remain : t_Rule := Rule;
   begin
      return Answer : t_RuleA do
         for K in Boolean loop
            for J in Boolean loop
               for I in Boolean loop
                  Answer(I,J,K) := (Remain mod 2 = 1);
                  Remain := Remain / 2;
               end loop;
            end loop;
         end loop;
      end return;
   end Translate;

   procedure Show_Automaton (Rule        : in t_Rule;
                             Initial     : in t_State;
                             Generations : in Positive) is
      RuleA : constant t_RuleA  := Translate(Rule);
      Width : constant Positive := Initial'Length;
      -- More convenient indices for neighbor wraparound with "mod"
      subtype t_State0 is t_State (0..Width-1);
      State     : t_State0 := Initial;
      New_State : t_State0;
   begin
      Ada.Text_IO.Put_Line ("Rule" & t_Rule'Image(Rule) & " :");
      for Generation in 1..Generations loop
         Ada.Text_IO.Put_Line (Image(State));
         for Cell in State'Range loop
            New_State(Cell) := RuleA(State((Cell-1) mod Width),
                                     State(Cell),
                                     State((Cell+1) mod Width));
         end loop;
         State := New_State;
      end loop;
   end Show_Automaton;

begin
   Show_Automaton (Rule        => 90,
                   Initial     => (-10..-1 => False, 0 => True, 1..10 => False),
                   Generations => 15);
   Show_Automaton (Rule        => 30,
                   Initial     => (-15..-1 => False, 0 => True, 1..15 => False),
                   Generations => 20);
   Show_Automaton (Rule        => 122,
                   Initial     => (-12..-1 => False, 0 => True, 1..12 => False),
                   Generations => 25);
end Elementary_Cellular_Automaton;


BEGIN # elementary cellular automaton #
    COMMENT returns the next state from state using rule; s must be at least 2 characters long and consist of # and - only COMMENT
    PROC next state = ( STRING state, INT rule )STRING:
         BEGIN
            COMMENT returns 1 or 0 depending on whether c = # or not COMMENT
            OP TOINT = ( CHAR c )INT: IF c = "#" THEN 1 ELSE 0 FI;
            # construct the state with additional extra elements to allow wrap-around #
            STRING s = state[ UPB state ] + state + state[ LWB state : LWB state + 1 ];
            COMMENT convert rule to a string of # or - depending on whether the bits of r are on or off COMMENT
            STRING r := "";
            INT    v := rule;
            FOR i TO 8 DO
                r +:= IF ODD v THEN "#" ELSE "-" FI;
                v OVERAB 2
            OD;
            STRING new state := "";
            FOR i FROM LWB s TO UPB s - 3 DO
                CHAR c1  = s[ i     ];
                CHAR c2  = s[ i + 1 ];
                CHAR c3  = s[ i + 2 ];
                INT pos := ( ( ( TOINT c1 * 2 ) + TOINT c2 ) * 2 ) + TOINT c3;
                new state +:= r[ pos + 1 ]
            OD;
            new state
         END # next state # ;
    # evolve state until the next state = the current state or 10 iterations have occured #
    PROC test = ( STRING state, INT rule )VOID:
         BEGIN
             print( ( "Rule ", whole( rule, 0 ), newline ) );
             STRING curr := state;
             print( ( " 0: ", curr, newline ) );
             FOR i TO 10 WHILE STRING next = next state( curr, rule );
                               next /= curr
             DO
                 print( ( whole( i, -2 ), ": ", next, newline ) );
                 curr := next
             OD
         END # test # ;
    # tests #
    test( "---------#---------", 30 );
    test( "---------#---------", 60 );
    test( "---------#---------", 90 )
END


state := StrSplit("0000000001000000000")
rule := 90
output := "Rule: " rule
Loop, 10 {
	output .= "`n" A_Index "`t" PrintState(state)
	state := NextState(state, rule)
}
Gui, Font,, Courier New
Gui, Add, Text,, % output
Gui, Show
return

GuiClose:
ExitApp

; Returns the next state based on the current state and rule.
NextState(state, rule) {
	r := ByteDigits(rule)
	result := {}
	for i, val in state {
		if (i = 1)			; The leftmost cell
			result.Insert(r[state[state.MaxIndex()] state.1 state.2])
		else if (i = state.MaxIndex())	; The rightmost cell
			result.Insert(r[state[i-1] val state.1])
		else				; All cells between leftmost and rightmost
			result.Insert(r[state[i - 1] val state[i + 1]])
	}
	return result
}

; Returns an array with each three digit sequence as a key corresponding to a value
; of true or false depending on the rule.
ByteDigits(rule) {
	res := {}
	for i, val in ["000", "001", "010", "011", "100", "101", "110", "111"] {
		res[val] := Mod(rule, 2)
		rule >>= 1
	}
	return res
}

; Converts 0 and 1 to . and # respectively and returns a string representing the state
PrintState(state) {
	for i, val in state
		result .= val = 1 ? "#" : "."
	return result
}


#include <stdio.h>
#include <limits.h>

typedef unsigned long long ull;
#define N  (sizeof(ull) * CHAR_BIT)
#define B(x) (1ULL << (x))

void evolve(ull state, int rule)
{
	int i;
	ull st;

	printf("Rule %d:\n", rule);
	do {
		st = state;
		for (i = N; i--; ) putchar(st & B(i) ? '#' : '.');
		putchar('\n');

		for (state = i = 0; i < N; i++)
			if (rule & B(7 & (st>>(i-1) | st<<(N+1-i))))
				state |= B(i);
	} while (st != state);
}

int main(int argc, char **argv)
{
	evolve(B(N/2), 90);
	evolve(B(N/4)|B(N - N/4), 30); // well, enjoy the fireworks

	return 0;
}


using System;
using System.Collections;
namespace ElementaryCellularAutomaton
{
    class Automata
    {
        BitArray cells, ncells;
        const int MAX_CELLS = 19;

        public void run()
        {
            cells = new BitArray(MAX_CELLS);
            ncells = new BitArray(MAX_CELLS);
            while (true)
            {
                Console.Clear();
                Console.WriteLine("What Rule do you want to visualize");
                doRule(int.Parse(Console.ReadLine()));
                Console.WriteLine("Press any key to continue...");
                Console.ReadKey();
            }
        }

        private byte getCells(int index)
        {
            byte b;
            int i1 = index - 1,
                i2 = index,
                i3 = index + 1;

            if (i1 < 0) i1 = MAX_CELLS - 1;
            if (i3 >= MAX_CELLS) i3 -= MAX_CELLS;

            b = Convert.ToByte(
                4 * Convert.ToByte(cells.Get(i1)) +
                2 * Convert.ToByte(cells.Get(i2)) +
                Convert.ToByte(cells.Get(i3)));
            return b;
        }

        private string getBase2(int i)
        {
            string s = Convert.ToString(i, 2);
            while (s.Length < 8)
            { s = "0" + s; }
            return s;
        }

        private void doRule(int rule)
        {
            Console.Clear();
            string rl = getBase2(rule);
            cells.SetAll(false);
            ncells.SetAll(false);
            cells.Set(MAX_CELLS / 2, true);

            Console.WriteLine("Rule: " + rule + "\n----------\n");

            for (int gen = 0; gen < 51; gen++)
            {
                Console.Write("{0, 4}", gen + ": ");

                foreach (bool b in cells)
                    Console.Write(b ? "#" : ".");

                Console.WriteLine("");

                int i = 0;
                while (true)
                {
                    byte b = getCells(i);
                    ncells[i] = '1' == rl[7 - b] ? true : false;
                    if (++i == MAX_CELLS) break;
                }

                i = 0;
                foreach (bool b in ncells)
                    cells[i++] = b;
            }
            Console.WriteLine("");
        }

    };
    class Program
    {
        static void Main(string[] args)
        {
            Automata t = new Automata();
            t.run();
        }
    }
}


#include <bitset>
#include <stdio.h>

#define SIZE	           80
#define RULE               30
#define RULE_TEST(x)       (RULE & 1 << (7 & (x)))

void evolve(std::bitset<SIZE> &s) {
    int i;
    std::bitset<SIZE> t(0);
    t[SIZE-1] = RULE_TEST( s[0] << 2 | s[SIZE-1] << 1 | s[SIZE-2] );
    t[     0] = RULE_TEST( s[1] << 2 | s[     0] << 1 | s[SIZE-1] );
    for (i = 1; i < SIZE-1; i++)
	t[i] = RULE_TEST( s[i+1] << 2 | s[i] << 1 | s[i-1] );
    for (i = 0; i < SIZE; i++) s[i] = t[i];
}
void show(std::bitset<SIZE> s) {
    int i;
    for (i = SIZE; --i; ) printf("%c", s[i] ? '#' : ' ');
    printf("\n");
}
int main() {
    int i;
    std::bitset<SIZE> state(1);
    state <<= SIZE / 2;
    for (i=0; i<10; i++) {
	show(state);
	evolve(state);
    }
    return 0;
}


class Rule(number) satisfies Correspondence<Boolean[3], Boolean> {

	shared Byte number;

	"all 3 bit patterns will return a value so this is always true"
	shared actual Boolean defines(Boolean[3] key) => true;

	shared actual Boolean? get(Boolean[3] key) =>
			number.get((key[0] then 4 else 0) + (key[1] then 2 else 0) + (key[2] then 1 else 0));

	function binaryString(Integer integer, Integer maxPadding) =>
			Integer.format(integer, 2).padLeading(maxPadding, '0');

	string =>
			let (digits = binaryString(number.unsigned, 8))
			"Rule #``number``
			 ``" | ".join { for (pattern in $111..0) binaryString(pattern, 3) }``
			 ``" | ".join(digits.map((Character element) => element.string.pad(3)))``";
}

class ElementaryAutomaton {

	shared static ElementaryAutomaton|ParseException parse(Rule rule, String cells, Character aliveChar, Character deadChar) {
		if (!cells.every((Character element) => element == aliveChar || element == deadChar)) {
			return ParseException("the string was not a valid automaton");
		}
		return ElementaryAutomaton(rule, cells.map((Character element) => element == aliveChar));
	}

	shared Rule rule;

	Array<Boolean> cells;

	shared new(Rule rule, {Boolean*} initialCells) {
		this.rule = rule;
		this.cells = Array { *initialCells };
	}

	shared Boolean evolve() {

		if (cells.empty) {
			return false;
		}

		function left(Integer index) {
			assert (exists cell = cells[index - 1] else cells.last);
			return cell;
		}

		function right(Integer index) {
			assert (exists cell = cells[index + 1] else cells.first);
			return cell;
		}

		value newCells = Array.ofSize(cells.size, false);
		for (index->cell in cells.indexed) {
			value neighbourhood = [left(index), cell, right(index)];
			assert (exists newCell = rule[neighbourhood]);
			newCells[index] = newCell;
		}

		if (newCells == cells) {
			return false;
		}

		newCells.copyTo(cells);
		return true;
	}

	shared void display(Character aliveChar = '#', Character deadChar = ' ') {
		print("".join(cells.map((Boolean element) => element then aliveChar else deadChar)));
	}
}

shared void run() {
	value rule = Rule(90.byte);
	print(rule);

	value automaton = ElementaryAutomaton.parse(rule, "          #          ", '#', ' ');
	assert (is ElementaryAutomaton automaton);

	for (generation in 0..10) {
		automaton.display();
		automaton.evolve();
	}
}


(defun automaton (init rule &optional (stop 10))
  (labels ((next-gen (cells)
             (mapcar #'new-cell
                     (cons (car (last cells)) cells)
                     cells
                     (append (cdr cells) (list (car cells)))))

           (new-cell (left current right)
             (let ((shift (+ (* left 4) (* current 2) right)))
               (if (logtest rule (ash 1 shift)) 1 0)))

           (pretty-print (cells)
             (format T "~{~a~}~%"
                     (mapcar (lambda (x) (if (zerop x) #\. #\#))
                             cells))))

    (loop for cells = init then (next-gen cells)
          for i below stop
          do (pretty-print cells))))

(automaton '(0 0 0 0 0 0 1 0 0 0 0 0 0) 90)


import std.stdio, std.string, std.conv, std.range, std.algorithm, std.typecons;

enum mod = (in int n, in int m) pure nothrow @safe @nogc => ((n % m) + m) % m;

struct ECAwrap {
    public string front;
    public enum bool empty = false;
    private immutable const(char)[string] next;

    this(in string cells_, in uint rule) pure @safe {
        this.front = cells_;
        immutable ruleBits = "%08b".format(rule).retro.text;
        next = 8.iota.map!(n => tuple("%03b".format(n), char(ruleBits[n]))).assocArray;
    }

    void popFront() pure @safe {
        alias c = front;
        c = iota(c.length)
            .map!(i => next[[c[(i - 1).mod($)], c[i], c[(i + 1) % $]]])
            .text;
    }
}

void main() @safe {
    enum nLines = 50;
    immutable string start = "0000000001000000000";
    immutable uint[] rules = [90, 30, 122];
    writeln("Rules: ", rules);
    auto ecas = rules.map!(rule => ECAwrap(start, rule)).array;

    foreach (immutable i; 0 .. nLines) {
        writefln("%2d: %-(%s    %)", i, ecas.map!(eca => eca.front.tr("01", ".#")));
        foreach (ref eca; ecas)
            eca.popFront;
    }
}


(lib 'types) ;; int32 vectors
(lib 'plot)

(define-constant BIT0 0)
(define-constant BIT1 (rgb 0.8 0.9 0.7)) ;; colored bit 1

;; integer to pattern
(define ( n->pat n)
		(for/vector ((i 8))
		#:when (bitwise-bit-set? n i)
		(for/vector  ((j (in-range 2 -1 -1)))
		(if (bitwise-bit-set? i j) BIT1  BIT0 ))))

;; test if three pixels match a pattern
(define (pmatch a b c pat)
		(for/or ((v pat))
		(and (= a (vector-ref v 0))  (= b (vector-ref v 1))   (= c (vector-ref v 2)) )))

;; next generation = next row
(define (generate x0 width PAT PIX (x))
		(for ((dx (in-range 0 width)))
		(set! x (+ x0 dx))
		(vector-set! PIX (+ x width) ;; next row
			(if
			(pmatch
				(vector-ref PIX (if (zero? dx) (+ x0 width) (1- x))) ;; let's wrap
				(vector-ref PIX x)
				(vector-ref PIX (if (= dx (1- width)) x0 (1+ x)))
				PAT)
			BIT1 BIT0))))

;; n is the pattern, starters in the number of set pixels at generation 0
(define (task n (starters 1))
		(define width (first (plot-size)))
		(define height (rest (plot-size)))
		(define PAT (n->pat n))
		(plot-clear)

		(define PIX (pixels->int32-vector))
		(init-pix  starters  width height PIX)

		(for ((y (1- height)))
			(generate (* y width) width PAT into: PIX))
		(vector->pixels PIX))

;; put n starters on first row
(define (init-pix starters width height PIX)
	(define dw (floor (/ width (1+ starters))))
	(for ((x (in-range dw width (1+ dw))))
				(vector-set! PIX x BIT1)))

;; usage
(task 99 3) → 672400 ;; ESC to see it
(task 22)   → 672400

;; check pattern generator
(n->pat 13)
    → #( #( 0 0 0) #( 0 -5052980 0) #( 0 -5052980 -5052980))


defmodule Elementary_cellular_automaton do
  def run(start_str, rule, times) do
    IO.puts "rule : #{rule}"
    each(start_str, rule_pattern(rule), times)
  end

  defp rule_pattern(rule) do
    list = Integer.to_string(rule, 2) |> String.pad_leading(8, "0")
           |> String.codepoints |> Enum.reverse
    Enum.map(0..7, fn i -> Integer.to_string(i, 2) |> String.pad_leading(3, "0") end)
    |> Enum.zip(list) |> Map.new
  end

  defp each(_, _, 0), do: :ok
  defp each(str, patterns, times) do
    IO.puts String.replace(str, "0", ".") |> String.replace("1", "#")
    str2 = String.last(str) <> str <> String.first(str)
    next_str = Enum.map_join(0..String.length(str)-1, fn i ->
      Map.get(patterns, String.slice(str2, i, 3))
    end)
    each(next_str, patterns, times-1)
  end
end

pad = String.duplicate("0", 14)
str = pad <> "1" <> pad
Elementary_cellular_automaton.run(str, 18, 25)


// Elementary Cellular Automaton . Nigel Galloway: July 31st., 2019
let eca N=
  let N=Array.init 8 (fun n->(N>>>n)%2)
  Seq.unfold(fun G->Some(G,[|yield Array.last G; yield! G; yield Array.head G|]|>Array.windowed 3|>Array.map(fun n->N.[n.[2]+2*n.[1]+4*n.[0]])))


USING: assocs formatting grouping io kernel math math.bits
math.combinatorics sequences sequences.extras ;

: make-rules ( n -- assoc )
    { f t } 3 selections swap make-bits 8 f pad-tail zip ;

: next-state ( assoc seq -- assoc seq' )
    dupd 3 circular-clump -1 rotate [ of ] with map ;

: first-state ( -- seq ) 15 f <repetition> dup { t } glue ;

: show-state ( seq -- ) [ "#" "." ? write ] each nl ;

: show-automaton ( rule -- )
    dup "Rule %d:\n" printf make-rules first-state 16
    [ dup show-state next-state ] times 2drop ;

90 show-automaton


#define NCELLS 400
#define border 16
dim as ubyte rule = 110

sub evolve( row as uinteger, rule as ubyte, pattern() as ubyte )
    dim as ubyte newp(NCELLS)
    dim as uinteger i
    dim as ubyte lookup
    for i = 0 to NCELLS-1
        pset (i + border, row + border ), pattern(i)*15
        lookup = 4*pattern((i-1) mod NCELLS) + 2*pattern(i) + pattern( (i+1) mod NCELLS )
        newp(i)=0
        if ( 2^lookup and rule )<>0 then newp(i) = 1
    next i
    for i = 0 to NCELLS-1
        pattern(i) = newp(i)
    next i
end sub

sub perform_simulation( rule as ubyte, pattern() as ubyte )
    dim as integer i
    for i = 1 to NCELLS
        evolve(i, rule, pattern())
    next i
end sub

sub reseed( pattern() as ubyte )
    dim as integer i
    for i = 0 to NCELLS-1
        pattern(i) = int(rnd+0.5)
    next i
end sub

sub display_text( rule as ubyte )
    locate 4,58 : print using "Rule ###";rule
    locate 6,53 : print " R: reshuffle seed "
    locate 8,53 : print " <,>: change rule "
    locate 10,53 : print " Q: quit "
end sub

screen 12
randomize timer
dim as boolean pattern(0 to NCELLS-1)
dim as string key="R"

do
    if key = "R" then
        cls
        reseed(pattern())
        perform_simulation(rule,pattern())
        display_text(rule)
    end if
    if key="<" then
        cls
        rule = (rule-1) mod 256
        reseed(pattern())
        perform_simulation(rule,pattern())
        display_text(rule)
    end if
    if key=">" then
        cls
        rule = (rule+1) mod 256
        reseed(pattern())
        perform_simulation(rule,pattern())
        display_text(rule)
    end if
    key = ucase(inkey)
loop until ucase(key) = "Q"


argc 4 < { ."Usage: " 0 argv sprint SPACE 1 argv sprint SPACE ."rule size\n"
."The \"rule\" and \"size\" are numbers.\n"
."0<=rule<=255\n" end }
zero gen
3 argv #s (#g) sto maxcell
2 argv (#g) sto rule
#g argc 5 >= { 4 argv #s (#g) sto gen }
@maxcell mem !maximize sto livingspace
@maxcell mem sto originallivingspace
@maxcell {| @livingspace {} 0 [^] |} @livingspace @maxcell #g 2 / 1 [^]
infi: {...
originallivingspace @livingspace #s =
@livingspace {~ #k @@ { '* }{ '. } print |} NL
#g
@livingspace~ lsp: {|
zero a
@originallivingspace {} ? @maxcell -- [] { 4 sto a }
@originallivingspace {} [] { @a 2 | sto a }
@originallivingspace {+} @maxcell == { 0 }{ {+} } [] { @a 1 | sto a }
8 {| @rule 1 {} << & {
{} @a == { @livingspace {}§lsp 1 [^] {<}§lsp }
}
|}
z: @livingspace {} 0 [^] {<}
|}

#s @originallivingspace @livingspace == { {.>.} }
@gen { {...} @gen #g == { {.>.} } }

...}
."Generations: " {...}§infi #g printnl
@livingspace free
@originallivingspace free
end
{ „maxcell” }
{ „rule” }
{ „state” }
{ „livingspace” }
{ „originallivingspace” }
{ „a” }
{ „gen” }

// =================================================


'
' Elementary One-Dimensional Cellular Automaton
'
' World is cyclic, and rules are defined by a parameter
'
' start$="01110110101010100100" ! start state for world
' rules%=104 ! number defining rule-set to use
start$="00000000000000000000100000000000000000000"
rules%=18
max_cycles%=20 ! give a maximum depth to world
'
' Global variables hold the world, with two rows
' world! is treated as cyclical
' cur% gives the row for current world,
' new% gives the row for the next world.
'
size%=LEN(start$)
DIM world!(size%,2)
cur%=0
new%=1
clock%=0
'
@setup_world(start$)
OPENW 1
CLEARW 1
DO
  @display_world
  @update_world
  EXIT IF @same_state
  clock%=clock%+1
  EXIT IF clock%>max_cycles% ! safety net
LOOP
~INP(2)
CLOSEW 1
'
' parse given string to set up initial states in world
' -- assumes world! is of correct size
'
PROCEDURE setup_world(defn$)
  LOCAL i%
  ' clear out the array
  ARRAYFILL world!(),FALSE
  ' for each 1 in string, set cell to true
  FOR i%=1 TO LEN(defn$)
    IF MID$(defn$,i%,1)="1"
      world!(i%-1,0)=TRUE
    ENDIF
  NEXT i%
  ' set references to cur and new
  cur%=0
  new%=1
RETURN
'
' Display the world
'
PROCEDURE display_world
  LOCAL i%
  FOR i%=1 TO size%
    IF world!(i%-1,cur%)
      PRINT "#";
    ELSE
      PRINT ".";
    ENDIF
  NEXT i%
  PRINT ""
RETURN
'
' Create new version of world
'
PROCEDURE update_world
  LOCAL i%
  FOR i%=1 TO size%
    world!(i%-1,new%)[email protected]_state(@get_value(i%-1))
  NEXT i%
  ' reverse cur/new
  cur%=1-cur%
  new%=1-new%
RETURN
'
' Test if cur/new states are the same
'
FUNCTION same_state
  LOCAL i%
  FOR i%=1 TO size%
    IF world!(i%-1,cur%)<>world!(i%-1,new%)
      RETURN FALSE
    ENDIF
  NEXT i%
  RETURN TRUE
ENDFUNC
'
' Return new state of cell given value
'
FUNCTION new_state(value%)
  RETURN BTST(rules%,value%)
ENDFUNC
'
' Compute value for cell + neighbours
'
FUNCTION get_value(cell%)
  LOCAL result%
  result%=0
  IF cell%-1<0 ! check for wrapping at left
    IF world!(size%-1,cur%)
      result%=result%+4
    ENDIF
  ELSE ! no wrapping
    IF world!(cell%-1,cur%)
      result%=result%+4
    ENDIF
  ENDIF
  IF world!(cell%,cur%)
    result%=result%+2
  ENDIF
  IF cell%+1>size% ! check for wrapping at right
    IF world!(0,cur%)
      result%=result%+1
    ENDIF
  ELSE ! no wrapping
    IF world!(cell%+1,cur%)
      result%=result%+1
    ENDIF
  ENDIF
  RETURN result%
ENDFUNC


package main

import (
    "fmt"
    "math/big"
    "math/rand"
    "strings"
)

func main() {
    const cells = 20
    const generations = 9
    fmt.Println("Single 1, rule 90:")
    a := big.NewInt(1)
    a.Lsh(a, cells/2)
    elem(90, cells, generations, a)
    fmt.Println("Random intial state, rule 30:")
    a = big.NewInt(1)
    a.Rand(rand.New(rand.NewSource(3)), a.Lsh(a, cells))
    elem(30, cells, generations, a)
}

func elem(rule uint, cells, generations int, a *big.Int) {
    output := func() {
        fmt.Println(strings.Replace(strings.Replace(
            fmt.Sprintf("%0*b", cells, a), "0", " ", -1), "1", "#", -1))
    }
    output()
    a1 := new(big.Int)
    set := func(cell int, k uint) {
        a1.SetBit(a1, cell, rule>>k&1)
    }
    last := cells - 1
    for r := 0; r < generations; r++ {
        k := a.Bit(last) | a.Bit(0)<<1 | a.Bit(1)<<2
        set(0, k)
        for c := 1; c < last; c++ {
            k = k>>1 | a.Bit(c+1)<<2
            set(c, k)
        }
        set(last, k>>1|a.Bit(0)<<2)
        a, a1 = a1, a
        output()
    }
}


import Data.Array (listArray, (!), bounds, elems)

step rule a = listArray (l,r) res
  where (l,r) = bounds a
        res = [rule (a!r)     (a!l) (a!(l+1)) ] ++
              [rule (a!(i-1)) (a!i) (a!(i+1)) | i <- [l+1..r-1] ] ++
              [rule (a!(r-1)) (a!r) (a!l)     ]

runCA rule = iterate (step rule)


import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.Timer;

public class WolframCA extends JPanel {
    final int[] ruleSet = {30, 45, 50, 57, 62, 70, 73, 75, 86, 89, 90, 99,
        101, 105, 109, 110, 124, 129, 133, 135, 137, 139, 141, 164,170, 232};
    byte[][] cells;
    int rule = 0;

    public WolframCA() {
        Dimension dim = new Dimension(900, 450);
        setPreferredSize(dim);
        setBackground(Color.white);
        setFont(new Font("SansSerif", Font.BOLD, 28));

        cells = new byte[dim.height][dim.width];
        cells[0][dim.width / 2] = 1;

        new Timer(5000, (ActionEvent e) -> {
            rule++;
            if (rule == ruleSet.length)
                rule = 0;
            repaint();
        }).start();
    }

    private byte rules(int lhs, int mid, int rhs) {
        int idx = (lhs << 2 | mid << 1 | rhs);
        return (byte) (ruleSet[rule] >> idx & 1);
    }

    void drawCa(Graphics2D g) {
        g.setColor(Color.black);
        for (int r = 0; r < cells.length - 1; r++) {
            for (int c = 1; c < cells[r].length - 1; c++) {
                byte lhs = cells[r][c - 1];
                byte mid = cells[r][c];
                byte rhs = cells[r][c + 1];
                cells[r + 1][c] = rules(lhs, mid, rhs); // next generation
                if (cells[r][c] == 1) {
                    g.fillRect(c, r, 1, 1);
                }
            }
        }
    }

    void drawLegend(Graphics2D g) {
        String s = String.valueOf(ruleSet[rule]);
        int sw = g.getFontMetrics().stringWidth(s);

        g.setColor(Color.white);
        g.fillRect(16, 5, 55, 30);

        g.setColor(Color.darkGray);
        g.drawString(s, 16 + (55 - sw) / 2, 30);
    }

    @Override
    public void paintComponent(Graphics gg) {
        super.paintComponent(gg);
        Graphics2D g = (Graphics2D) gg;
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        drawCa(g);
        drawLegend(g);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame f = new JFrame();
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setTitle("Wolfram CA");
            f.setResizable(false);
            f.add(new WolframCA(), BorderLayout.CENTER);
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
        });
    }
}


const alive = '#';
const dead = '.';

// ------------------------------------------------------------[ Bit banging ]--
const setBitAt = (val, idx) => BigInt(val) | (1n << BigInt(idx));
const clearBitAt = (val, idx) => BigInt(val) & ~(1n << BigInt(idx));
const getBitAt = val => idx => (BigInt(val) >> BigInt(idx)) & 1n;
const hasBitAt = val => idx => ((BigInt(val) >> BigInt(idx)) & 1n) === 1n;

// ----------------------------------------------------------------[ Utility ]--
const makeArr = n => Array(n).fill(0);
const reverse = x => Array.from(x).reduce((p, c) => [c, ...p], []);
const numToLine = width => int => {
  const test = hasBitAt(int);
  const looper = makeArr(width);
  return reverse(looper.map((_, i) => test(i) ? alive : dead)).join('');
}

// -------------------------------------------------------------------[ Main ]--
const displayCA = (rule, width, lines, startIndex) => {
  const result = [];
  result.push(`Rule:${rule} Width:${width} Gen:${lines}\n`)
  const ruleTest = hasBitAt(rule);
  const lineLoop = makeArr(lines);
  const looper = makeArr(width);
  const pLine = numToLine(width);

  let nTarget = setBitAt(0n, startIndex);
  result.push(pLine(nTarget));
  lineLoop.forEach(() => {
    const bitTest = getBitAt(nTarget);
    looper.forEach((e, i) => {
      const l = bitTest(i === 0 ? width - 1 : i - 1);
      const m = bitTest(i);
      const r = bitTest(i === width - 1 ? 0 : i + 1);
      nTarget = ruleTest(
          parseInt([l, m, r].join(''), 2))
          ? setBitAt(nTarget, i)
          : clearBitAt(nTarget, i);
    });
    result.push(pLine(nTarget));
  });
  return result.join('\n');
}

displayCA(90, 57, 31, 28);


# The ordinal value of the relevant states:
def states:
  {"111": 1, "110": 2, "101": 3, "100": 4, "011": 5, "010": 6, "001": 7, "000": 8};

# Compute the next "state"
# input: a state ("111" or "110" ...)
# rule: the rule represented as a string of 0s and 1s
# output: the next state "0" or "1" depending on the rule
def next(rule):
  states[.] as $n | rule[($n-1):$n] ;

# The state of cell $n, using 0-based indexing
def triple($n):
  if $n == 0 then .[-1:] + .[0:2]
  elif $n == (length-1) then .[-2:] + .[0:1]
  else .[$n-1:$n+2]
  end;

# input: non-negative decimal integer
# output: 0-1 binary string
def binary_digits:
  if . == 0 then "0"
  else [recurse( if . == 0 then empty else ./2 | floor end ) % 2 | tostring]
    | reverse
    | .[1:] # remove the leading 0
    | join("")
  end ;


const lines = 10
const start = ".........#........."
const rules = [90, 30, 14]

rule2poss(rule) = [rule & (1 << (i - 1)) != 0 for i in 1:8]

cells2bools(cells) = [cells[i] == '#' for i in 1:length(cells)]

bools2cells(bset) = prod([bset[i] ? "#" : "." for i in 1:length(bset)])

function transform(bset, ruleposs)
    newbset = map(x->ruleposs[x],
        [bset[i - 1] * 4 + bset[i] * 2 + bset[i + 1] + 1
        for i in 2:length(bset)-1])
    vcat(newbset[end], newbset, newbset[1])
end

const startset = cells2bools(start)

for rul in rules
    println("\nUsing Rule $rul:")
    bset = vcat(startset[end], startset, startset[1]) # wrap ends
    rp = rule2poss(rul)
    for _ in 1:lines
        println(bools2cells(bset[2:end-1]))  # unwrap ends
        bset = transform(bset, rp)
    end
end


// version 1.1.51

import java.util.BitSet

const val SIZE  = 32
const val LINES = SIZE / 2
const val RULE  = 90

fun ruleTest(x: Int) = (RULE and (1 shl (7 and x))) != 0

infix fun Boolean.shl(bitCount: Int) = (if (this) 1 else 0) shl bitCount

fun Boolean.toInt() = if (this) 1 else 0

fun evolve(s: BitSet) {
    val t = BitSet(SIZE)  // all false by default
    t[SIZE - 1] = ruleTest((s[0] shl 2) or (s[SIZE - 1] shl 1) or s[SIZE - 2].toInt())
    t[0] = ruleTest((s[1] shl 2) or (s[0] shl 1) or s[SIZE - 1].toInt())
    for (i in 1 until SIZE - 1) {
        t[i] = ruleTest((s[i + 1] shl 2) or (s[i] shl 1) or s[i - 1].toInt())
    }
    for (i in 0 until SIZE) s[i] = t[i]
}

fun show(s: BitSet) {
    for (i in SIZE - 1 downTo 0) print(if (s[i]) "*" else " ")
    println()
}

fun main(args: Array<String>) {
    var state = BitSet(SIZE)
    state.set(LINES)
    println("Rule $RULE:")
    repeat(LINES) {
        show(state)
        evolve(state)
    }
}


local CA = {
  state = "..............................#..............................",
  bstr = { [0]="...", "..#", ".#.", ".##", "#..", "#.#", "##.", "###" },
  new = function(self, rule)
    local inst = setmetatable({rule=rule}, self)
    for b = 0,7 do
      inst[inst.bstr[b]] = rule%2==0 and "." or "#"
      rule = math.floor(rule/2)
    end
    return inst
  end,
  evolve = function(self)
    local n, state, newstate = #self.state, self.state, ""
    for i = 1,n do
      local nbhd = state:sub((i+n-2)%n+1,(i+n-2)%n+1) .. state:sub(i,i) .. state:sub(i%n+1,i%n+1)
      newstate = newstate .. self[nbhd]
    end
    self.state = newstate
  end,
}
CA.__index = CA
ca = { CA:new(18), CA:new(30), CA:new(73), CA:new(129) }
for i = 1, 63 do
  print(string.format("%-66s%-66s%-66s%-61s", ca[1].state, ca[2].state, ca[3].state, ca[4].state))
  for j = 1, 4 do ca[j]:evolve() end
end


function init = cellularAutomaton(rule, init, n)
  init(n + 1, :) = 0;
  for k = 1 : n
    init(k + 1, :) = bitget(rule, 1 + filter2([4 2 1], init(k, :)));
  end


import bitops

const
  Size = 32
  LastBit = Size - 1
  Lines = Size div 2
  Rule = 90

type State = int    # State is represented as an int and will be used as a bit string.

#---------------------------------------------------------------------------------------------------

template bitVal(state: State; n: typed): int =
  ## Return the value of a bit as an int rather than a bool.
  ord(state.testBit(n))

#---------------------------------------------------------------------------------------------------

proc ruleTest(x: int): bool =
  ## Return true if a bit must be set.
  (Rule and 1 shl (7 and x)) != 0

#---------------------------------------------------------------------------------------------------

proc evolve(state: var State) =
  ## Compute next state.

  var newState: State  # All bits cleared by default.
  if ruleTest(state.bitVal(0) shl 2 or state.bitVal(LastBit) shl 1 or state.bitVal(LastBit-1)):
    newState.setBit(LastBit)
  if ruleTest(state.bitVal(1) shl 2 or state.bitVal(0) shl 1 or state.bitVal(LastBit)):
    newState.setBit(0)
  for i in 1..<LastBit:
    if ruleTest(state.bitVal(i + 1) shl 2 or state.bitVal(i) shl 1 or state.bitVal(i - 1)):
      newState.setBit(i)
  state = newState

#---------------------------------------------------------------------------------------------------

proc show(state: State) =
  ## Show the current state.
  for i in countdown(LastBit, 0):
    stdout.write if state.testbit(i): '*' else: ' '
  echo ""

#———————————————————————————————————————————————————————————————————————————————————————————————————

var state: State
state.setBit(Lines)
echo "Rule ", Rule
for _ in 1..Lines:
  show(state)
  evolve(state)


clear all
E=200;
idx=round(E/2);
z(1:1:E^2)=0;                % init lattice
z(idx)=1;                    % seed apex of triangle with a single cell
A=2;                         % Number of bits-1    rule30 uses 3 so A=2
for n=1:1:E^2/2-E-2;         % n=lines
theta=0;                     % theta
for a=0:1:A;
theta=theta+2^a*z(n+A-a);
endfor
x=(asin(sin (pi/4*(theta-3/4))));
z(n+E+1)=round( (4*x+pi)/(2*pi) );
endfor
imagesc(reshape(z,E,E)');    % Medland map


T Node
   Int length
   Int suffix
   [Char = Int] edges

   F (length, suffix = 0)
      .length = length
      .suffix = suffix

-V oddRoot = 1

F eertree(s)
   V tree = [Node(0, :oddRoot), Node(-1, :oddRoot)]
   V suffix = :oddRoot
   L(c) s
      V i = L.index
      V n = suffix
      Int k
      L
         k = tree[n].length
         V b = i - k - 1
         I b >= 0 & s[b] == c
            L.break
         n = tree[n].suffix

      V? edge = tree[n].edges.find(c)
      I edge != N
         suffix = edge
         L.continue

      suffix = tree.len
      tree [+]= Node(k + 2)
      tree[n].edges[c] = suffix
      I tree[suffix].length == 1
         tree[suffix].suffix = 0
         L.continue

      L
         n = tree[n].suffix
         V b = i - tree[n].length - 1
         I b >= 0 & s[b] == c
            L.break

      tree[suffix].suffix = tree[n].edges[c]

   R tree

F subPalindromes(tree)
   [String] s

   F children(Int n, String =p) -> N
      L(c, n) @tree[n].edges
         p = c‘’p‘’c
         @s [+]= p
         @children(n, p)

   children(0, ‘’)
   L(c, n) tree[1].edges
      s [+]= c
      children(n, c)

   R s

V tree = eertree(‘eertree’)
print(subPalindromes(tree))


use strict;
use warnings;

package Automaton {
    sub new {
	my $class = shift;
	my $rule = [ reverse split //, sprintf "%08b", shift ];
	return bless { rule => $rule, cells => [ @_ ] }, $class;
    }
    sub next {
	my $this = shift;
	my @previous = @{$this->{cells}};
	$this->{cells} = [
	    @{$this->{rule}}[
	    map {
	      4*$previous[($_ - 1) % @previous]
	    + 2*$previous[$_]
	    +   $previous[($_ + 1) % @previous]
	    } 0 .. @previous - 1
	    ]
	];
	return $this;
    }
    use overload
    q{""} => sub {
	my $this = shift;
	join '', map { $_ ? '#' : ' ' } @{$this->{cells}}
    };
}

my @a = map 0, 1 .. 91; $a[45] = 1;
my $a = Automaton->new(90, @a);

for (1..40) {
    print "|$a|\n"; $a->next;
}


using System;
using System.Collections.Generic;

namespace Eertree {
    class Node {
        public Node(int length) {
            this.Length = length;
            // empty or
            this.Edges = new Dictionary<char, int>();
        }

        public Node(int length, Dictionary<char, int> edges, int suffix) {
            this.Length = length;
            this.Edges = edges;
            this.Suffix = suffix;
        }

        public int Length { get; set; }
        public Dictionary<char, int> Edges { get; set; }
        public int Suffix { get; set; }
    }

    class Program {
        const int EVEN_ROOT = 0;
        const int ODD_ROOT = 1;

        static List<Node> Eertree(string s) {
            List<Node> tree = new List<Node> {
                //new Node(0, null, ODD_ROOT), or
                new Node(0, new Dictionary<char, int>(), ODD_ROOT),
                //new Node(-1, null, ODD_ROOT) or
                new Node(-1, new Dictionary<char, int>(), ODD_ROOT)
            };
            int suffix = ODD_ROOT;
            int n, k;
            for (int i = 0; i < s.Length; i++) {
                char c = s[i];
                for (n = suffix; ; n = tree[n].Suffix) {
                    k = tree[n].Length;
                    int b = i - k - 1;
                    if (b >= 0 && s[b] == c) {
                        break;
                    }
                }
                if (tree[n].Edges.ContainsKey(c)) {
                    suffix = tree[n].Edges[c];
                    continue;
                }
                suffix = tree.Count;
                tree.Add(new Node(k + 2));
                tree[n].Edges[c] = suffix;
                if (tree[suffix].Length == 1) {
                    tree[suffix].Suffix = 0;
                    continue;
                }
                while (true) {
                    n = tree[n].Suffix;
                    int b = i - tree[n].Length - 1;
                    if (b >= 0 && s[b] == c) {
                        break;
                    }
                }
                tree[suffix].Suffix = tree[n].Edges[c];
            }
            return tree;
        }

        static List<string> SubPalindromes(List<Node> tree) {
            List<string> s = new List<string>();
            SubPalindromes_children(0, "", tree, s);
            foreach (var c in tree[1].Edges.Keys) {
                int m = tree[1].Edges[c];
                string ct = c.ToString();
                s.Add(ct);
                SubPalindromes_children(m, ct, tree, s);
            }
            return s;
        }

        static void SubPalindromes_children(int n, string p, List<Node> tree, List<string> s) {
            foreach (var c in tree[n].Edges.Keys) {
                int m = tree[n].Edges[c];
                string p1 = c + p + c;
                s.Add(p1);
                SubPalindromes_children(m, p1, tree, s);
            }
        }

        static void Main(string[] args) {
            List<Node> tree = Eertree("eertree");
            List<string> result = SubPalindromes(tree);
            string listStr = string.Join(", ", result);
            Console.WriteLine("[{0}]", listStr);
        }
    }
}


with javascript_semantics
string s = ".........#.........",
       t = s, r = "........"
integer rule = 90, k, l = length(s)
for i=1 to 8 do
    r[i] = iff(mod(rule,2)?'#':'.')
    rule = floor(rule/2)
end for
for i=0 to 50 do
    ?s
    for j=1 to l do
        k = (s[iff(j=1?l:j-1)]='#')*4
          + (s[          j   ]='#')*2
          + (s[iff(j=l?1:j+1)]='#')+1
        t[j] = r[k]
    end for
    s = t
end for


(de dictionary (N)
   (extract
      '((A B)
         (and
            (= "1" B)
            (mapcar
               '((L) (if (= "1" L) "#" "."))
               A ) ) )
      (mapcar
         '((N) (chop (pad 3 (bin N))))
         (range 7 0) )
      (chop (pad 8 (bin N))) ) )
(de cellular (Lst N)
   (let (Lst (chop Lst)  D (dictionary N))
      (do 10
         (prinl Lst)
         (setq Lst
            (make
               (map
                  '((L)
                     (let Y (head 3 L)
                        (and
                           (cddr Y)
                           (link (if (member Y D) "#" ".")) ) ) )
                  (conc (cons (last Lst)) Lst (cons (car Lst))) ) ) ) ) ) )
(cellular
   ".........#........."
   90 )


#include <iostream>
#include <functional>
#include <map>
#include <vector>

struct Node {
    int length;
    std::map<char, int> edges;
    int suffix;

    Node(int l) : length(l), suffix(0) {
        /* empty */
    }

    Node(int l, const std::map<char, int>& m, int s) : length(l), edges(m), suffix(s) {
        /* empty */
    }
};

constexpr int evenRoot = 0;
constexpr int oddRoot = 1;

std::vector<Node> eertree(const std::string& s) {
    std::vector<Node> tree = {
        Node(0, {}, oddRoot),
        Node(-1, {}, oddRoot)
    };
    int suffix = oddRoot;
    int n, k;

    for (size_t i = 0; i < s.length(); ++i) {
        char c = s[i];
        for (n = suffix; ; n = tree[n].suffix) {
            k = tree[n].length;
            int b = i - k - 1;
            if (b >= 0 && s[b] == c) {
                break;
            }
        }

        auto it = tree[n].edges.find(c);
        auto end = tree[n].edges.end();
        if (it != end) {
            suffix = it->second;
            continue;
        }
        suffix = tree.size();
        tree.push_back(Node(k + 2));
        tree[n].edges[c] = suffix;
        if (tree[suffix].length == 1) {
            tree[suffix].suffix = 0;
            continue;
        }
        while (true) {
            n = tree[n].suffix;
            int b = i - tree[n].length - 1;
            if (b >= 0 && s[b] == c) {
                break;
            }
        }
        tree[suffix].suffix = tree[n].edges[c];
    }

    return tree;
}

std::vector<std::string> subPalindromes(const std::vector<Node>& tree) {
    std::vector<std::string> s;

    std::function<void(int, std::string)> children;
    children = [&children, &tree, &s](int n, std::string p) {
        auto it = tree[n].edges.cbegin();
        auto end = tree[n].edges.cend();
        for (; it != end; it = std::next(it)) {
            auto c = it->first;
            auto m = it->second;

            std::string pl = c + p + c;
            s.push_back(pl);
            children(m, pl);
        }
    };
    children(0, "");

    auto it = tree[1].edges.cbegin();
    auto end = tree[1].edges.cend();
    for (; it != end; it = std::next(it)) {
        auto c = it->first;
        auto n = it->second;

        std::string ct(1, c);
        s.push_back(ct);

        children(n, ct);
    }

    return s;
}

int main() {
    using namespace std;

    auto tree = eertree("eertree");
    auto pal = subPalindromes(tree);

    auto it = pal.cbegin();
    auto end = pal.cend();

    cout << "[";
    if (it != end) {
        cout << it->c_str();
        it++;
    }
    while (it != end) {
        cout << ", " << it->c_str();
        it++;
    }
    cout << "]" << endl;

    return 0;
}


play :-	initial(I), do_auto(50, I).

initial([0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0]).

do_auto(0, _) :- !.
do_auto(N, I) :-
	maplist(writ, I), nl,
	apply_rules(I, Next),
	succ(N1, N),
	do_auto(N1, Next).

r(0,0,0,0).
r(0,0,1,1).
r(0,1,0,0).
r(0,1,1,1).
r(1,0,0,1).
r(1,0,1,0).
r(1,1,0,1).
r(1,1,1,0).

apply_rules(In, Out) :-
	apply1st(In, First),
	Out = [First|_],
	apply(In, First, First, Out).

apply1st([A,B|T], A1) :-                            last([A,B|T], Last), r(Last,A,B,A1).
apply([A,B], Prev, First, [Prev, This]) :-          r(A,B,First,This).
apply([A,B,C|T], Prev, First, [Prev,This|Rest]) :-  r(A,B,C,This), apply([B,C|T], This, First, [This|Rest]).

writ(0) :- write('.').
writ(1) :- write(1).


import std.array;
import std.stdio;

void main() {
    auto tree = eertree("eertree");
    writeln(subPalindromes(tree));
}

struct Node {
    int length;
    int[char] edges;
    int suffix;
}

const evenRoot = 0;
const oddRoot = 1;

Node[] eertree(string s) {
    Node[] tree = [
        Node(0, null, oddRoot),
        Node(-1, null, oddRoot),
    ];
    int suffix = oddRoot;
    int n, k;
    foreach (i, c; s) {
        for (n=suffix; ; n=tree[n].suffix) {
            k = tree[n].length;
            int b = i-k-1;
            if (b>=0 && s[b]==c) {
                break;
            }
        }
        if (c in tree[n].edges) {
            suffix = tree[n].edges[c];
            continue;
        }
        suffix = tree.length;
        tree ~= Node(k+2);
        tree[n].edges[c] = suffix;
        if (tree[suffix].length == 1) {
            tree[suffix].suffix = 0;
            continue;
        }
        while (true) {
            n = tree[n].suffix;
            int b = i-tree[n].length-1;
            if (b>=0 && s[b]==c) {
                break;
            }
        }
        tree[suffix].suffix = tree[n].edges[c];
    }
    return tree;
}

auto subPalindromes(Node[] tree) {
    auto s = appender!(string[]);
    void children(int n, string p) {
        foreach (c, n; tree[n].edges) {
            p = c ~ p ~ c;
            s ~= p;
            children(n, p);
        }
    }
    children(0, "");
    foreach (c, n; tree[1].edges) {
        string ct = [c].idup;
        s ~= ct;
        children(n, ct);
    }
    return s.data;
}


def eca(cells, rule):
    lencells = len(cells)
    c = "0" + cells + "0"    # Zero pad the ends
    rulebits = '{0:08b}'.format(rule)
    neighbours2next = {'{0:03b}'.format(n):rulebits[::-1][n] for n in range(8)}
    yield c[1:-1]
    while True:
        c = ''.join(['0',
                     ''.join(neighbours2next[c[i-1:i+2]]
                             for i in range(1,lencells+1)),
                     '0'])
        yield c[1:-1]

if __name__ == '__main__':
    lines, start, rules = 50, '0000000001000000000', (90, 30, 122)
    zipped = [range(lines)] + [eca(start, rule) for rule in rules]
    print('\n   Rules: %r' % (rules,))
    for data in zip(*zipped):
        i = data[0]
        cells = data[1:]
        print('%2i: %s' % (i, '    '.join(cells).replace('0', '.').replace('1', '#')))


  ( the Cellular Automaton is on the stack as 3 items, the   )
  ( Rule (R), the Size of the space (S) and the Current      )
  ( state (C). make-ca sets this up from a string indicating )
  ( the size and starting state, and a rule number.          )

  [ [] swap
    8 times
      [ dup 1 &
        rot swap join
        swap 1 >> ]
    drop swap
    dup size swap
    0 swap reverse
    witheach
      [ char # =
        dip [ 1 << ]
        + ] ]                   is make-ca    (   $ n --> R S C )

  [ $ "" unrot
    swap times
      [ dup 1 & iff
          [ char # ]
        else
          [ char . ]
        rot join swap
        1 >> ]
      drop echo$ cr ]           is echo-ca    (   S C -->       )

  [ dip bit
    2dup 1 & iff
      | else drop
    1 << swap
    over & 0 != | ]             is wrap       (   S C --> C     )

  [ rot temp put
    dip dup 0 unrot wrap
    rot times
      [ dup 7 &
        temp share swap peek if
          [ i^ bit rot | swap ]
        1 >> ]
    drop temp release ]         is next-ca    ( R S C --> C     )

  [ dip
      [ over echo$ cr
        make-ca ]
    1 - times
      [ dip 2dup next-ca
        2dup echo-ca ]
    2drop drop ]                is generations ( $ n n -->      )

  say "Rule 30, 50 generations:" cr cr

  $ ".........#........." 30 50 generations


package main

import "fmt"

func main() {
    tree := eertree([]byte("eertree"))
    fmt.Println(subPalindromes(tree))
}

type edges map[byte]int

type node struct {
    length int
    edges
    suffix int
}

const evenRoot = 0
const oddRoot = 1

func eertree(s []byte) []node {
    tree := []node{
        evenRoot: {length: 0, suffix: oddRoot, edges: edges{}},
        oddRoot:  {length: -1, suffix: oddRoot, edges: edges{}},
    }
    suffix := oddRoot
    var n, k int
    for i, c := range s {
        for n = suffix; ; n = tree[n].suffix {
            k = tree[n].length
            if b := i - k - 1; b >= 0 && s[b] == c {
                break
            }
        }
        if e, ok := tree[n].edges[c]; ok {
            suffix = e
            continue
        }
        suffix = len(tree)
        tree = append(tree, node{length: k + 2, edges: edges{}})
        tree[n].edges[c] = suffix
        if tree[suffix].length == 1 {
            tree[suffix].suffix = 0
            continue
        }
        for {
            n = tree[n].suffix
            if b := i - tree[n].length - 1; b >= 0 && s[b] == c {
                break
            }
        }
        tree[suffix].suffix = tree[n].edges[c]
    }
    return tree
}

func subPalindromes(tree []node) (s []string) {
    var children func(int, string)
    children = func(n int, p string) {
        for c, n := range tree[n].edges {
            c := string(c)
            p := c + p + c
            s = append(s, p)
            children(n, p)
        }
    }
    children(0, "")
    for c, n := range tree[1].edges {
        c := string(c)
        s = append(s, c)
        children(n, c)
    }
    return
}


#lang racket
(require racket/fixnum)
(provide usable-bits/fixnum usable-bits/fixnum-1 CA-next-generation
         wrap-rule-truncate-left-word show-automaton)

(define usable-bits/fixnum 30)
(define usable-bits/fixnum-1 (sub1 usable-bits/fixnum))
(define usable-bits/mask (fx- (fxlshift 1 usable-bits/fixnum) 1))
(define 2^u-b-1 (fxlshift 1 usable-bits/fixnum-1))
(define (fxior3 a b c) (fxior (fxior a b) c))
(define (if-bit-set n i [result 1]) (if (bitwise-bit-set? n i) result 0))

(define (shift-right-1-bit-with-lsb-L L n)
  (fxior (if-bit-set L 0 2^u-b-1) (fxrshift n 1)))

(define (shift-left-1-bit-with-msb-R n R)
  (fxior (fxand usable-bits/mask (fxlshift n 1))
         (if-bit-set R usable-bits/fixnum-1)))

(define ((CA-next-bit-state rule) L n R)
  (for/fold ([n+ 0])
            ([b (in-range usable-bits/fixnum-1 -1 -1)])
    (define rule-bit (fxior3 (if-bit-set (shift-right-1-bit-with-lsb-L L n) b 4)
                             (if-bit-set n b 2)
                             (if-bit-set (shift-left-1-bit-with-msb-R n R) b)))
    (fxior (fxlshift n+ 1) (if-bit-set rule rule-bit))))

;; CA-next-generation generates a function which takes:
;;  v-in   : an fxvector representing the CA's current state as a bit field. This may be mutated
;;  offset : the offset of the leftmost element of v-in; this is used in infinite CA to allow the CA
;;           to occupy negative indices
;;  wrap-rule : provided for automata that are not an integer number of usable-bits/fixnum bits wide
;;  wrap-rule = #f - v-in and offset are unchanged
;;  wrap-rule : (v-in vl-1 offset) -> (values v-out vl-1+ offset-)
;;             v-in as passed into CA-next-generation
;;             vl-1=(sub1 (length v-in)), since its precomputed vaule is needed
;;             offset as passed into CA-next-generation
;;             v-out: either a new copy of v-in, or v-in itself (which might be mutated)
;;             vl-1+: (sub1 (length v-out))
;;             offset- : a new value for offset (it will have decreased since the CA grows to the left
;;             with offset, and to the right with (length v-out)
(define (CA-next-generation rule #:wrap-rule (wrap-rule values))
  (define next-state (CA-next-bit-state rule))
  (lambda (v-in offset)
    (define vl-1 (fx- (fxvector-length v-in) 1))
    (define-values [v+ v+l-1 offset-] (wrap-rule v-in vl-1 offset))
    (define rv
      (for/fxvector ([l (in-sequences (in-value (fxvector-ref v+ v+l-1)) (in-fxvector v+))]
                     [n (in-fxvector v+)]
                     [r (in-sequences (in-fxvector v+ 1) (in-value (fxvector-ref v+ 0)))])
        (next-state l n r)))
    (values rv offset-)))

;; CA-next-generation with the default (non) wrap rule wraps the MSB of the left-hand word (L) and the
;; LSB of the right-hand word (R) in the CA. If the CA is not a multiple of usable-bits/fixnum wide,
;; then we use this function to put these bits where they can be used... i.e. the actual MSB is copied
;; to the word's MSB and the LSB is copied to the bit that is to the left of the actual MSB.
(define (wrap-rule-truncate-left-word sig-bits)
  (define wlb-mask (fx- (fxlshift 1 sig-bits) 1))
  (unless (fx< sig-bits (fx- usable-bits/fixnum 1))
    (error "we need at least 2 bits in the top of the word to do this safely"))
  (lambda (v-in vl-1 offset)
    (define v0 (fxvector-ref v-in 0))
    ;; this must wrap to wlb of the first word
    (define last-bit (fxlshift (fxand 1 (fxvector-ref v-in vl-1)) sig-bits))
    ;; this must wrap to the extreme left of the first word
    (define first-bit (if-bit-set v0 (fx- sig-bits 1) 2^u-b-1))
    (fxvector-set! v-in 0 (fxior3 last-bit first-bit (fxand v0 wlb-mask)))
    (values v-in vl-1 offset)))

;; This displays a state of the CA
(define (show-automaton v #:step (step #f) #:sig-bits (sig-bits #f) #:push-right (push-right #f))
  (when step (printf "[~a] " (~a #:align 'right #:width 10 step)))
  (when push-right (display (make-string (* usable-bits/fixnum push-right) #\.)))
  (when (number? sig-bits)
    (display (~a #:width sig-bits #:align 'right #:pad-string "0"
                 (number->string (fxvector-ref v 0) 2))))
  (for ([n (in-fxvector v (if sig-bits 1 0))])
    (display (~a #:width usable-bits/fixnum #:align 'right #:pad-string "0" (number->string n 2)))))

(module+ main
  (define ng/122/19-bits (CA-next-generation 122 #:wrap-rule (wrap-rule-truncate-left-word 19)))
  (for/fold ([v (fxvector #b1000000000)] [o 0]) ([step (in-range 40)])
    (show-automaton v #:step step #:sig-bits 19)
    (newline)
    (ng/122/19-bits v o)))


class Automaton {
    has $.rule;
    has @.cells;
    has @.code = $!rule.fmt('%08b').flip.comb».Int;

    method gist { "|{ @!cells.map({+$_ ?? '#' !! ' '}).join }|" }

    method succ {
        self.new: :$!rule, :@!code, :cells(
            @!code[
                    4 «*« @!cells.rotate(-1)
                »+« 2 «*« @!cells
                »+«       @!cells.rotate(1)
            ]
        )
    }
}

my @padding = 0 xx 10;

my Automaton $a .= new:
    :rule(30),
    :cells(flat @padding, 1, @padding);

say $a++ for ^10;


class ElemCellAutomat
  include Enumerable

  def initialize (start_str, rule, disp=false)
    @cur = start_str
    @patterns = Hash[8.times.map{|i|["%03b"%i, "01"[rule[i]]]}]
    puts "Rule (#{rule}) : #@patterns" if disp
  end

  def each
    return to_enum unless block_given?
    loop do
      yield @cur
      str = @cur[-1] + @cur + @cur[0]
      @cur = @cur.size.times.map {|i| @patterns[str[i,3]]}.join
    end
  end

end

eca = ElemCellAutomat.new('1'.center(39, "0"), 18, true)
eca.take(30).each{|line| puts line.tr("01", ".#")}


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Eertree {
    public static void main(String[] args) {
        List<Node> tree = eertree("eertree");
        List<String> result = subPalindromes(tree);
        System.out.println(result);
    }

    private static class Node {
        int length;
        Map<Character, Integer> edges = new HashMap<>();
        int suffix;

        public Node(int length) {
            this.length = length;
        }

        public Node(int length, Map<Character, Integer> edges, int suffix) {
            this.length = length;
            this.edges = edges != null ? edges : new HashMap<>();
            this.suffix = suffix;
        }
    }

    private static final int EVEN_ROOT = 0;
    private static final int ODD_ROOT = 1;

    private static List<Node> eertree(String s) {
        List<Node> tree = new ArrayList<>();
        tree.add(new Node(0, null, ODD_ROOT));
        tree.add(new Node(-1, null, ODD_ROOT));
        int suffix = ODD_ROOT;
        int n, k;
        for (int i = 0; i < s.length(); ++i) {
            char c = s.charAt(i);
            for (n = suffix; ; n = tree.get(n).suffix) {
                k = tree.get(n).length;
                int b = i - k - 1;
                if (b >= 0 && s.charAt(b) == c) {
                    break;
                }
            }
            if (tree.get(n).edges.containsKey(c)) {
                suffix = tree.get(n).edges.get(c);
                continue;
            }
            suffix = tree.size();
            tree.add(new Node(k + 2));
            tree.get(n).edges.put(c, suffix);
            if (tree.get(suffix).length == 1) {
                tree.get(suffix).suffix = 0;
                continue;
            }
            while (true) {
                n = tree.get(n).suffix;
                int b = i - tree.get(n).length - 1;
                if (b >= 0 && s.charAt(b) == c) {
                    break;
                }
            }
            tree.get(suffix).suffix = tree.get(n).edges.get(c);
        }
        return tree;
    }

    private static List<String> subPalindromes(List<Node> tree) {
        List<String> s = new ArrayList<>();
        subPalindromes_children(0, "", tree, s);
        for (Map.Entry<Character, Integer> cm : tree.get(1).edges.entrySet()) {
            String ct = String.valueOf(cm.getKey());
            s.add(ct);
            subPalindromes_children(cm.getValue(), ct, tree, s);
        }
        return s;
    }

    // nested methods are a pain, even if lambdas make that possible for Java
    private static void subPalindromes_children(final int n, final String p, final List<Node> tree, List<String> s) {
        for (Map.Entry<Character, Integer> cm : tree.get(n).edges.entrySet()) {
            Character c = cm.getKey();
            Integer m = cm.getValue();
            String pl = c + p + c;
            s.add(pl);
            subPalindromes_children(m, pl, tree, s);
        }
    }
}


fn main() {
    struct ElementaryCA {
        rule: u8,
        state: u64,
    }
    impl ElementaryCA {
        fn new(rule: u8) -> (u64, ElementaryCA) {
            let out = ElementaryCA {
                rule,
                state: 1,
            };
            (out.state, out)
        }
        fn next(&mut self) -> u64 {
            let mut next_state = 0u64;
            let state = self.state;
            for i in 0..64 {
                next_state |= (((self.rule as u64)>>(7 & (state.rotate_left(1).rotate_right(i as u32)))) & 1)<<i;
            }
            self.state = next_state;
            self.state
        }
    }
    fn rep_u64(val: u64) -> String {
        let mut out = String::new();
        for i in (0..64).rev() {
            if 1<<i & val != 0 {
                out = out + "\u{2588}";
            } else {
                out = out + "-";
            }
        }
        out
    }

    let (i, mut thirty) = ElementaryCA::new(154);
    println!("{}",rep_u64(i));
    for _ in 0..32 {
        let s = thirty.next();
        println!("{}", rep_u64(s));
    }
}


import java.awt._
import java.awt.event.ActionEvent

import javax.swing._

object ElementaryCellularAutomaton extends App {

  SwingUtilities.invokeLater(() =>
    new JFrame("Elementary Cellular Automaton") {

      class ElementaryCellularAutomaton extends JPanel {
        private val dim = new Dimension(900, 450)
        private val cells = Array.ofDim[Byte](dim.height, dim.width)
        private var rule = 0

        private def ruleSet =
          Seq(30, 45, 50, 57, 62, 70, 73, 75, 86, 89, 90, 99, 101, 105, 109, 110, 124, 129, 133, 135, 137, 139, 141, 164, 170, 232)

        override def paintComponent(gg: Graphics): Unit = {
          def drawCa(g: Graphics2D): Unit = {

            def rules(lhs: Int, mid: Int, rhs: Int) = {
              val idx = lhs << 2 | mid << 1 | rhs
              (ruleSet(rule) >> idx & 1).toByte
            }

            g.setColor(Color.black)
            for (r <- 0 until cells.length - 1;
                 c <- 1 until cells(r).length - 1;
                 lhs = cells(r)(c - 1);
                 mid = cells(r)(c);
                 rhs = cells(r)(c + 1)) {
              cells(r + 1)(c) = rules(lhs, mid, rhs) // next generation
              if (cells(r)(c) == 1) g.fillRect(c, r, 1, 1)
            }
          }

          def drawLegend(g: Graphics2D): Unit = {
            val s = ruleSet(rule).toString
            val sw = g.getFontMetrics.stringWidth(ruleSet(rule).toString)
            g.setColor(Color.white)
            g.fillRect(16, 5, 55, 30)
            g.setColor(Color.darkGray)
            g.drawString(s, 16 + (55 - sw) / 2, 30)
          }

          super.paintComponent(gg)
          val g = gg.asInstanceOf[Graphics2D]
          g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
          drawCa(g)
          drawLegend(g)
        }

        new Timer(5000, (_: ActionEvent) => {
          rule += 1
          if (rule == ruleSet.length) rule = 0
          repaint()
        }).start()
        cells(0)(dim.width / 2) = 1
        setBackground(Color.white)
        setFont(new Font("SansSerif", Font.BOLD, 28))
        setPreferredSize(dim)
      }

      add(new ElementaryCellularAutomaton, BorderLayout.CENTER)
      pack()
      setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
      setLocationRelativeTo(null)
      setResizable(false)
      setVisible(true)
    })

}


mutable struct Node
    edges::Dict{Char, Node}
    link::Union{Node, Missing}
    sz::Int
    Node() = new(Dict(), missing, 0)
end

sizednode(x) = (n = Node(); n.sz = x; n)

function eertree(str)
    nodes = Vector{Node}()
    oddroot = sizednode(-1)
    evenroot = sizednode(0)
    oddroot.link = evenroot
    evenroot.link = oddroot
    S = "0"
    maxsuffix = evenroot

    function maxsuffixpal(startnode,a::Char)
        # Traverse the suffix-palindromes of tree looking for equality with a
        u = startnode
        i = length(S)
        k = u.sz
        while u !== oddroot && S[i - k] != a
            if u === u.link
                throw("circular reference above oddroot")
            end
            u = u.link
            k = u.sz
        end
        u
    end

    function addchar(a::Char)
        Q = maxsuffixpal(maxsuffix, a)
        creatednode = !haskey(Q.edges, a)
        if creatednode
            P = sizednode(Q.sz + 2)
            push!(nodes, P)
            if P.sz == 1
                P.link = evenroot
            else
                P.link = maxsuffixpal(Q.link, a).edges[a]
            end
            Q.edges[a] = P            # adds edge (Q, P)
        end
        maxsuffix = Q.edges[a]        # P becomes the new maxsuffix
        S *= string(a)
        creatednode
    end

    function getsubpalindromes()
        result = Vector{String}()
        getsubpalindromes(oddroot, [oddroot], "", result)
        getsubpalindromes(evenroot, [evenroot], "", result)
        result
    end

    function getsubpalindromes(nd, nodestohere, charstohere, result)
        for (lnkname, nd2) in nd.edges
            getsubpalindromes(nd2, vcat(nodestohere, nd2), charstohere * lnkname, result)
        end
        if nd !== oddroot && nd !== evenroot
            assembled = reverse(charstohere) *
                (nodestohere[1] === evenroot ? charstohere : charstohere[2:end])
            push!(result, assembled)
        end
    end

    println("Results of processing string \"$str\":")
    for c in str
        addchar(c)
    end
    println("Number of sub-palindromes: ", length(nodes))
    println("Sub-palindromes: ", getsubpalindromes())
end

eertree("eertree")


// version 1.1.4

class Node {
    val edges = mutableMapOf<Char, Node>()  // edges (or forward links)
    var link: Node? = null                  // suffix link (backward links)
    var len = 0                             // the length of the node
}

class Eertree(str: String) {
    val nodes = mutableListOf<Node>()

    private val rto = Node()                // odd length root node, or node -1
    private val rte = Node()                // even length root node, or node 0
    private val s = StringBuilder("0")      // accumulated input string, T = S[1..i]
    private var maxSufT = rte               // maximum suffix of tree T

    init {
        // Initialize and build the tree
        rte.link = rto
        rto.link = rte
        rto.len  = -1
        rte.len  = 0
        for (ch in str) add(ch)
    }

    private fun getMaxSuffixPal(startNode: Node, a: Char): Node {
        // We traverse the suffix-palindromes of T in the order of decreasing length.
        // For each palindrome we read its length k and compare T[i-k] against a
        // until we get an equality or arrive at the -1 node.
        var u = startNode
        val i = s.length
        var k = u.len
        while (u !== rto && s[i - k - 1] != a) {
            if (u === u.link!!) throw RuntimeException("Infinite loop detected")
            u = u.link!!
            k = u.len
        }
        return u
    }

    private fun add(a: Char): Boolean {
        // We need to find the maximum suffix-palindrome P of Ta
        // Start by finding maximum suffix-palindrome Q of T.
        // To do this, we traverse the suffix-palindromes of T
        // in the order of decreasing length, starting with maxSuf(T)
        val q = getMaxSuffixPal(maxSufT, a)

        // We check Q to see whether it has an outgoing edge labeled by a.
        val createANewNode = a !in q.edges.keys

        if (createANewNode) {
            // We create the node P of length Q + 2
            val p = Node()
            nodes.add(p)
            p.len = q.len + 2
            if (p.len == 1) {
                // if P = a, create the suffix link (P, 0)
                p.link = rte
            }
            else {
                // It remains to create the suffix link from P if |P|>1. Just
                // continue traversing suffix-palindromes of T starting with the
                // the suffix link of Q.
                p.link = getMaxSuffixPal(q.link!!, a).edges[a]
            }

            // create the edge (Q, P)
            q.edges[a] = p
        }

        // P becomes the new maxSufT
        maxSufT = q.edges[a]!!

        // Store accumulated input string
        s.append(a)

        return createANewNode
    }

    fun getSubPalindromes(): List<String> {
        // Traverse tree to find sub-palindromes
        val result = mutableListOf<String>()
        // Odd length words
        getSubPalindromes(rto, listOf(rto), "", result)
        // Even length words
        getSubPalindromes(rte, listOf(rte), "", result)
        return result
    }

    private fun getSubPalindromes(nd: Node, nodesToHere: List<Node>,
                          charsToHere: String, result: MutableList<String>) {
        // Each node represents a palindrome, which can be reconstructed
        // by the path from the root node to each non-root node.

        // Traverse all edges, since they represent other palindromes
        for ((lnkName, nd2) in nd.edges) {
            getSubPalindromes(nd2, nodesToHere + nd2, charsToHere + lnkName, result)
        }

        // Reconstruct based on charsToHere characters.
        if (nd !== rto && nd !== rte) { // Don't print for root nodes
            val assembled = charsToHere.reversed() +
                if (nodesToHere[0] === rte)  // Even string
                    charsToHere
                else  // Odd string
                    charsToHere.drop(1)
            result.add(assembled)
        }
    }
}

fun main(args: Array<String>) {
    val str = "eertree"
    println("Processing string '$str'")
    val eertree = Eertree(str)
    println("Number of sub-palindromes: ${eertree.nodes.size}")
    val result = eertree.getSubPalindromes()
    println("Sub-palindromes: $result")
}


; uses SRFI-1 library http://srfi.schemers.org/srfi-1/srfi-1.html

(define (evolve ls r)
  (unfold
    (lambda (x) (null? (cddr x)))
    (lambda (x)
      (vector-ref r (+ (* 4 (first x)) (* 2 (second x)) (third x))))
    cdr
    (cons (last ls) (append ls (list (car ls))))))

(define (automaton s r n)
  (define (*automaton s0 rv n)
    (for-each (lambda (x) (display (if (zero? x) #\. #\#))) s0)
    (newline)
    (if (not (zero? n))
      (let ((s1 (evolve s0 rv)))
	(*automaton s1 rv (- n 1)))))
  (display "Rule ")
  (display r)
  (newline)
  (*automaton
    s
    (list->vector
      (append
	(int->bin r)
	(make-list (- 7 (floor (/ (log r) (log 2)))) 0)))
    n))

(automaton '(0 1 0 0 0 1 0 1 0 0 1 1 1 1 0 0 0 0 0 1) 30 20)


class Automaton(rule, cells) {

    method init {
        rule = sprintf("%08b", rule).chars.map{.to_i}.reverse
    }

    method next {
        var previous = cells.map{_}
        var len = previous.len
        cells[] = rule[
                    previous.range.map { |i|
                        4*previous[i-1 % len] +
                        2*previous[i]         +
                        previous[i+1 % len]
                    }
                  ]
    }

    method to_s {
        cells.map { _ ? '#' : ' ' }.join
    }
}

var size = 20
var arr = size.of(0)
arr[size/2] = 1

var auto = Automaton(90, arr)

(size/2).times {
    print "|#{auto}|\n"
    auto.next
}


If Version<9.5 Then exit
If Version=9.5 And Revision<2 Then Exit
Class Node {
      inventory myedges
      length, suffix=0
      Function edges(s$) {
            =-1 : if exist(.myedges, s$) then =eval(.myedges)
      }
      Module edges_append (a$, where) {
            Append .myedges, a$:=where
      }
Class:
      Module Node(.length) {
            Read ? .suffix, .myedges
      }
}
function eertree(s$) {
      Const evenRoot=0, oddRoot=1
      Inventory Tree= oddRoot:=Node(-1,1),evenRoot:=Node(0,1)
      k=0
      suffix=oddRoot
      for i=0 to len(s$)-1 {
            c$=mid$(s$,i+1,1)
            n=suffix
            Do {
                 k=tree(n).length
                 b=i-k-1
                  if b>=0 then if mid$(s$,b+1,1)=c$ Then exit
                  n =tree(n).suffix
            } Always
            e=tree(n).edges(c$)
            if e>=0 then suffix=e :continue
            suffix=len(Tree)

            Append Tree, len(Tree):=Node(k+2)
            Tree(n).edges_append c$, suffix
            If tree(suffix).length=1 then tree(suffix).suffix=0 : continue
            Do {
                  n=tree(n).suffix
                  b=i-tree(n).length-1
                  if b>0 Then If  mid$(s$, b+1,1)=c$ then exit
            } Always
            e=tree(n).edges(c$)
            if e>=0 then tree(suffix).suffix=e

      }
      =tree
}
children=lambda (s, tree,  n, root$="")->{
            L=Len(tree(n).myEdges)
            if L=0 then =s : exit
            L--
            For i=0 to L {
                  c=tree(n).myEdges
                  c$=Eval$(c, i)  ' read keys at position i
                  nxt=c(i!)   '  read value using position
                  p$ = if$(n=1 -> c$, c$+root$+c$)
                  append s, (p$,)
                  \\ better use lambda() and not children()
                  \\ for recursion when we copy this lambda to other identifier.
                  s = lambda(s, tree, nxt, p$)
            }
         = s
      }
aString=Lambda ->{
          Push Quote$(Letter$)
}
aLine=Lambda ->{
      Shift 2  ' swap two top stack items
      if stackitem$()="" then  { Drop}  Else Push letter$+", "+Letter$
}
Palindromes$=Lambda$ children, aString, aLine (Tree)-> {
            ="("+children(children((,), Tree, 0), Tree, 1)#Map(aString)#Fold$(aline,"")+")"
 }

Print Palindromes$(eertree("eertree"))


import algorithm, strformat, strutils, tables

type

  Node = ref object
    edges: Table[char, Node]  # Edges (forward links).
    link: Node                # Suffix link (backward link).
    len: int                  # Length of the node.

  Eertree = object
    nodes: seq[Node]
    rto: Node                 # Odd length root node or node -1.
    rte: Node                 # Even length root node or node 0.Node
    str: string               # Accumulated input string.
    maxSuf: Node              # Maximum suffix.

#---------------------------------------------------------------------------------------------------

func initEertree(): Eertree =
  ## Create and initialize an eertree.
  result = Eertree(rto: Node(len: - 1), rte: Node(len: 0))
  result.rto.link = result.rto
  result.rte.link = result.rto
  result.str = "0"
  result.maxSuf = result.rte

#---------------------------------------------------------------------------------------------------

func getMaxSuffixPal(tree: Eertree; startNode: Node; ch: char): Node =
  ## We traverse the suffix-palindromes of "tree" in the order of decreasing length.
  ## For each palindrome we read its length "k" and compare "tree[i-k]" against "ch"
  ## until we get an equality or arrive at the -1 node.

  result = startNode
  let i = tree.str.high
  while result != tree.rto and tree.str[i - result.len] != ch:
    doAssert(result != result.link, "circular reference above odd root")
    result = result.link

#---------------------------------------------------------------------------------------------------

func add(tree: var Eertree; ch: char): bool =
  ## We need to find the maximum suffix-palindrome P of Ta.
  ## Start by finding maximum suffix-palindrome Q of T.
  ## To do this, we traverse the suffix-palindromes of T
  ## in the order of decreasing length, starting with maxSuf(T).

  let q = tree.getMaxSuffixPal(tree.maxSuf, ch)

  # We check "q" to see whether it has an outgoing edge labeled by "ch".
  result = ch notin q.edges

  if result:
    # We create the node "p" of length "q.len + 2"
    let p = Node()
    tree.nodes.add(p)
    p.len = q.len + 2
    if p.len == 1:
      # If p = ch, create the suffix link (p, 0).
      p.link = tree.rte
    else:
      # It remains to create the suffix link from "p" if "|p|>1". Just continue
      # traversing suffix-palindromes of T starting with the suffix link of "q".
      p.link = tree.getMaxSuffixPal(q.link, ch).edges[ch]
    # Create the edge "(q, p)".
    q.edges[ch] = p

  # "p" becomes the new maxSuf.
  tree.maxSuf = q.edges[ch]

  # Store accumulated input string.
  tree.str.add(ch)

#---------------------------------------------------------------------------------------------------

func getSubPalindromes(tree: Eertree; node: Node;
                       nodesToHere: seq[Node]; charsToHere: string;
                       result: var seq[string]) =
  ## Each node represents a palindrome, which can be reconstructed
  ## by the path from the root node to each non-root node.

  # Traverse all edges, since they represent other palindromes.
  for linkName, node2 in node.edges.pairs:
    tree.getSubPalindromes(node2, nodesToHere & node2, charsToHere & linkName, result)

  # Reconstruct based on charsToHere characters.
  if node notin [tree.rto, tree.rte]:
    # Don’t print for root nodes.
    let assembled = reversed(charsTohere).join() &
                    (if nodesToHere[0] == tree.rte: charsToHere
                     else: charsToHere[1..^1])
    result.add(assembled)

#---------------------------------------------------------------------------------------------------

func getSubPalindromes(tree: Eertree): seq[string] =
  ## Traverse tree to find sub-palindromes.

  # Odd length words
  tree.getSubPalindromes(tree.rto, @[tree.rto], "", result)
  # Even length words
  tree.getSubPalindromes(tree.rte, @[tree.rte], "", result)

#———————————————————————————————————————————————————————————————————————————————————————————————————

when isMainModule:
  const Str = "eertree"
  echo fmt"Processing string: '{Str}'"
  var eertree = initEertree()
  for ch in Str:
    discard eertree.add(ch)
  echo fmt"Number of sub-palindromes: {eertree.nodes.len}"
  let result = eertree.getSubPalindromes()
  echo fmt"Sub-palindromes: {result.join("", "")}"


use Collection.Generic;

class Eertree {
  function : Main(args : String[]) ~ Nil {
    tree := GetEertree("eertree");
    Show(SubPalindromes(tree));
  }

  function : GetEertree(s : String) ~ Vector<Node> {
    tree := Vector->New()<Node>;
    tree->AddBack(Node->New(0, Nil, 1));
    tree->AddBack(Node->New(-1, Nil, 1));
    suffix := 1;

    n : Int; k : Int;
    for(i := 0; i < s->Size(); ++i;) {
      c := s->Get(i);

      done := false;
      for (j := suffix; <>done; j := tree->Get(j)->GetSuffix();) {
        k := tree->Get(j)->GetLength();
        b := i - k - 1;
        if (b >= 0 & s->Get(b) = c) {
          n := j;
          done := true;
        };
      };
      skip := false;
      if (tree->Get(n)->GetEdges()->Has(c)) {
        suffix := tree->Get(n)->GetEdges()->Find(c)->Get();
        skip := true;
      };

      if(<>skip) {
        suffix := tree->Size();
        tree->AddBack(Node->New(k + 2));
        tree->Get(n)->GetEdges()->Insert(c, suffix);
        if (tree->Get(suffix)->GetLength() = 1) {
          tree->Get(suffix)->SetSuffix(0);
          skip := true;
        };

        if(<>skip) {
          done := false;
          while (<>done) {
            n := tree->Get(n)->GetSuffix();
            b := i - tree->Get(n)->GetLength() - 1;
            if (b >= 0 & s->Get(b) = c) {
              done := true;
            };
          };
          tree->Get(suffix)->SetSuffix(tree->Get(n)->GetEdges()->Find(c)->Get());
        };
      };
    };

    return tree;
  }

  function : SubPalindromes(tree : Vector<Node>) ~ Vector<String> {
    s := Vector->New()<String>;
    SubPalindromesChildren(0, "", tree, s);

    keys := tree->Get(1)->GetEdges()->GetKeys()<CharHolder>;
    each(k : keys) {
      key := keys->Get(k);
      str := key->Get()->ToString();
      s->AddBack(str);
      value := tree->Get(1)->GetEdges()->Find(key)->As(IntHolder)->Get();
      SubPalindromesChildren(value, str, tree, s);
    };

    return s;
  }

  function : SubPalindromesChildren(n : Int, p : String, tree : Vector<Node>, s : Vector<String>)  ~ Nil {
    keys := tree->Get(n)->GetEdges()->GetKeys()<CharHolder>;
    each(k : keys) {
      key := keys->Get(k);
      c := key->Get();
      value := tree->Get(n)->GetEdges()->Find(key)->As(IntHolder)->Get();
      str := ""; str += c; str += p; str += c;
      s->AddBack(str);
      SubPalindromesChildren(value, str, tree, s);
    };
  }

  function : Show(result : Vector<String>) ~ Nil {
    out := "[";
    each(i : result) {
      out += result->Get(i);
      if(i + 1 < result->Size()) {
        out += ", ";
      };
    };
    out += "]";
    out->PrintLine();
  }
}

class Node {
  @length : Int;
  @edges : Map<CharHolder, IntHolder>;
  @suffix : Int;

  New(length : Int, edges : Map<CharHolder, IntHolder>, suffix : Int) {
    @length := length;
    @edges := edges <> Nil ? edges : Map->New()<CharHolder, IntHolder>;
    @suffix := suffix;
  }

  New(length : Int) {
    @length := length;
    @edges := Map->New()<CharHolder, IntHolder>;
  }

  method : public : GetLength() ~ Int {
    return @length;
  }

  method : public : GetSuffix() ~ Int {
    return @suffix;
  }

  method : public : SetSuffix(suffix : Int) ~ Nil {
    @suffix := suffix;
  }

  method : public : GetEdges() ~ Map<CharHolder, IntHolder> {
    return @edges;
  }
}


package require Tcl 8.6

oo::class create ElementaryAutomaton {
    variable rules
    # Decode the rule number to get a collection of state mapping rules.
    # In effect, "compiles" the rule number
    constructor {ruleNumber} {
	set ins {111 110 101 100 011 010 001 000}
	set bits [split [string range [format %08b $ruleNumber] end-7 end] ""]
	foreach input {111 110 101 100 011 010 001 000} state $bits {
	    dict set rules $input $state
	}
    }

    # Apply the rule to an automaton state to get a new automaton state.
    # We wrap the edges; the state space is circular.
    method evolve {state} {
	set len [llength $state]
	for {set i 0} {$i < $len} {incr i} {
	    lappend result [dict get $rules [
		    lindex $state [expr {($i-1)%$len}]][
		    lindex $state $i][
		    lindex $state [expr {($i+1)%$len}]]]
	}
	return $result
    }

    # Simple driver method; omit the initial state to get a centred dot
    method run {steps {initialState ""}} {
	if {[llength [info level 0]] < 4} {
	    set initialState "[string repeat . $steps]1[string repeat . $steps]"
	}
	set s [split [string map ". 0 # 1" $initialState] ""]
	for {set i 0} {$i < $steps} {incr i} {
	    puts [string map "0 . 1 #" [join $s ""]]
	    set s [my evolve $s]
	}
	puts [string map "0 . 1 #" [join $s ""]]
    }
}


$str = "eertree";

for $n (1 .. length($str)) {
   for $m (1 .. length($str)) {
      $strrev = "";
      $strpal = substr($str, $n-1, $m);
      if ($strpal ne "") {
         for $p (reverse 1 .. length($strpal)) {
            $strrev .= substr($strpal, $p-1, 1);
         }
         ($strpal eq $strrev) and push @pal, $strpal;
      }
   }
}

print join ' ', grep {not $seen{$_}++} @pal, "\n";


with javascript_semantics
enum LEN,SUFF,CHARS,NEXT

function node(integer len, suffix=1, string chars="", sequence next={})
    return {len,suffix,chars,next} -- must match above enum!
end function

function eertree(string s)
sequence tree = {node(-1),  -- odd lengths
                 node(0)}   -- even lengths
integer suff = 2    -- max suffix palindrome

    for i=1 to length(s) do
        integer cur = suff, curlen, ch = s[i], k
        while (true) do
            curlen = tree[cur][LEN]
            k = i-1-curlen
            if k>=1 and s[k]==ch then
                exit
            end if
            cur = tree[cur][SUFF]
        end while
        k = find(ch,tree[cur][CHARS])
        if k then
            suff = tree[cur][NEXT][k]
        else
            tree = append(tree,node(curlen+2))
            suff = length(tree)
            tree[cur][CHARS] &= ch
            tree[cur][NEXT] = deep_copy(tree[cur][NEXT])&suff

            if tree[suff][LEN]==1 then
                tree[suff][SUFF] = 2
            else
                while (true) do
                    cur = tree[cur][SUFF]
                    curlen = tree[cur][LEN]
                    k = i-1-curlen
                    if k>=0 and s[k]==ch then
                        k = find(ch,tree[cur][CHARS])
                        if k then
                            tree[suff][SUFF] = tree[cur][NEXT][k]
                        end if
                        exit
                    end if
                end while
            end if
        end if
    end for
    return tree
end function

function children(sequence s, tree, integer n, string root="")
    for i=1 to length(tree[n][CHARS]) do
        integer c = tree[n][CHARS][i],
                nxt = tree[n][NEXT][i]
        string p = iff(n=1 ? c&""
                           : c&root&c)
        s = append(s, p)
        s = children(s, tree, nxt, p)
    end for
    return s
end function

procedure main()
    sequence tree = eertree("eertree")
    puts(1,"tree:\n")
    for i=1 to length(tree) do
        sequence ti = deep_copy(tree[i])
        ti[NEXT] = sprint(ti[NEXT])
        ti = i&ti
        printf(1,"[%d]: len:%2d  suffix:%d  chars:%-5s next:%s\n",ti)
    end for
    puts(1,"\n")

    -- odd then even lengths:
    ?children(children({},tree,1), tree, 2)
end procedure
main()


import "/fmt" for Conv

var SIZE  = 32
var LINES = SIZE / 2
var RULE  = 90

var ruleTest = Fn.new { |x| (RULE & (1 << (7 & x))) != 0 }

var bshl = Fn.new { |b, bitCount| Conv.btoi(b) << bitCount }

var evolve = Fn.new { |s|
    var t = List.filled(SIZE, false)
    t[SIZE-1] = ruleTest.call(bshl.call(s[0], 2) | bshl.call(s[SIZE-1], 1) | Conv.btoi(s[SIZE-2]))
    t[0] = ruleTest.call(bshl.call(s[1], 2) | bshl.call(s[0], 1) | Conv.btoi(s[SIZE-1]))
    for (i in 1...SIZE - 1) {
        t[i] = ruleTest.call(bshl.call(s[i+1], 2) | bshl.call(s[i], 1) | Conv.btoi(s[i-1]))
    }
    for (i in 0...SIZE) s[i] = t[i]
}

var show = Fn.new { |s|
    for (i in SIZE - 1..0) System.write(s[i] ? "*" : " ")
    System.print()
}

var state = List.filled(SIZE, false)
state[LINES] = true
System.print("Rule %(RULE):")
for (i in 0...LINES) {
    show.call(state)
    evolve.call(state)
}


#!/bin/python
from __future__ import print_function

class Node(object):
	def __init__(self):
		self.edges = {} # edges (or forward links)
		self.link = None # suffix link (backward links)
		self.len = 0 # the length of the node

class Eertree(object):
	def __init__(self):
		self.nodes = []
		# two initial root nodes
		self.rto = Node() #odd length root node, or node -1
		self.rte = Node() #even length root node, or node 0

		# Initialize empty tree
		self.rto.link = self.rte.link = self.rto;
		self.rto.len = -1
		self.rte.len = 0
		self.S = [0] # accumulated input string, T=S[1..i]
		self.maxSufT = self.rte # maximum suffix of tree T

	def get_max_suffix_pal(self, startNode, a):
		# We traverse the suffix-palindromes of T in the order of decreasing length.
		# For each palindrome we read its length k and compare T[i-k] against a
		# until we get an equality or arrive at the -1 node.
		u = startNode
		i = len(self.S)
		k = u.len
		while id(u) != id(self.rto) and self.S[i - k - 1] != a:
			assert id(u) != id(u.link) #Prevent infinte loop
			u = u.link
			k = u.len

		return u

	def add(self, a):

		# We need to find the maximum suffix-palindrome P of Ta
		# Start by finding maximum suffix-palindrome Q of T.
		# To do this, we traverse the suffix-palindromes of T
		# in the order of decreasing length, starting with maxSuf(T)
		Q = self.get_max_suffix_pal(self.maxSufT, a)

		# We check Q to see whether it has an outgoing edge labeled by a.
		createANewNode = not a in Q.edges

		if createANewNode:
			# We create the node P of length Q+2
			P = Node()
			self.nodes.append(P)
			P.len = Q.len + 2
			if P.len == 1:
				# if P = a, create the suffix link (P,0)
				P.link = self.rte
			else:
				# It remains to create the suffix link from P if |P|>1. Just
				# continue traversing suffix-palindromes of T starting with the suffix
				# link of Q.
				P.link = self.get_max_suffix_pal(Q.link, a).edges[a]

			# create the edge (Q,P)
			Q.edges[a] = P

		#P becomes the new maxSufT
		self.maxSufT = Q.edges[a]

		#Store accumulated input string
		self.S.append(a)

		return createANewNode

	def get_sub_palindromes(self, nd, nodesToHere, charsToHere, result):
		#Each node represents a palindrome, which can be reconstructed
		#by the path from the root node to each non-root node.

		#Traverse all edges, since they represent other palindromes
		for lnkName in nd.edges:
			nd2 = nd.edges[lnkName] #The lnkName is the character used for this edge
			self.get_sub_palindromes(nd2, nodesToHere+[nd2], charsToHere+[lnkName], result)

		#Reconstruct based on charsToHere characters.
		if id(nd) != id(self.rto) and id(nd) != id(self.rte): #Don't print for root nodes
			tmp = "".join(charsToHere)
			if id(nodesToHere[0]) == id(self.rte): #Even string
				assembled = tmp[::-1] + tmp
			else: #Odd string
				assembled = tmp[::-1] + tmp[1:]
			result.append(assembled)

if __name__=="__main__":
	st = "eertree"
	print ("Processing string", st)
	eertree = Eertree()
	for ch in st:
		eertree.add(ch)

	print ("Number of sub-palindromes:", len(eertree.nodes))

	#Traverse tree to find sub-palindromes
	result = []
	eertree.get_sub_palindromes(eertree.rto, [eertree.rto], [], result) #Odd length words
	eertree.get_sub_palindromes(eertree.rte, [eertree.rte], [], result) #Even length words
	print ("Sub-palindromes:", result)


fcn rule(n){ n=n.toString(2); "00000000"[n.len() - 8,*] + n }
fcn applyRule(rule,cells){
   cells=String(cells[-1],cells,cells[0]); // wrap cell ends
   (cells.len() - 2).pump(String,'wrap(n){ rule[7 - cells[n,3].toInt(2)] })
}