# History

This kata is a sequel of my [Mixbonacci](https://www.codewars.com/kata/mixbonacci/python) kata. Zozonacci is a special integer sequence named after [**ZozoFouchtra**](https://www.codewars.com/users/ZozoFouchtra), who came up with this kata idea in the [Mixbonacci discussion](https://www.codewars.com/kata/mixbonacci/discuss/python). 

This sequence combines the rules for computing the n-th elements of fibonacci, jacobstal, pell, padovan, tribonacci and tetranacci sequences according to a given pattern.

# Task

Compute the first `n` elements of the Zozonacci sequence for a given pattern `p`.

## Rules

1. `n` is given as integer and `p` is given as a list of as abbreviations as strings (e.g. `["fib", "jac", "pad"]`)
2. When `n` is 0 or `p` is empty return an empty list.
3. The first four elements of the sequence are determined by the first abbreviation in the pattern (see the table below).
4. Compute the fifth element using the formula corespoding to the first element of the pattern, the sixth element using the formula for the second element and so on. (see the table below and the examples)
5. If `n` is more than the length of `p` repeat the pattern.


```
+------------+--------------+------------------------------------------+---------------------+
|  sequence  | abbreviation |         formula for n-th element         | first four elements |
+------------|--------------+------------------------------------------|---------------------|
| fibonacci  |     fib      | a[n] = a[n-1] + a[n-2]                   |     0, 0, 0, 1      |
| jacobsthal |     jac      | a[n] = a[n-1] + 2 * a[n-2]               |     0, 0, 0, 1      |
| padovan    |     pad      | a[n] = a[n-2] + a[n-3]                   |     0, 1, 0, 0      |
| pell       |     pel      | a[n] = 2 * a[n-1] + a[n-2]               |     0, 0, 0, 1      |
| tetranacci |     tet      | a[n] = a[n-1] + a[n-2] + a[n-3] + a[n-4] |     0, 0, 0, 1      |
| tribonacci |     tri      | a[n] = a[n-1] + a[n-2] + a[n-3]          |     0, 0, 0, 1      |
+------------+--------------+------------------------------------------+---------------------+
```

## Example

```
zozonacci(["fib", "tri"], 7) == [0, 0, 0, 1, 1, 2, 3]

Explanation: 

          b     d
       /-----\/----\
[0, 0, 0, 1, 1, 2, 3]
 \--------/  
     | \--------/
     a     c

a - [0, 0, 0, 1] as "fib" is the first abbreviation
b - 5th element is 1 as the 1st element of the pattern is "fib": 1 = 0 + 1
c - 6th element is 2 as the 2nd element of the pattern is "tri": 2 = 0 + 1 + 1
d - 7th element is 3 as the 3rd element of the pattern is "fib" (see rule no. 5): 3 = 2 + 1
```

## Sequences

* [fibonacci](https://oeis.org/A000045) : 0, 1, 1, 2, 3 ...
* [padovan](https://oeis.org/A000931): 1, 0, 0, 1, 0 ...
* [jacobsthal](https://oeis.org/A001045): 0, 1, 1, 3, 5 ...
* [pell](https://oeis.org/A000129): 0, 1, 2, 5, 12 ...
* [tribonacci](https://oeis.org/A000073): 0, 0, 1, 1, 2 ...
* [tetranacci](https://oeis.org/A000078): 0, 0, 0, 1, 1 ...

Difficulty: introductory

Starter code:

def zozonacci(pattern, length):
	

Solutions:
from itertools import cycle

ROOT = {'fib': [0, 0, 0, 1],
        'jac': [0, 0, 0, 1],
        'pad': [0, 1, 0, 0],
        'pel': [0, 0, 0, 1],
        'tet': [0, 0, 0, 1],
        'tri': [0, 0, 0, 1]}
GEN  = {'fib': lambda a: a[-1] + a[-2],
        'jac': lambda a: a[-1] + 2 * a[-2],
        'pad': lambda a: a[-2] + a[-3],
        'pel': lambda a: 2 * a[-1] + a[-2],
        'tet': lambda a: a[-1] + a[-2] + a[-3] + a[-4],
        'tri': lambda a: a[-1] + a[-2] + a[-3]}


def zozonacci(pattern, n):
    if not pattern or not n: return []
    
    lst  = ROOT[pattern[0]][:]
    cycl = cycle(map(GEN.get, pattern))
    
    for f,_ in zip(cycl,range(n-4)): lst.append(f(lst))
    
    return lst[:n]

from collections import deque

start = {
    'fib': [0, 0, 0, 1],
    'jac': [0, 0, 0, 1],
    'pad': [0, 1, 0, 0],
    'pel': [0, 0, 0, 1],
    'tet': [0, 0, 0, 1],
    'tri': [0, 0, 0, 1],
}

sum_idx = {
    'fib': [-1, -2],
    'jac': [-1, -2, -2],
    'pad': [-2, -3],
    'pel': [-1, -1, -2],
    'tet': [-1, -2, -3, -4],
    'tri': [-1, -2, -3],
}

def sequence(pattern):
    pattern, seq = deque(pattern), deque(start[pattern[0]])
    while 1:
        seq.append(sum(seq[idx] for idx in sum_idx[pattern[0]]))
        yield seq.popleft()
        pattern.rotate(-1)

def zozonacci(pattern, length):
    return pattern and [n for _, n in zip(range(length), sequence(pattern))]

from itertools import cycle

def nacci(coef):
    return lambda seq: sum(c * a for c, a in zip(coef, seq[-4:]))
        
def zozonacci(pattern, length):
    if not pattern:
        return []
    d = {'fib' : nacci([0, 0, 1, 1]),
         'pad' : nacci([0, 1, 1, 0]),
         'jac' : nacci([0, 0, 2, 1]),
         'pel' : nacci([0, 0, 1, 2]),
         'tri' : nacci([0, 1, 1, 1]),
         'tet' : nacci([1, 1, 1, 1])
        }
    c = cycle(map(d.get, pattern))
    res = ([0, 1, 0, 0] if pattern[0] == 'pad' else [0, 0, 0, 1])[:length]
    for _ in range(length - 4):
        res.append(next(c)(res))
    return res

from functools import reduce

config = {
    'fib':  ([0, 0, 0, 1], lambda a: a[3] + a[2]),
    'jac':  ([0, 0, 0, 1], lambda a: a[3] + 2 * a[2]),
    'pad':  ([0, 1, 0, 0], lambda a: a[2] + a[1]),
    'pel':  ([0, 0, 0, 1], lambda a: 2 * a[3] + a[2]),
    'tet':  ([0, 0, 0, 1], lambda a: a[3] + a[2] + a[1] + a[0]),
    'tri':  ([0, 0, 0, 1], lambda a: a[3] + a[2] + a[1])
}

def zozonacci(p, l):
    if not p or not l: return []
    return reduce(lambda r, i: r + [config[p[i % len(p)]][1](r[-4:])], range(0, l-4), config[p[0]][0][:])[0:l]

def wrapper_fun(func, lst): return func(lst)
def fib(lst): return lst[-1] + lst[-2]
def tri(lst): return lst[-1] + lst[-2] + lst[-3]
def tet(lst): return lst[-1] + lst[-2] + lst[-3] + lst[-4]
def pad(lst): return lst[-2] + lst[-3]
def pel(lst): return lst[-1] * 2 + lst[-2]
def jac(lst): return lst[-1] + lst[-2] * 2
def zozonacci(pattern, length):
    if not pattern or not length: return []
    if length < 5:
        if pattern[0] == 'pad': return [0, 1, 0, 0][:length]
        else: return [0, 0, 0, 1][:length]
    cp_lst = zozonacci(pattern, length - 1)
    return cp_lst + [wrapper_fun(eval(pattern[(length - 5) % len(pattern)]), cp_lst)]

def zozonacci(pattern, length):
    n = len(pattern)
    if n == 0 or length == 0:
        return []
    GEN = {
        "fib": lambda x: x[-1] + x[-2],
        "jac": lambda x: x[-1] + 2 * x[-2],
        "pad": lambda x: x[-2] + x[-3],
        "pel": lambda x: 2 * x[-1] + x[-2],
        "tet": lambda x: x[-1] + x[-2] + x[-3] + x[-4],
        "tri": lambda x: x[-1] + x[-2] + x[-3],
    }
    seq = [0, 1, 0, 0] if pattern[0] == "pad" else [0, 0, 0, 1]
    for i in range(length - 4):
        seq += [GEN.get(pattern[i % n])(seq)]
    return seq[:length]

SEQUENCES = {
    'fib': ((lambda s, n: s[n - 1] + s[n - 2]), (0, 0, 0, 1)),
    'jac': ((lambda s, n: s[n - 1] + 2 * s[n - 2]), (0, 0, 0, 1)),
    'pad': ((lambda s, n: s[n - 2] + s[n - 3]), (0, 1, 0, 0)),
    'pel': ((lambda s, n: 2 * s[n - 1] + s[n - 2]), (0, 0, 0, 1)),
    'tri': ((lambda s, n: s[n - 1] + s[n - 2] + s[n - 3]), (0, 0, 0, 1)),
    'tet': ((lambda s, n: s[n - 1] + s[n - 2] + s[n - 3] + s[n - 4]), (0, 0, 0, 1)),
}


def zozonacci(pattern, length):
    if not pattern:
        return []

    seq = list(SEQUENCES[pattern[0]][1])
    i = 0
    while len(seq) < length:
        fn, _ = SEQUENCES[pattern[i]]
        seq.append(fn(seq, len(seq)))
        i = (i + 1) % len(pattern)
    return seq[:length]

def zozonacci(pattern, length):
    if (pattern == [] or length == 0):
        return []
            
    pattern_dict = {"fib": fibonacci,
                     "jac": jacobsthal,
                     "pad": padovan,
                     "pel": pell,
                     "tet": tetranacci,
                     "tri": tribonacci}
    initial_pattern = pattern[0]
    
    if (initial_pattern in ["fib", "jac", "pel", "tet", "tri"]):
        resultList = [0, 0, 0, 1]
    else:
        resultList = [0, 1, 0, 0]
    if (length < 4):
        return resultList[:length]
    
    for x in range(length - 4):
        rule = pattern[x % len(pattern)]
        pattern_dict[rule](resultList)
    return resultList

def fibonacci(numbers):
    n = len(numbers)
    numbers.append(numbers[n-1] + numbers[n-2])
    
def jacobsthal(numbers):
    n = len(numbers)
    numbers.append(numbers[n-1] + 2 * numbers[n-2])
    
def padovan(numbers):
    n = len(numbers)
    numbers.append(numbers[n-2] + numbers[n-3])
    
def pell(numbers):
    n = len(numbers)
    numbers.append(numbers[n-1] * 2 + numbers[n-2])
    
def tetranacci(numbers):
    n = len(numbers)
    numbers.append(numbers[n-1] + numbers[n-2] + numbers[n-3] + numbers[n-4])
    
def tribonacci(numbers):
    n = len(numbers)
    numbers.append(numbers[n-1] + numbers[n-2] + numbers[n-3])



def zozonacci(pattern, length):  
    if length == 0:
        return []
    if pattern == []:
        return []
    
    result = [0,0,0,1] if pattern[0] != "pad" else [0,1,0,0]
    result2 = []
    if length < 4:
        for i in  range(length):
            result2.append(result[i])
        return result2
    j = 0
    for i in range(4, length):
        seq = pattern[j]
        if seq == "fib":
            result.append(result[i-1] + result[i-2])
        elif seq == "jac":
            result.append(result[i-1] + 2 * result[i-2])
        elif seq == "pad":
            result.append(result[i-2] + result[i-3])
        elif seq == "pel":
            result.append(2 * result[i-1] + result[i-2])
        elif seq == "tet":
            result.append(result[i-1] + result[i-2] + result[i-3] + result[i-4])
        else:
            result.append(result[i-1] + result[i-2] + result[i-3])
        j += 1
        if j == len(pattern):
            j = 0
    return result
            
            
            


def zozonacci(pat,n):
    if not pat or not  n: return []
    d = {'fib': lambda x: sum(x[-2:]),
         'jac': lambda x: x[-1]+2*x[-2],
         'pad': lambda x: x[-2]+x[-3],
         'pel': lambda x: 2*x[-1]+x[-2],
         'tet': lambda x: sum(x[-4:]),
         'tri': lambda x: sum(x[-3:])
        }
    if pat[0] == 'pad': a = [0,1,0,0]
    else: a = [0,0,0,1]
    for i in range(n-4):
        a.append(d.get(pat[i%len(pat)])(a))
    return a[:n]

from itertools import cycle
def zozonacci(seq, n):
    if not n or not seq:return []
    common = {1: [0, 1, 0, 0], 0: [0, 0, 0, 1]}
    ini = common[seq[0] == "pad"]
    if n<=4:return ini[:n]
    fib = lambda:sum(ini[-2:])
    jac = lambda:ini[-1] + 2 * ini[-2]
    pad = lambda:sum(ini[-3:-1])
    pel = lambda:2 * ini[-1] + ini[-2]
    tet = lambda:sum(ini[-4:])
    tri = lambda:sum(ini[-3:])
    for i in cycle(seq):
        ini.append(locals()[i]())
        if len(ini) == n : return ini

from itertools import cycle

FIRST4 = {'fib': [0,0,0,1], 'jac': [0,0,0,1], 'pad': [0,1,0,0], 'pel': [0,0,0,1], 'tet': [0,0,0,1], 'tri': [0,0,0,1]}
FUNCS = {
    'fib': lambda a: a[-1] + a[-2],
    'jac': lambda a: a[-1] + 2 * a[-2],
    'pad': lambda a: a[-2] + a[-3],
    'pel': lambda a: 2 * a[-1] + a[-2],
    'tet': lambda a: a[-1] + a[-2] + a[-3] + a[-4],
    'tri': lambda a: a[-1] + a[-2] + a[-3],
}

def zozonacci(p, n):
    if n == 0 or not p:
        return []
    a = FIRST4[p[0]][:n]
    fs = cycle(map(FUNCS.get, p))
    for i in range(n-4):
        a.append(next(fs)(a))
    return a

acci = {
    "fib":([0,0,0,1],[-1,-2]),
    "jac":([0,0,0,1],[-1,-2,-2]),
    "pad":([0,1,0,0],[-2,-3]),
    "pel":([0,0,0,1],[-1,-1,-2]),
    "tet":([0,0,0,1],[-1,-2,-3,-4]),
    "tri":([0,0,0,1],[-1,-2,-3])
}

def zozonacci(p, l):
    if not p: return []
    h,i = acci[p[0]][0][:],0
    while len(h)<l:
        h.append(sum(h[j] for j in acci[p[i]][1]))
        i=(i+1)%len(p)
    return h[:l]

from itertools import cycle, islice

def zozonacci(pattern, length):
    values = [0, 1, 0, 0] if "pad" in pattern[:1] else [0, 0, 0, 1]
    weights = {"fib": [0, 0, 1, 1], "pad": [0, 1, 1, 0], "jac": [0, 0, 2, 1], "pel": [0, 0, 1, 2], 
        "tri": [0, 1, 1, 1], "tet": [1, 1, 1, 1]}
    for p in islice(cycle(pattern), max(length - 4, 0)):
        values.append(sum(w*v for w, v in zip(weights[p], values[-4:])))
    return values[:(len(pattern) and length)]

from itertools import cycle
seq={'fib':[0,0,0,1],'pad':[0,1,0,0],'jac':[0,0,0,1],'pel':[0,0,0,1],'tri':[0,0,0,1],'tet':[0,0,0,1]}
pat={'fib':'a[-1]+a[-2]','pad':'a[-2]+a[-3]','jac':'a[-1]+2*a[-2]','pel':'2*a[-1]+a[-2]',
     'tri':'a[-1]+a[-2]+a[-3]','tet':'a[-1]+a[-2]+a[-3]+a[-4]'}
def zozonacci(pattern, length):
    if not pattern: return []
    a=seq[pattern[0]][:length]
    c=cycle(pattern)
    for i in range(length-4):
        a.append(eval(pat[next(c)]))
    return a

def zozonacci(pattern, n):
    bind = {'fib': [[0, 0, 0, 1], lambda arr: sum(arr[-2:])],
            'jac': [[0, 0, 0, 1], lambda arr: sum([2 * arr[-2], arr[-1]])],
            'pad': [[0, 1, 0, 0], lambda arr: sum([arr[-3], arr[-2]])],
            'pel': [[0, 0, 0, 1], lambda arr: sum([arr[-2], 2 * arr[-1]])],
            'tet': [[0, 0, 0, 1], lambda arr: sum(arr[-4:])],
            'tri': [[0, 0, 0, 1], lambda arr: sum(arr[-3:])]}
    if not pattern or n == 0:
        return []
    res = bind[pattern[0]][0]
    q, r = divmod(n-len(res), len(pattern))
    for v in (q * pattern + pattern[:r]):
        res.append(bind[v][1](res))
    return res[:n]


def zozonacci(pattern, length):
    if length == 0 or pattern == []:
        return []
    result = {
        "fib": [0, 0, 0, 1],
        "jac": [0, 0, 0, 1],
        "pad": [0, 1, 0, 0],
        "pel": [0, 0, 0, 1],
        "tet": [0, 0, 0, 1],
        "tri": [0, 0, 0, 1]
    }[pattern[0]]
    if length < 4:
        result = result[:length]
        return result
    i = 0
    j = 4
    while len(result) < length:
        r = {
            "fib": result[j - 1] + result[j - 2],
            "jac": result[j - 1] + 2 * result[j - 2],
            "pad": result[j - 2] + result[j - 3],
            "pel": 2 * result[j - 1] + result[j - 2],
            "tet": result[j - 1] + result[j - 2] + result[j - 3] + result[j - 4],
            "tri": result[j - 1] + result[j - 2] + result[j - 3]
        }[pattern[i]]
        result.append(r)
        i = (i + 1) % len(pattern)
        j += 1
    return result

from itertools import cycle
def zozonacci(pattern, length):
    if not pattern or not length:return []
    
    fib=lambda lst:sum(lst[-2:])
    jac=lambda lst:lst[-1]+2*lst[-2]
    pad=lambda lst:lst[-2]+lst[-3]
    pel=lambda lst:2*lst[-1]+lst[-2]
    tet=lambda lst:sum(lst[-4:])
    tri=lambda lst:sum(lst[-3:])
    
    FIRST={"fib":[0,0,0,1],"jac":[0,0,0,1],"pad":[0,1,0,0],"pel":[0,0,0,1],"tri":[0,0,0,1],"tet":[0,0,0,1]}
    NEXT={"fib":fib,"jac":jac,"pad":pad,"pel":pel,"tet":tet,"tri":tri}
    
    seq=FIRST[pattern[0]]
    pattern=cycle(pattern)
    
    for i in range(length-4):
        seq.append(NEXT[next(pattern)](seq))
    
    return seq[:length]

def fibonacci(array,
              index):

    return array[index - 1] + array[index - 2]


def jacobsthal(array,
               index):

    return array[index - 1] + 2 * array[index - 2]


def padovan(array,
            index):

    return array[index - 2] + array[index - 3]


def pell(array,
         index):

    return 2 * array[index - 1] + array[index - 2]


def tetranacci(array,
               index):

    return array[index - 1] + array[index - 2] + array[index - 3] + array[index - 4]


def tribonacci(array,
               index):

    return array[index - 1] + array[index - 2] + array[index - 3]  


MAP = {"fib": fibonacci,
       "jac": jacobsthal,
       "pad": padovan,
       "pel": pell,
       "tet": tetranacci,
       "tri": tribonacci}


def initialize(pattern):

    if (pattern[0] == "pad"):

        return [0,
                1,
                0,
                0]
    else:

        return [0,
                0,
                0,
                1]


def zozonacci(pattern,
              length):

    result = []
    if (not (len(pattern) and
             length)):

        return result

    result = initialize(pattern)
    if (length <= 4):

        return result[:length]

    index = 4
    while (len(result) < length):
        for sequence in pattern:
            current = MAP[sequence](result,
                                    index)
            result.append(current)
            index += 1
            if (len(result) == length):
                break

    return result


from itertools import cycle

def zozonacci(p,n):
    
    if not p or not n: return []
    
    formula={"fib":lambda : a[-1]+a[-2],
         "jac":lambda : a[-1]+2*a[-2],
         "pad":lambda : a[-2]+a[-3],
         "pel":lambda : 2*a[-1]+a[-2],
         "tet":lambda : sum(a[-i] for i in range(1,5)),
         "tri":lambda : sum(a[-i] for i in range(1,4))
        }
 
    a=[0,1,0,0] if p[0]=="pad" else [0,0,0,1]
    
    if n<=4: return a[:n]
    
    i=0
    p=cycle(p)
    
    while i<n-4:    
        a.append(formula[next(p)]())
        i+=1
        
    return a