import numpy as np
from random import random, randint

class AbstractFilter (object):
    def encode(self, data):
        raise NotImplementedError()
    def decode(self, data):
        raise NotImplementedError()
        
class KernelFilter (AbstractFilter):
    def kernel(self, data, decode=False):
        raise NotImplementedError()
    def encode(self, data):
        return self.kernel(data)
    def decode(self, data):
        return self.kernel(data, decode=True)

class NullFilter (KernelFilter):
    def kernel(self, data, decode=False):
        return data

class PaethFilter (KernelFilter):

    def kernel(self, data, decode=False):
        if decode:
            copy = data
        else:
            copy = np.copy(data)
        height, width, channels = data.shape
        for c in xrange(channels):
            for j in xrange(1, height):
                for i in xrange(1, width):
                    A = data[j,   i-1, c]
                    B = data[j-1, i,   c]
                    C = data[j-1, i-1, c]
                    p = A + B - C
                    if abs(A - p) <= abs(B - p) and abs(A - p) <= abs(C- p):
                        x = A
                    elif abs(B - p) <= abs(C - p):
                        x = B
                    else:
                        x = C
                    if decode:
                        copy[j, i, c] += x
                    else:
                        copy[j, i, c] -= x
        return copy

class SubFilter (KernelFilter):

    def kernel(self, data, decode=False):
        if decode:
            copy = data
        else:
            copy = np.copy(data)
        height, width, channels = data.shape
        for c in xrange(channels):
            for j in xrange(1, height):
                for i in xrange(1, width):
                    A = data[j, i-1, c]
                    if decode:
                        copy[j, i, c] += A
                    else:
                        copy[j, i, c] -= A
        return copy

class UniformGlitchFilter (NullFilter):
    
    def __init__(self, rate=0.005):
        self.rate = rate
    
    def encode(self, data):
        width, height, channels = data.shape
        for c in xrange(channels):
            for i in xrange(1, height):
                for j in xrange(1, width):
                    if random() < self.rate:
                        data[j, i, c] = randint(0, 255)
        return data
    

@@ -12,16 +12,46 @@
 y as np%0A
+from itertools import product%0A
 from ran
@@ -822,94 +822,62 @@
 or c
- in xrange(channels):%0A            for j in xrange(1, height):%0A                for i in
+, j, i in product(xrange(channels), xrange(1, height),
  xra
@@ -881,32 +881,33 @@
 xrange(1, width)
+)
 :%0A              
@@ -900,32 +900,24 @@
             
-       
- 
 A = data%5Bj, 
@@ -922,32 +922,24 @@
 ,   i-1, c%5D%0A
-        
             
@@ -972,24 +972,16 @@
         
-   
-     
 C = data
@@ -1006,24 +1006,16 @@
         
-       
- 
 p = A + 
@@ -1020,24 +1020,16 @@
 + B - C%0A
-        
         
@@ -1109,29 +1109,14 @@
     
-        x = A%0A       
+x = A%0A
     
@@ -1123,17 +1123,16 @@
         
- 
 elif abs
@@ -1166,32 +1166,24 @@
             
-        
 x = B%0A      
@@ -1184,32 +1184,24 @@
             
-   
-     
 else:%0A      
@@ -1214,30 +1214,14 @@
     
-       
- 
 x = C%0A
-        
     
@@ -1235,32 +1235,24 @@
  if decode:%0A
-        
             
@@ -1286,38 +1286,22 @@
         
-        else:%0A        
+else:%0A
         
@@ -1575,94 +1575,62 @@
 or c
- in xrange(channels):%0A            for j in xrange(1, height):%0A                for i in
+, j, i in product(xrange(channels), xrange(1, height),
  xra
@@ -1638,34 +1638,27 @@
 ge(1, width)
+)
 :%0A
-        
             
@@ -1685,32 +1685,24 @@
             
- 
-       
 if decode:%0A 
@@ -1712,32 +1712,24 @@
             
-   
-     
 copy%5Bj, i, c
@@ -1747,38 +1747,22 @@
         
-        else:%0A        
+else:%0A
         
@@ -1960,21 +1960,21 @@
     
-width, height
+height, width
 , ch
@@ -2010,58 +2010,51 @@
 or c
- in xrange(channels):%0A            for i in
+, j, i in product(xrange(channels),
  xrange(
 1, h
@@ -2053,52 +2053,24 @@
 nge(
-1, 
 height)
-:%0A                for j in
+,
  xrange(
 1, w
@@ -2069,27 +2069,17 @@
 nge(
-1, 
 width)
+)
 :%0A
-        
     
@@ -2111,24 +2111,16 @@
 f.rate:%0A
-        
         

refactor nested loops to use itertools.product