# Path: crysp/utils/operators.py
# def rol(x,n):
#     return (x<<n | x>>(x.size-n))
# 
# Path: crysp/padding.py
# class MDpadding(blockiterator):
#     def __init__(self,l,wsize):
#         self.wsize = wsize
#         super(MDpadding,self).__init__(l)
#     # add sha1 padding:
#     def lastblock(self,m,**kargs):
#         bitlen = kargs.get('bitlen',None)
#         if bitlen is None:
#             bitlen = self.bitcnt+len(m)*8
#         assert self.bitcnt<=bitlen
#         needed = bitlen-self.bitcnt
#         mb = Bits(m,size=needed)
#         countersize = self.wsize*2
#         N = self.blocksize-1-countersize-needed
#         if N<0: N += self.blocksize
#         pad = mb//Bits(1,1)//Bits(0,N)
#         self.padflag = True
#         self.bitcnt += needed
#         return pad.bytes()+pack(Bits(bitlen,countersize))
#     # remove sha1 padding:
#     def remove(self,c):
#         clen = self.wsize//4
#         counter,_ = unpack(c[-clen:])
#         c = list(c[:-clen])
#         while Bits(c[-1]).ival==0:
#             c.pop()
#         if len(c)==0: raise PaddingError("failed to remove padding")
#         b = Bits(c.pop())
#         b.size=str(b).rfind('1')
#         return b''.join(c)+b.bytes()
# 
# 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/padding.py
# class Nullpadding(blockiterator):
#     def lastblock(self,m,**kargs):
#         bitlen = kargs.get('bitlen',None)
#         if bitlen is None:
#             bitlen = len(m)*8
#         else:
#             bitlen = bitlen-self.bitcnt
#         q = self.blocksize-bitlen
#         b = (Bits(m,bitlen)//Bits(0,q)).bytes()
#         assert len(b)==self.blocklen
#         self.bitcnt += bitlen
#         self.padflag = True
#         self.padcnt = q
#         return b
#     # remove padding:
#     def remove(self,m):
#         b=Bits(m[-self.blocklen:])
#         b.size -= self.padcnt
#         return m[:-self.blocklen]+b.bytes()
# 
# Path: crysp/utils/operators.py
# def concat(L,bigend=False):
#     if len(L)==1: return L[0]
#     if bigend: L = reversed(L)
#     return reduce(lambda x,y: x//y, L)


from crysp.bits import *
from crysp.utils.operators import rol
from crysp.padding import MDpadding
from crysp.poly import Poly
from crysp.padding import Nullpadding
from crysp.utils.operators import concat
                r = i//16
                T = rol(a+self.ft[r](b,c,d)+W[i]+self.K[r],self.st[r][i%4])
                a = d
                d = c
                c = b
                b = T
            self.H[0] += a
            self.H[1] += b
            self.H[2] += c
            self.H[3] += d
        return b''.join([pack(h) for h in self.H])

#------------------------------------------------------------------------------
class MD5(MD4):
    def __init__(self):
        super().__init__()
        # modified functions and constants:
        f = lambda x,y,z: z^(x&(y^z))
        g = lambda x,y,z: f(z,x,y)
        h = lambda x,y,z: x^y^z
        i = lambda x,y,z: y^(x|~z)
        self.ft = [f,g,h,i]
        self.K = [0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
                  0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
                  0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
                  0x6b901122,0xfd987193,0xa679438e,0x49b40821,
                  0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
                  0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
                  0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
                  0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,

# Path: crysp/utils/operators.py
# def rol(x,n):
#     return (x<<n | x>>(x.size-n))
# 
# Path: crysp/padding.py
# class MDpadding(blockiterator):
#     def __init__(self,l,wsize):
#         self.wsize = wsize
#         super(MDpadding,self).__init__(l)
#     # add sha1 padding:
#     def lastblock(self,m,**kargs):
#         bitlen = kargs.get('bitlen',None)
#         if bitlen is None:
#             bitlen = self.bitcnt+len(m)*8
#         assert self.bitcnt<=bitlen
#         needed = bitlen-self.bitcnt
#         mb = Bits(m,size=needed)
#         countersize = self.wsize*2
#         N = self.blocksize-1-countersize-needed
#         if N<0: N += self.blocksize
#         pad = mb//Bits(1,1)//Bits(0,N)
#         self.padflag = True
#         self.bitcnt += needed
#         return pad.bytes()+pack(Bits(bitlen,countersize))
#     # remove sha1 padding:
#     def remove(self,c):
#         clen = self.wsize//4
#         counter,_ = unpack(c[-clen:])
#         c = list(c[:-clen])
#         while Bits(c[-1]).ival==0:
#             c.pop()
#         if len(c)==0: raise PaddingError("failed to remove padding")
#         b = Bits(c.pop())
#         b.size=str(b).rfind('1')
#         return b''.join(c)+b.bytes()
# 
# 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/padding.py
# class Nullpadding(blockiterator):
#     def lastblock(self,m,**kargs):
#         bitlen = kargs.get('bitlen',None)
#         if bitlen is None:
#             bitlen = len(m)*8
#         else:
#             bitlen = bitlen-self.bitcnt
#         q = self.blocksize-bitlen
#         b = (Bits(m,bitlen)//Bits(0,q)).bytes()
#         assert len(b)==self.blocklen
#         self.bitcnt += bitlen
#         self.padflag = True
#         self.padcnt = q
#         return b
#     # remove padding:
#     def remove(self,m):
#         b=Bits(m[-self.blocklen:])
#         b.size -= self.padcnt
#         return m[:-self.blocklen]+b.bytes()
# 
# Path: crysp/utils/operators.py
# def concat(L,bigend=False):
#     if len(L)==1: return L[0]
#     if bigend: L = reversed(L)
#     return reduce(lambda x,y: x//y, L)


# Path: crysp/md.py
from crysp.bits import *
from crysp.utils.operators import rol
from crysp.padding import MDpadding
from crysp.poly import Poly
from crysp.padding import Nullpadding
from crysp.utils.operators import concat

                r = i//16
                T = rol(a+self.ft[r](b,c,d)+W[i]+self.K[r],self.st[r][i%4])
                a = d
                d = c
                c = b
                b = T
            self.H[0] += a
            self.H[1] += b
            self.H[2] += c
            self.H[3] += d
        return b''.join([pack(h) for h in self.H])

#------------------------------------------------------------------------------
class MD5(MD4):
    def __init__(self):
        super().__init__()
        # modified functions and constants:
        f = lambda x,y,z: z^(x&(y^z))
        g = lambda x,y,z: f(z,x,y)
        h = lambda x,y,z: x^y^z
        i = lambda x,y,z: y^(x|~z)
        self.ft = [f,g,h,i]
        self.K = [0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
                  0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
                  0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
                  0x6b901122,0xfd987193,0xa679438e,0x49b40821,
                  0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
                  0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
                  0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
                  0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,

                  0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,