text: import numpy as np 
  import time
  
  
  test1='''../.# => ##./#../...
  .#./..#/### => #..#/..../..../#..#'''
  
  real='''../.. => .##/#../..#
  #./.. => .##/#../###
  ##/.. => ..#/#.#/#..
  .#/#. => #../#../.#.
  ##/#. => .#./#../#..
  ##/## => .##/.../.##
  .../.../... => #.#./###./####/#..#
  #../.../... => .###/####/##../#.##
  .#./.../... => ###./.###/#..#/#.##
  ##./.../... => ..../..../.#../##..
  #.#/.../... => ...#/.##./..../##..
  ###/.../... => ##../##../##.#/..##
  .#./#../... => .#../###./##../####
  ##./#../... => ####/##.#/..../..##
  ..#/#../... => ..#./####/...#/#.##
  #.#/#../... => #.#./##../##../.##.
  .##/#../... => ##../####/..#./...#
  ###/#../... => #..#/#.#./##.#/#.#.
  .../.#./... => .#.#/..#./#.../....
  #../.#./... => ##../..##/..##/.#..
  .#./.#./... => ..../##../##../#.##
  ##./.#./... => ...#/##../#..#/.###
  #.#/.#./... => ####/##.#/###./..##
  ###/.#./... => ..../...#/.###/.#..
  .#./##./... => #.#./#..#/.##./.#.#
  ##./##./... => .###/#.../#..#/#.#.
  ..#/##./... => .###/####/..../#.##
  #.#/##./... => ...#/.###/.###/.###
  .##/##./... => ..##/..##/.###/##.#
  ###/##./... => ####/#..#/####/#.#.
  .../#.#/... => #.##/..#./.###/#.#.
  #../#.#/... => ####/##.#/##.#/....
  .#./#.#/... => #.../...#/#.##/#..#
  ##./#.#/... => .#.#/##../##../....
  #.#/#.#/... => ##.#/#.../##../.#..
  ###/#.#/... => ...#/###./.#.#/...#
  .../###/... => .###/#.##/#.../###.
  #../###/... => ..##/.#../.###/..#.
  .#./###/... => ..../.##./#.##/#.##
  ##./###/... => .#.#/##.#/#.../#.#.
  #.#/###/... => ..#./#.../#.#./.##.
  ###/###/... => ..##/.#.#/#..#/.##.
  ..#/.../#.. => ..##/.#../##.#/##..
  #.#/.../#.. => ..#./..../#.../...#
  .##/.../#.. => .##./..##/####/#...
  ###/.../#.. => #.##/..../##../#.##
  .##/#../#.. => .###/...#/###./....
  ###/#../#.. => .#.#/#.#./#.##/..#.
  ..#/.#./#.. => ...#/..#./..##/.#.#
  #.#/.#./#.. => #.../##.#/.###/#.#.
  .##/.#./#.. => ###./####/#..#/##.#
  ###/.#./#.. => ..../..#./..../#...
  .##/##./#.. => .#.#/.##./.#.#/#.##
  ###/##./#.. => ..../##../###./.#.#
  #../..#/#.. => ...#/#.../#.##/.###
  .#./..#/#.. => #..#/.#../###./#.#.
  ##./..#/#.. => #.#./..#./###./###.
  #.#/..#/#.. => .#.#/##.#/##../####
  .##/..#/#.. => ###./..../.#../...#
  ###/..#/#.. => #.#./.##./.#.#/#..#
  #../#.#/#.. => #.#./##.#/.#../.###
  .#./#.#/#.. => ##.#/#.#./#.../####
  ##./#.#/#.. => .#.#/#.../..#./#.##
  ..#/#.#/#.. => ##.#/.##./#.../.###
  #.#/#.#/#.. => ..##/..../..../####
  .##/#.#/#.. => ####/#.#./###./.#.#
  ###/#.#/#.. => #.##/..#./##../#...
  #../.##/#.. => ..##/##.#/####/.#..
  .#./.##/#.. => ..##/##../.#../..##
  ##./.##/#.. => ..##/.#.#/#..#/....
  #.#/.##/#.. => #.../##../...#/.#.#
  .##/.##/#.. => ##../...#/.###/.#.#
  ###/.##/#.. => ####/..#./.##./#.##
  #../###/#.. => .#.#/##.#/#.#./#.#.
  .#./###/#.. => .###/#..#/.#.#/###.
  ##./###/#.. => ##../.#../###./.#.#
  ..#/###/#.. => #.##/..../...#/..#.
  #.#/###/#.. => #.../#..#/..../.#..
  .##/###/#.. => ####/#..#/..#./.#.#
  ###/###/#.. => .##./##../.#../..#.
  .#./#.#/.#. => #.#./.###/#.#./..##
  ##./#.#/.#. => .##./..../..##/##..
  #.#/#.#/.#. => ...#/..../.#.#/..##
  ###/#.#/.#. => .#../####/#.#./#.##
  .#./###/.#. => #..#/.#.#/#..#/#.#.
  ##./###/.#. => .#../##../#..#/..##
  #.#/###/.#. => #.#./.##./##.#/.#.#
  ###/###/.#. => #.#./...#/..##/#...
  #.#/..#/##. => ..#./..#./...#/#..#
  ###/..#/##. => #..#/###./..../##.#
  .##/#.#/##. => #.##/.#.#/...#/..##
  ###/#.#/##. => #.##/...#/.##./.###
  #.#/.##/##. => ..../##.#/..../...#
  ###/.##/##. => .###/#.../###./###.
  .##/###/##. => #.../#.#./.###/..#.
  ###/###/##. => #.##/.#../..#./.#.#
  #.#/.../#.# => .##./##../###./.###
  ###/.../#.# => ..##/...#/###./.#..
  ###/#../#.# => ##.#/..#./#.##/.#..
  #.#/.#./#.# => .#../#.##/...#/###.
  ###/.#./#.# => ..#./..../####/####
  ###/##./#.# => ###./#..#/..../#..#
  #.#/#.#/#.# => ##.#/###./..../#...
  ###/#.#/#.# => ##../.###/#..#/.#..
  #.#/###/#.# => #.../###./.###/..#.
  ###/###/#.# => ..../.##./.#../###.
  ###/#.#/### => ##../#.../.###/#...
  ###/###/### => .###/###./#.##/..#.'''
  
  
  
  def process(inp): 
      src = inp.split('/')
      rows = []
      for row in src: 
          numbers = []
          for col in row: 
              if col == ".": 
                  numbers.append(0)
              else: 
                  numbers.append(1)
          rows.append(numbers)
      return rows
  
  def part1(inp): 
      lines = inp.split('\n')
      i3 = 0
      rules = {}
      for line in lines: 
          orig,tranform = line.split(' => ')
          
          rows = process(orig)
          t_rows = np.array(process(tranform))
  
          # print(rows)
          size = len(rows)
  
          if size == 2: 
              for i in range(4): 
                  rot_rows = np.rot90(rows,i)
                  pattern1 = tuple((tuple(rot_rows[0]), tuple(rot_rows[1])))
                  rules[pattern1] = t_rows
  
                  pattern2 = tuple((tuple(rot_rows.T[0]), tuple(rot_rows.T[1])))
                  rules[pattern2] = t_rows
  
          else: # size = 3
  
              i3 += 1 
              for i in range(4): 
                  rot_rows = np.rot90(rows,i)
  
                  pattern1 = tuple((tuple(rot_rows[0]), tuple(rot_rows[1]), tuple(rot_rows[2])))
                  rules[pattern1] = t_rows
  
                  pattern2 = tuple((tuple(rot_rows.T[0]), tuple(rot_rows.T[1]), tuple(rot_rows.T[2])))
                  rules[pattern2] = t_rows
  
      img = [[0,1,0],
               [0,0,1], 
               [1,1,1]]
      img = np.array(img, dtype=int)
  
      for i in range(18): 
          size = len(img)
          if size % 2 == 0: 
              n = size//2
              new_img = np.empty((size + n, size+n), dtype=int)
              for j in range(n): 
                  for k in range(n): 
                      row_start = 2*j
                      row_end = 2*j + 2
                      col_start = 2*k 
                      col_end = 2*k + 2
                      part = img[row_start:row_end, col_start:col_end]
                      pattern = tuple((tuple(part[0]), tuple(part[1])))
                      new = rules[pattern]
  
                      row_start = 3*j
                      row_end = 3*j + 3
                      col_start = 3*k 
                      col_end = 3*k + 3
                      new_img[row_start:row_end, col_start:col_end] = new
  
              img = new_img
          elif size % 3 == 0: 
              n = size//3
              new_img = np.empty((size + n, size+n), dtype=int)
              for j in range(n): 
                  for k in range(n): 
                      row_start = 3*j
                      row_end = row_start + 3
                      col_start = 3*k 
                      col_end = col_start + 3
                      # print('3:', row_start, row_end, col_start, col_end)
                      part = img[row_start:row_end, col_start:col_end]
                      pattern = tuple((tuple(part[0]), tuple(part[1]), tuple(part[2])))
                      new = rules[pattern]
                      
                      row_start = 4*j
                      row_end = row_start + 4
                      col_start = 4*k 
                      col_end = col_start + 4
                      new_img[row_start:row_end, col_start:col_end] = new
  
              img = new_img
          else: 
              raise ValueError("Houston we have a problem!")
          # print(img)
          if i ==4: 
              print(np.sum(img))
      return img
  
  st = time.time()
  new_img = part1(real)
  print(np.sum(new_img))
  print('time', time.time()-st)
  
  
  