from scipy import misc


def inc_rng(a, aV, aD, min_r, A, AV, AD, r, w, p_):

    if r > min_r:  # A, AV, AD inc.to adjust for redundancy to patterns formed by prior comp:
        A += a     # a: min m for inclusion into positive vP
        AV += aV   # aV: min V for initial comp() recursion, AV: min V for higher recursions

    if r > min_r-1:  # default range is shorter for d_[w]: ds are redundant to and smaller than ps
        AD += aD     # aV: min |D| for initial comp() recursion over d_[w], AD: min |D| for higher recursions

    W = w  # to differentiate from new w
    ip_ = p_  # to differentiate from new p_; local nP is initialized:

    n, P_ = (0, dict())  # r was incremented in higher-scope p_
    pri_s, w, I, D, V, rv, p_ = (0, 0, 0, 0, 0, 0, dict())  # tuple vP=0
    pri_sd, wd, Id, Dd, Vd, rd, d_ = (0, 0, 0, 0, 0, 0, dict())  # tuple dP=0

    for x in range(r+2, W):

        p, fd, fv = ip_[x]  # keys = w, always start from 1, comp to r+2 pixels back
        pp, pfd, pfv = ip_[x-r]  # previously compared p(ignored) and its fd and fv to next p
        fv += pfv  # fv is summed over extended-comp range
        fd += pfd  # fd is summed over extended-comp range

        pri_p, pri_fd, pri_fv = ip_[x-r-1]  # for comp(p, pri_p), pri_fd and pri_fv are ignored

        pri_s, w, I, D, V, rv, p_, pri_sd, wd, Id, Dd, Vd, rd, d_, n, P_ = \
        comp(p, pri_p, fd, fv, W, x,
             pri_s, w, I, D, V, rv, p_,
             pri_sd, wd, Id, Dd, Vd, rd, d_,
             a, aV, aD, min_r, A, AV, AD, r, n, P_)

    return P_  # local vPs and dPs to replace p_, A, AV, AD accumulated per comp recursion but not for next pixel


def inc_der(a, aV, aD, min_r, A, AV, AD, r, wd, d_):

    if r > min_r:
        A += a; AV += aV
    if r > min_r-1:
        AD += aD

    W = wd  # to differentiate from new w
    ip_ = d_  # to differentiate from new d_; local nP is initialized:

    fd, fv, r, n, P_ = (0, 0, 0, 0, dict())  # r is initialized for each d_
    pri_s, w, I, D, V, rv, p_ = (0, 0, 0, 0, 0, 0, dict())  # tuple nP=0,
    pri_sd, wd, Id, Dd, Vd, rd, d_ = (0, 0, 0, 0, 0, 0, dict())  # tuple nP=0

    for x in range(1, W):  # keys = wd, always start from 1

        p = ip_[x]
        if x > 1:

            pri_s, w, I, D, V, rv, p_, pri_sd, wd, Id, Dd, Vd, rd, d_, n, P_ = \
            comp(p, pri_p, fd, fv, W, x,
                 pri_s, w, I, D, V, rv, p_,
                 pri_sd, wd, Id, Dd, Vd, rd, d_,
                 a, aV, aD, min_r, A, AV, AD, r, n, P_)

        pri_p = p

    return P_  # local vPs and dPs to replace d_


def comp(p, pri_p, fd, fv, W, x,  # x is from higher-scope for loop w
         pri_s, w, I, D, V, rv, p_,
         pri_sd, wd, Id, Dd, Vd, rd, d_,
         a, aV, aD, min_r, A, AV, AD, r, n, P_):

    d = p - pri_p      # difference between consecutive pixels
    m = min(p, pri_p)  # match between consecutive pixels
    v = m - A          # relative match (predictive value) between consecutive pixels

    fd += d  # fd includes all shorter- and current- range ds between comparands
    fv += v  # fv includes all shorter- and current- range vs between comparands

    # formation of value pattern vP: span of pixels forming same-sign v s:

    s = 1 if v > 0 else 0  # s: positive sign of v
    if x > r+1 and (s != pri_s or x == W-1):  # (if derived pri_s) vP is terminated

        n += 1  # n: number of dPs + vPs formed within line, or recursively within higher p_ or d_
        if w > r+3 and pri_s == 1 and V > AV:  # w includes P_? ps in p_ are cross-compared over extended distance:

            r += 1  # r: incremental range of comp
            rv = 1  # rv: incremental range flag:
            w += 1  # concat?
            p_[w] = inc_rng(a, aV, aD, min_r, A, AV, AD, r, w, p_)

        P_[n] = 0, pri_s, w, I, D, V, rv, p_  # output of vP
        w, I, D, V, rv, p_ = (0, 0, 0, 0, 0, dict())  # initialization of new vP

    pri_s = s   # prior s updated, v pattern is incremented:
    w += 1      # span of vP: number of pixels forming same-sign v
    I += pri_p  # ps summed within w
    D += fd     # fds summed within w into fuzzy D
    V += fv     # fvs summed within w into fuzzy V
    p_[w] = pri_p, fd, fv  # buffered within w for selective extended comp

    # formation of difference pattern dP: span of pixels forming same-sign d s:

    sd = 1 if d > 0 else 0  # sd: positive sign of d;
    if x > r+2 and (sd != pri_sd or x == W-1):  # (if derived pri_sd) dP is terminated

        n += 1
        if wd > 3 and abs(Dd) > AD:  # wd includes P_? ds in d_ are cross-compared:

            rd = 1  # rd: incremental derivation flag:
            wd += 1  # concat?
            d_[wd] = inc_der(a, aV, aD, min_r, A, AV, AD, r, wd, d_)

        P_[n] = 1, pri_sd, wd, Id, Dd, Vd, rd, d_  # output of dP
        wd, Id, Dd, Vd, rd, d_ = (0, 0, 0, 0, 0, dict())  # initialization of new dP

    pri_sd = sd  # prior sd updated, d pattern is incremented:
    wd += 1      # span of dP: number of pixels forming same-sign d
    Id += pri_p  # ps summed within wd
    Dd += fd     # fds summed within wd
    Vd += fv     # fvs summed within wd
    d_[wd] = fd  # prior fds are buffered in d_, same-sign as distinct from in p_

    return pri_s, w, I, D, V, rv, p_, pri_sd, wd, Id, Dd, Vd, rd, d_, n, P_
    # for next p comparison, vP and dP increment, and output


def frame(Fp_):  # last '_' distinguishes array name from element name:

    FP_ = dict()  # output frame of patterns, P_: line of patterns (vP or dP)
    H, W = Fp_.shape  # H: frame height, W: frame width

    a = 127  # minimal filter for vP inclusion
    aV = 63  # minimal filter for incremental-range comp
    aD = 63  # minimal filter for incremental-derivation comp
    min_r=0  # default range of fuzzy comparison

    for y in range(H):

        ip_ = Fp_[y, :]  # y is index of new line ip_; or p_ = dict(zip(range(len(p_)), p_.values())))?

        if min_r == 0: A = a; AV = aV  # actual filters, incremented per comp recursion
        else: A = 0; AV = 0  # if r > min_r

        if min_r <= 1: AD = aD
        else: AD = 0

        fd, fv, r, x, n, P_ = (0, 0, 0, 0, 0, dict())  # nP tuple = (n, P_, r, A, AV, AD)
        pri_s, w, I, D, V, rv, p_ = (0, 0, 0, 0, 0, 0, dict())  # vP tuple = (pri_s, w, I, D, V, rv, p_)
        pri_sd, wd, Id, Dd, Vd, rd, d_ = (0, 0, 0, 0, 0, 0, dict())  # dP tuple = (pri_sd, wd, Id, Dd, Vd, rd, d_)

        for x in range(W):  # cross-compares consecutive pixels, forming a sequence of d, m, v:

            p = ip_[x]  # new pixel, comp to prior pixel:
            if x > 0:

                pri_s, w, I, D, V, rv, p_, pri_sd, wd, Id, Dd, Vd, rd, d_, n, P_ = \
                comp(p, pri_p, fd, fv, W, x,
                     pri_s, w, I, D, V, rv, p_,
                     pri_sd, wd, Id, Dd, Vd, rd, d_,
                     a, aV, aD, min_r, A, AV, AD, r, n, P_)

            pri_p = p  # prior pixel, pri_ values are always derived before use

        FP_[y] = n, P_  # y = len(FP_), or FP_ = P_, line of patterns is added to frame of patterns

    return FP_  # or return FP_  # frame of patterns (vP or dP): output to level 2;

f = misc.face(gray=True)  # input frame of pixels
f = f.astype(int)
frame(f)