code: def update(self, data, ed):
          
          if ed == 'e':
              encrypted_blocks = b''
              self.cache += data
              if len(self.cache) < self.blocksize:
                  return b''
              for i in range(0, len(self.cache)-self.blocksize+1, self.blocksize):
                  self.IV = self.codebook.encrypt(strxor(self.cache[i:i+self.blocksize],self.IV))
                  encrypted_blocks += self.IV
              self.cache = self.cache[i+self.blocksize:]
              return encrypted_blocks
          else:
              decrypted_blocks = b''
              self.cache += data
              if len(self.cache) < self.blocksize:
                  return b''
              for i in range(0, len(self.cache)-self.blocksize+1, self.blocksize):
                  plaintext = strxor(self.IV,self.codebook.decrypt(self.cache[i:i + self.blocksize]))
                  self.IV = self.cache[i:i + self.blocksize]
                  decrypted_blocks+=plaintext
              self.cache = self.cache[i+self.blocksize:]
              return decrypted_blocks
docs: Processes the given ciphertext/plaintext
  
          Inputs:
              data: raw string of any length
              ed:   'e' for encryption, 'd' for decryption
          Output:
              processed raw string block(s), if any
  
          When the supplied data is not a multiple of the blocksize
            of the cipher, then the remaining input data will be cached.
          The next time the update function is called with some data,
            the new data will be concatenated to the cache and then
            cache+data will be processed and full blocks will be outputted.