# Draw an indexed fractal

## Introduction

In this challenge, a 2×2 matrix is indexed like this:

0 1
2 3


We define a family of fractal-like patterns F(L), where L is a length-n list of these indices and F(L) has size 2n-1 × 2n-1.

• If L == [], then F(L) is the 1×1 pattern #.
• If L != [], then F(L) is constructed as follows. Let P be the pattern obtained from L with first element removed. Take four grids of size 2n-1-1 × 2n-1-1 filled with periods ., and replace the grid indexed by L[0] with the pattern P. Then, glue the grids together using one layer of hashes # between them. Here are diagrams for the four cases:

L[0]==0  L[0]==1  L[0]==2  L[0]==3
#...  ...#     ...#...  ...#...
[P]#...  ...#[P]  ...#...  ...#...
#...  ...#     ...#...  ...#...
#######  #######  #######  #######
...#...  ...#...     #...  ...#
...#...  ...#...  [P]#...  ...#[P]
...#...  ...#...     #...  ...#


## Example

Consider the input L = [2,0]. We begin with the 1×1 grid #, and traverse L from the right. The rightmost element is 0, so we take four copies of the 1×1 grid ., replace the first one by #, and glue them together with hashes. This results in the 3×3 grid

##.
###
.#.


The next element is 2, so we take four copies of the 3×3 grid of .s, and replace the third one with the above grid. The four grids are

...  ...  ##.  ...
...  ...  ###  ...
...  ...  .#.  ...


and gluing them together with #s results in the 7×7 grid

...#...
...#...
...#...
#######
##.#...
####...
.#.#...


This is our final output.

## Input

Your input is a list L of the indices 0, 1, 2, 3. You can take it as a list of integers, or a string of digits. Note that it may be empty, and it may contain duplicates. The length of L is at most 5.

## Output

Your output is the pattern F(L) as a newline-delimited string.

## Rules and scoring

You can write a full program or a function. the lowest byte count wins, and standard loopholes are disallowed.

## Test cases

[]
#

[0]
##.
###
.#.

[3]
.#.
###
.##

[2,0]
...#...
...#...
...#...
#######
##.#...
####...
.#.#...

[1,1]
...#.##
...####
...#.#.
#######
...#...
...#...
...#...

[1,2,0]
.......#...#...
.......#...#...
.......#...#...
.......########
.......###.#...
.......#####...
.......#.#.#...
###############
.......#.......
.......#.......
.......#.......
.......#.......
.......#.......
.......#.......
.......#.......

[3,3,1]
.......#.......
.......#.......
.......#.......
.......#.......
.......#.......
.......#.......
.......#.......
###############
.......#...#...
.......#...#...
.......#...#...
.......########
.......#...#.##
.......#...####
.......#...#.#.

[0,1,2,3]
.......#...#...#...............
.......#...#...#...............
.......#...#...#...............
.......#########...............
.......#.#.#...#...............
.......#####...#...............
.......#.###...#...............
################...............
.......#.......#...............
.......#.......#...............
.......#.......#...............
.......#.......#...............
.......#.......#...............
.......#.......#...............
.......#.......#...............
###############################
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............
...............#...............

[0,0,1,2,3]
.......#...#...#...............#...............................
.......#...#...#...............#...............................
.......#...#...#...............#...............................
.......#########...............#...............................
.......#.#.#...#...............#...............................
.......#####...#...............#...............................
.......#.###...#...............#...............................
################...............#...............................
.......#.......#...............#...............................
.......#.......#...............#...............................
.......#.......#...............#...............................
.......#.......#...............#...............................
.......#.......#...............#...............................
.......#.......#...............#...............................
.......#.......#...............#...............................
################################...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
...............#...............#...............................
###############################################################
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................
...............................#...............................

• In your example, why do you begin with the 1x1 grid #? L !=[] in that example, as it has 1 or more elements. Does this mean that F(L) is always a # at first? Commented Apr 10, 2016 at 20:10
• @R.Kap Okay, the example is not very clear. The definition is recursive, so for L = [2,0], you chop off the head and look at the pattern F([0]), then chop off the head of [0] and look at the pattern F([]), which is the 1x1 grid #. Then you use the chopped-off index 0 on it to build the 3x3 pattern, and use the chopped-off index 2 on that one to build the 7x7 pattern. To answer your question: yes, you always begin with the 1x1 grid since that's the base case of the recursion. Commented Apr 10, 2016 at 20:15
• Related reading Commented Apr 11, 2016 at 11:51

## CJam, 59474341 40 bytes

Thanks to Sp3000 for saving 1 byte.

Sal~W%{_Bff|a4*I@t2/{zSf*z}:F%F}fI3ff+N*


Test it here.

### Explanation

Slightly outdated. Will fix later.

All the dimension reorderings of 4D lists are making me dizzy...

This code implements the specification very literally, using the iterative algorithm from the example section instead of its recursive definition.. One major golfing trick is that I'm using spaces instead of # during the computation and only replace them with # at the end, which simplifies the code in one place and allows me to use S instead of '# or "#" in several.

Sa       e# Push [" "], i.e. a 1x1 grid containing only a space as the
e# initial fractal.
l~       e# Read and evaluate input.
W%       e# Reverse the list.
{        e# For each list element, assigning the element to variable I...
_      e#   Duplicate the grid.
Eff|   e#   Map (OR 14) over each character in the grid, turning spaces into
e#   periods and leaving periods unchanged.
a4*    e#   Create an array with four copies of this cleared grid.
I@t    e#   Replace the Ith element in this list with the previous grid.
2/     e#   Split this array into a 2x2 grid of subgrids...
e#   Now it's getting a bit weird... we've got 4 dimensions now, which are:
e#    - Rows of the 2x2 meta-grid.
e#    - Cells in each row of the 2x2 meta-grid (i.e. subgrids).
e#    - Rows of each subgrid.
e#    - Characters in each row of each subgrid.
:z     e#   Transpose each outer row, i.e. swap dimensions 2 and 3.
e#   We've now got in each row of the meta-grid, a list of pairs of
e#   corresponding rows of the subgrids.
Sff*   e#   Join those pairs of rows with a single space each. We're now down
e#   to three dimensions:
e#    - Rows of the 2x2 meta-grid.
e#    - Rows of each 1x2 block of the meta-grid.
e#    - Characters in each row of those blocks.
:z     e#   Transpose the blocks, i.e. turn the 1x2 blocks into a list of
e#   columns of their characters.
z      e#   Transpose the outer grid, i.e. turn it into a list of pairs of
e#   corresponding columns in the two 1x2 blocks.
Sf*    e#   Join each pair of columns with a single space. We've now got the
e#   new grid we're looking for, but it's a list of columns, i.e. transposed.
z      e#   Fix that by transposing the entire grid once more.
}I
N*       e# Join the rows of the grid with linefeeds.
S'#er    e# Replace all spaces with #.


# MATL, 42 41 bytes

'.#'4:He!XIiP"Iq@=wX*1X@WZ(l5MY(]3Lt3$)Q)  Try it online! ### Explanation This works iteratively using a Kronecker product to extend the array in each iteration. The array is built with 0 and 1 instead of . and #, and at the end they are replaced by the appropriate characters. There will be as many iterations as the input size. Input is processed from right to left. Iteration index starts at 1. Using the example in the challenge, with input [2,0], the array is initiallized as 1 2 3 4  This corresponds to the initial 1 (#) extended by one row and one column, whose purpose will be clear later. The values in those columns are not important, as they will be overwritten; they could equally be ones: 1 1 1 1  At each iteration, the existing array is Kronecker-multiplied by a 2×2 zero-one array that contains 1 at the position indicated by the current entry of the input, and 0 at the other entries. In the example at iteration i = 1, since the rightmost input entry is 0, the zero-one array is 1 0 0 0  and the Kronecker product of these two arrays is  1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0  Next, the row and column with index 2^i are filled with ones:  1 1 0 0 1 1 1 1 0 1 0 0 0 1 0 0  The first three rows and columns constitute the result of the first iteration. As before, there are an extra row and column, which are useful for extending the array in the next iteration. At iteration i = 2, since the current input value contains 2 the array above is Kronecker-multiplied by 0 0 1 0  which gives  0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0  Filling the 2^i-th row and column with ones gives  0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 1 0 0 0 0 1 1 1 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 1 0 0 0 0  Since this is the last iteration, the extra row and column are removed:  0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 1 1 0 0 0 0 1 0 1 0 0 0  and the character substitution is done to produce the final result: ...#... ...#... ...#... ####### ##.#... ####... .#.#...  Detailed description of the code follows: '.#' % Push this string. Will be indexed into 4:He! % Push 2×2 array [1 2; 3 4] XI % Copy it into clipboard I iP % Input array and reverse it " % For each entry of the reversed input I % Push [1 2; 3 4] from clipboard I q % Subtract 1 to yield [0 1; 2 3] @= % Compare with current entry of the input. Gives 2×2 array % with an entry equal to 1 and the rest 0 wX* % Swap. Kronecker product 1 % Push 1 X@ % Push iteration index, i W % Compute 2^i Z( % Write 1 into column 2^i l % Push 1 5M % Push 2^i again Y( % Write 1 into row 2^i ] % End for each 3Lt % Push [1, -1j] (corresponding to index 1:end-1) twice 3$)       % Apply index. Removes last row and column
Q         % Add 1. Gives an array of values 1 and 2
)         % Index into initial string


## Haskell, 123 122 bytes

unlines.foldr(#)["#"]
n#p=zipWith(++)(r++h:t)$('#':)<$>u++h:s where b='.'<$p<$p;h='#'<$p;(r:s:t:u:_)=drop n$cycle[p,b,b,b]


Usage example:

*Main> putStr $(unlines.foldr(#)["#"]) [2,3,1] .......#....... .......#....... .......#....... .......#....... .......#....... .......#....... .......#....... ############### ...#...#....... ...#...#....... ...#...#....... ########....... ...#.###....... ...#####....... ...#.#.#.......  How it works:  ["#"] -- starting with "#" foldr(#) -- fold the function # from the right into the input unlines -- and join the result with newlines n#p= -- helper function # -- n: next index, p: fractal so far zipWith(++) -- join the left and right part elementwise (r++h:t) -- left part ('#':) <$> u++h:s   -- right part (prepend '#' to each line for vertical
-- separator

-- helper
b='.'<$p<$p                -- b is a blank square of the same size as p
h='#'<$p -- h is a line of '#' of the same length as p (r:s:t:u:_)= -- drop the first n elements of the infinite drop n$cycle[p,b,b,b]  --   list [p,b,b,b,p,b,b,b,p,b,b,b,...] and
--   assign the next 4 element to r,s,t,u.
--   As r,s,t,u are always inserted at the
--   same position in the fractal, we get the
--   variants by assigning different values.


## JavaScript (ES6), 171 152 bytes

([d,...a],h=#,r=replace)=>d<4?(s=f(a)[r](/.+/g,s=>(t=s[r](/./g,.),d&1?t+h+s:s+h+t)),t=s[r](/.+/g,w=t+h+t),w=
${w[r](/./g,h)} ,d&2?t+w+s:s+w+t):h  Takes the result of the recursive call, then replaces each line with itself plus a hash plus a string of dots of the same length, in reverse order if necessary, then from that partial result creates a string of dots except for the newlines and central column of hashes, and also a string of hashes with surrounding newlines, then joins those three strings together in the appropriate order. # Ruby, 143 134 bytes An anonymous function. 1 byte saved by a rearrangement of the first line. 6 bytes saved by changing the way z is incremented from a formula to a table. 2 bytes saved by eliminating varable w. ->a{r=-1+u=2<<a.size s=(?.*r+$/)*r
a<<0
z=r*u/2-1
a.each{|i|r/=2
(-r..r).each{|j|s[z+j]=s[z+j*u]=?#}
z+=-r/2*[u+1,u-1,1-u,-u-1][i]}
s}


Ungolfed in test program

f=->a{
r=w=(u=2<<a.size)-1        #w=length of line excluding newline, u=length of line including newline.
s=(?.*w+$/)*w #initialize string s with w rows of w dots terminated by newlines. z=w*u/2-1 #z is the centre of the fractal a<<0 #add a dummy value to the end of a a.each{|i| #for each element in a r/=2 #r is the radius of the current iteration: ....15,7,3,1 (-r..r).each{|j| #for j=-r to r s[z+j]=s[z+j*u]=?# #overwrite . with #, forming horizontal and vertical lines } z+=-r/2*(u+1)+ #move z to centre of upper left quarter (where it should be if i=0) i%2*(q=r+1)+ #move across if i=1,3 i/2%2*q*u #and down if i=2,3 } s} #return string puts$/,f[[]]

puts $/,f[[0]] puts$/,f[[3]]

puts $/,f[[2,0]] puts$/,f[[1,1]]

puts $/,f[[1,2,0]] puts$/,f[[3,3,1]]

puts $/,f[[0,1,2,3]] puts$/,f[[0,0,1,2,3]]


# Ruby, 150 bytes

Anonymous function. Uses a recursive call to build a list of strings, one string per line, then joins them all together at the end.

->i{f=->l{s=2**l.size-1;g=[[?.*s]*s]*4;m=->x,y{x.zip(y).map{|a,b|a+?#+b}}
s<1?[?#]:(g[l.shift]=f[l];m[*g[0,2]]+[?#*(2*s+1)]+m[*g[2,2]])}
f[i].join"
"}


# Python 3.5, 1151 bytes:

Not much of a code golf, but oh well. Will try to prune it more over time where I can.

def x(s):
y=[''];l=['#'];k=[' ']
for z in s[::-1]:y.append(z)
y=y[::-1]
for h in range(len(y)):
if y[-1]!='':u=(int(y.pop())&3)
else:u=y.pop()
if len(l)<2:k.append(u);p=((2**(len(k)-1))-1);l.append((('.'*p+'#'+'.'*p+'\n')*p)+'#'*((p*2)+1)+'\n'+(('.'*p+'#'+'.'*p+'\n')*p))
else:
if len(l)>2:del l[0]
p=((2**(len(k)-1))-1);a=[[_+i for i in range(p)]for _ in range(len(l[1]))if _%((p*2)+2)==0 and _!=(((p*2)+2)*(p))];b=[[_+i for i in range(p)]for _ in range(len(l[1]))if _%(int(((p*2)+2)/2))==0 and _!=(int(((p*2)+2)/2)*((p)*2))and _ not in[g for i in a for g in i]];W=[g for i in a[:len(a)-(int(len(a)/2)):1]for g in i];B=[g for i in b[:len(b)-(int(len(b)/2)):1]for g in i];C=[g for i in a[len(a)-(int(len(a)/2)):len(a):1]for g in i];T=[g for i in b[len(b)-(int(len(b)/2)):len(b):1]for g in i];f=list(l[1])
for i in list(''.join(l[0].split())):
if u==0:f[W[0]]=i;del W[0]
elif u==1:f[B[0]]=i;del B[0]
elif u==2:f[C[0]]=i;del C[0]
elif u==3:f[T[0]]=i;del T[0]
del l[0];k.append(u);p=((2**(len(k)-1))-1);l.append(''.join(f));l.append((('.'*p+'#'+'.'*p+'\n')*p)+'#'*((p*2)+1)+'\n'+(('.'*p+'#'+'.'*p+'\n')*p))
print(l[-2])


A pretty naive way to do this, but, nonetheless, currently works perfectly and, as you can see, uses no external modules/libraries. Additionally, it can take on way more than 5 items in the provided list s without losing any accuracy (that is, if your hardware can handle it). It satisfies all the requirements, and I could not be happier with what I got. :)

It can also now not only accept any number within the range 0=>3 as any of the values, but any number, period, thanks to the & bitwise operator ! You can read more about them here. Now, for instance, [4,4,1,2,3] as the input list is the same as [0,0,1,2,3].

Note: Input must be provided as a list

# Ungolfed with explanation:

def x(s):
# Create 3 lists:
# y is for the values of s (the list provided) and an empty element for the
# first pattern
# l is reserved for the pattersn created through each item in list y
# k is created for the value of p which is the main value through which the
# pattern is created.
y=[''];l=['#'];k=[' ']
# Reverse s, and then add each element from s to y
# (in addition to the empty element)
for z in s[::-1]:
y.append(z)
# y should now equal the list created, but reversed
# If not reversed, then, if, for instance, the input is 0,1,2 and list y
# therefore contains '',2,1,0, the empty element will be called at the end,
# which is NOT what we want.
y=y[::-1]
# The main loop; will be iterated through the length of y number of times
for h in range(len(y)):
# Here is where each element from the end of y is recieved as u for
# use in the pattern in each iteration.
# As you can also see, a bitwise operator (&) is used here so that
# ALL numbers can be accepted. Not just those in the range 0-4.
# However, that will happen only if the value of y[-1] (the last elment in y) is
# NOT ''.
if y[-1]!='':
u=(int(y.pop())&3)
else:
u=y.pop()
# If the length of list l is less than 2
# (which means it only contains #), then do the following:
if len(l)<2:
# Append u to k
k.append(u)
# Use the length of k as n in the operation (2^(n-1)-1) to get the
# length of the dot filled part of the new pattern.
p=((2**(len(k)-1))-1)
# Add that pattern to the list (currently empty,
# i.e. containing no other pattern in any other quadrant)
l.append((('.'*p+'#'+'.'*p+'\n')*p)+'#'*((p*2)+1)+'\n'+(('.'*p+'#'+'.'*p+'\n')*p))
# Now, if the length of l is >=2, do the following:
else:
# If the length of l is >2, then delete the first element in list l
# (this will happen only once, when the # is still the first element)
if len(l)>2:
del l[0]
# Again, use the length of k as n in the operation (2^(n-1)-1)
# to get the length of the dot filled part of the pattern.
p=((2**(len(k)-1))-1)
# Create a list with all the index values of all the dot elements on the left hand
# side of the grid l[-1], and the index value + i where i is every integer in
# the range 0-p (this way, it will create lists within a list, each
# which contain p number of integers, which are all indexes of all the dots on
# the very left side of the grid)
a=[[_+i for i in range(p)]for _ in range(len(l[1]))if _%((p
*2)+2)==0 and _!=(((p*2)+2)*(p))]
# Create another list with all the index values of the dots using the same
# strategy as above, but this time, those in the right half of the grid.
b=[[_+i for i in range(p)]for _ in range(len(l[1]))if _%(int(((p*2)+2)/2))==0
and _!=(int(((p*2)+2)/2)*((p)*2))and _ not in[g for i in a for g in i]]
# Create 4 lists, each containing index values specific to each of the
# 4 quadrants of the grid.
# W is the list, based on A, containing all the indexes for the 1st quadrant of
# the grid in l[-1] containing dots (index 0 in the grid)
W=[g for i in a[:len(a)-(int(len(a)/2)):1]for g in i]
# B is the list, this time based on b, containing all indexes for the 2nd
# dot-filled quadrant of the grid l[-1] (index 1 in the grid)
B=[g for i in b[:len(b)-(int(len(b)/2)):1]for g in i]
# C is the list, also, like W, based on a, containg all the index values for
# the 3rd dot-filled quadrant of the grid in l[-1] (index 2 in the grid)
C=[g for i in a[len(a)-(int(len(a)/2)):len(a):1]for g in i]
# T is the final list, which, also like B, is based on b, and contains all the
# index values for the final (4th) dot-filled quadrant of the grid in l[-1]
T=[g for i in b[len(b)-(int(len(b)/2)):len(b):1]for g in i];f=list(l[1])
# Finally, in this for loop, utilize all the above lists to create the new
# pattern, using the last two elements in list l, where each character of grid
# l[-2] (the second to last element) is added to the correct index of grid l[-1]
# based on the value of u
for i in list(''.join(l[0].split())):
if u==0:
f[W[0]]=i
del W[0]
elif u==1:
f[B[0]]=i
del B[0]
elif u==2:
f[C[0]]=i
del C[0]
elif u==3:
f[T[0]]=i
del T[0]
# Delete the very first element of l, as it is now not needed anymore
del l[0]
# Append u to listk at the end of the loop this time
k.append(u)
# Update the value of p with the new value of length(k)
p=((2**(len(k)-1))-1)
# Append the new patter created from the for-loop above to list l
l.append(''.join(f))
# Append a new, empty pattern to list l for use in the next iteration
l.append((('.'*p+'#'+'.'*p+'\n')*p)+'#'*((p*2)+1)+'\n'+(('.'*p+'#'+'.'*p+'\n')*p))
# When the above main loop is all finished, print out the second-to-last elment in
# list l as the very last element is the new, empty grid created just in case
# there is another iteration
print(l[-2])


# Broader & far more visually appealing explanation:

For a broader and far more visually appealing explanation, consider the second time going through the "main"-loop in the above code, in which the input list is [0,2]. In this case, the elements in the "main" list l would be:

.#.
###
##.


and

...#...
...#...
...#...
#######
...#...
...#...
...#...


and list y would only contain 0. Taking advantage of Python's way of indexing the last element of grid l[-1], we can label the very left elements of the grid like so:

 0 ...#...\n 7
8 ...#...\n 15
16 ...#...\n 23
#######\n <- Ignore this as it is nothing but #s and a new line
32 ...#...\n 39
40 ...#...\n 47
48 ...#...\n 55


What pattern do you see? Every index on the very left of the grid is a multiple of 8, and since, using the equation 2^(n-1)-1 yields the length of each segment of dots in the grid, we can do ((2^(n-1)-1)*2)+2 to find the length of the top edge of the grid as a whole (+2 to include the middle #s and the \n at the end). We can use that equation, which we will call i to find the index values of each element on the left side of a grid of any size by creating a list, and appending to the list every integer, which we will call _, in the range 0=>length of grid l[-1], such that that item is a multiple of i, AND also such that _ does NOT equal i*(2^(n-1)-1), so that we can exclude the middle segment of #s separating the top half from the lower half. But we want ALL the dot elements from the left, and not only the elements on the very left side. Well, there is a fix to that, and that would be to simply append to the list a list containing i+h where h is every integer in the range 0=>2^(n-1) each time a value from range 0=>length of grid l[-1] is added to the list, so that each time, there will be as many number of values added to the list as the length of one quadrant of dots. And that is list a.

But now, how about the dots on the right half? Well, let us look at the indexing a different way:

   0 ...# 4  ...\n 7
8 ...# 12 ...\n 15
16 ...# 20 ...\n 23
#######\n <- Ignore this as it is nothing but #s and a new line
32 ...# 36 ...\n 39
40 ...# 44 ...\n 47
48 ...# 52 ...\n 55

^
|

These are the values we are looking at now


As you can see, the values now in the middle are those that we need, as they are the beginning of the index of every segment of dots on the right hand side of the grid. Now, what is the pattern here? Well, if it's not already obvious enough, now the middle values are all multiples of i/2! With that information, we can now create another list, b, to which the multiples of i/2 are added from the range 0=>length of grid l[-1] such that each integer from that range, which we will again call _, is NOT equal to (i/2)*(p*2) to exclude the line of #s separating the top and lower halves, AND such that _ is NOT already in list a, since we don't really need 8,16,32,etc. in list b. And now, again, we don't only want those specific indexes. We want ALL the dot characters on the right side of the grid. Well, just like we did in list a, here we can also add to list b lists of _+h where h is each integer in the range 0=>2^(n-1).

Now, we have both lists a and b packed and ready to go. How would we bring these together now? This is where lists W, T, G, and C come in. They will hold the indexes to each specific quadrant of dots in grid l[-1]. For instance, let us reserve list W as the list for all the indexes equal to quadrant 1 (index 0) of the grid. In this list, we would then add the first 2^(n-1) lists from list a, since list a contains all the indexes for dots in the left half of the grid, and then split them all up so that W now contains (2^(n-1))*(2^(n-1)) elements. We would do the same for list T, but with the difference that T would contain elements from list b, since Tis reserved for quadrant 2 (index 1). List G would be the same as list W, except it would contain the rest of the elements from list a, and list C is the same as list T, except it now contains the rest of the elements from list b. And, that's it! We now have out index values for every quadrant containing dots in the grid, all split up into four lists corresponding to each quadrant. We can now use these 4 lists (W,T,G,C) to tell the program which characters it should replace in grid l[-1] with each character from grid l[0], which is the very first element of list l. Since the value is 0 here, it would replace all the dots in the first quadrant (index 0) with grid l[0] utilizing list W.

Therefore, we finally have the following:

.#.#...
####...
##.#...
#######
...#...
...#...
...#...


Whew! Long process, isn't it? However, it works perfectly, and, again, I could not be happier. :)