# Path: crysp/poly.py
# class Poly(SubPoly):
# 
#   def __getitem__(self,i):
#       if isinstance(i,int):
#           return Poly(self.ival[i],self.size)
#       elif isinstance(i,slice):
#           return Poly(self.span(i),self.size)
#       else:
#           return Poly([self.ival[j] for j in i],self.size)
# 
# Path: crysp/salsa20.py
# class Salsa20(object):
#     def __init__(self,K=None,rounds=20):
#     def hash(self,m):
#     def keystream(self,v):
#     def enc(self,v,m):
#     def dec(self,v,c):
#     def quarterround(self,y):
#     def rowround(self,y):
#     def columnround(self,x):
#     def doubleround(self,x):
#     def core(self,X,dround=10):
#         L = Bits(m,bitorder=1).split(32)
#         X = Poly([x.int() for x in L],size=32)
#         C = []
#         Z = X
#             Z = self.doubleround(Z)


from crysp.bits import *
from crysp.poly import Poly
from crysp.utils.operators import *
from crysp import salsa20

    def __init__(self,K=None,rounds=8):
        super().__init__(K,rounds)
        # modified init from Salsa20 to Chacha:
        if K is not None:
            consts = self.p[0,5,10,15]
            self.p = Poly(0,size=32,dim=16)
            self.p[0:4] = consts
            self.p[4:8] = self.K[0].split(32)
            self.p[8:12] = self.K[1].split(32)

    def keystream(self,v):
        assert self.K is not None
        assert isinstance(v,Bits) and v.size==64
        self.p[14:16] = v.split(32)
        maxlen = 1<<64
        i = 0
        while i<maxlen:
            self.p[12:14] = (i&0xffffffff,i>>32)
            yield self.core(self.p,dround=self.dround)
            i += 1

    def quarterround(self,y):
        a,b,c,d = y[0],y[1],y[2],y[3]
        a += b; d ^= a; d = rol(d,16)
        c += d; b ^= c; b = rol(b,12)
        a += b; d ^= a; d = rol(d, 8)
        c += d; b ^= c; b = rol(b, 7)
        return concat([a,b,c,d])


# Path: crysp/poly.py
# class Poly(SubPoly):
# 
#   def __getitem__(self,i):
#       if isinstance(i,int):
#           return Poly(self.ival[i],self.size)
#       elif isinstance(i,slice):
#           return Poly(self.span(i),self.size)
#       else:
#           return Poly([self.ival[j] for j in i],self.size)
# 
# Path: crysp/salsa20.py
# class Salsa20(object):
#     def __init__(self,K=None,rounds=20):
#     def hash(self,m):
#     def keystream(self,v):
#     def enc(self,v,m):
#     def dec(self,v,c):
#     def quarterround(self,y):
#     def rowround(self,y):
#     def columnround(self,x):
#     def doubleround(self,x):
#     def core(self,X,dround=10):
#         L = Bits(m,bitorder=1).split(32)
#         X = Poly([x.int() for x in L],size=32)
#         C = []
#         Z = X
#             Z = self.doubleround(Z)


# Path: crysp/chacha.py
from crysp.bits import *
from crysp.poly import Poly
from crysp.utils.operators import *
from crysp import salsa20


    def __init__(self,K=None,rounds=8):
        super().__init__(K,rounds)
        # modified init from Salsa20 to Chacha:
        if K is not None:
            consts = self.p[0,5,10,15]
            self.p = Poly(0,size=32,dim=16)
            self.p[0:4] = consts
            self.p[4:8] = self.K[0].split(32)
            self.p[8:12] = self.K[1].split(32)

    def keystream(self,v):
        assert self.K is not None
        assert isinstance(v,Bits) and v.size==64
        self.p[14:16] = v.split(32)
        maxlen = 1<<64
        i = 0
        while i<maxlen:
            self.p[12:14] = (i&0xffffffff,i>>32)
            yield self.core(self.p,dround=self.dround)
            i += 1

    def quarterround(self,y):
        a,b,c,d = y[0],y[1],y[2],y[3]
        a += b; d ^= a; d = rol(d,16)
        c += d; b ^= c; b = rol(b,12)
        a += b; d ^= a; d = rol(d, 8)
        c += d; b ^= c; b = rol(b, 7)
        return concat([a,b,c,d])


    def rowround(self,y):