text: import gzip, numpy, pickle
  import keras
  from keras import backend as K
  from keras import optimizers
  import numpy as np
  from random import shuffle
  
  import matplotlib
  import matplotlib.pyplot as plt
  from matplotlib.animation import FuncAnimation
  
  window_size = 25
  
  def reshape_operator( x4d ):
      import tensorflow as tf
      
      window_tensor = tf.constant(window_size)
      shape = tf.shape( x4d )
      new_d2 = tf.cast(shape[1]/window_tensor, dtype=tf.int32)
      x5d = tf.reshape( x4d, [shape[0] , new_d2, window_tensor, shape[2], shape[3]] )
      return x5d
  
  def reshape_shape( x4d_shape ):
      import tensorflow as tf
      
      in_batch, in_rows, in_cols, in_filters = x4d_shape
      output_shape = ( in_batch, None, window_size, in_cols, in_filters)
      return output_shape
      
      
  def reshape_operator_noembed( x3d ):
      import tensorflow as tf
      
      window_tensor = tf.constant(window_size)
      shape = tf.shape( x3d )
      new_d2 = tf.cast(shape[1]/window_tensor, dtype=tf.int32)
      x4d = tf.reshape( x3d, [shape[0] , new_d2, window_tensor, shape[2], 1] )
      return x4d
  
  def reshape_shape_noembed( x3d_shape ):
      import tensorflow as tf
      
      in_batch, in_rows, in_cols = x3d_shape
      output_shape = ( in_batch, None, window_size, in_cols, 1)
      return output_shape
  
  def mean_operator(x5d):
      return K.mean(x5d, axis=1, keepdims=False)
      
  def mean_shape(x5d_shape):
      in_batch, in_steps, in_window_size, in_cols, in_filters = x5d_shape
      output_shape = (in_batch, in_window_size, in_cols, in_filters)
      return output_shape
      
  def max_operator(x5d):
      return K.max(x5d, axis=1, keepdims=False)
      
  def max_shape(x5d_shape):
      in_batch, in_steps, in_window_size, in_cols, in_filters = x5d_shape
      output_shape = (in_batch, in_window_size, in_cols, in_filters)
      return output_shape
  
  def max_min_mean_operator(x5d):
      max = K.max(x5d, axis=1, keepdims=True)
      min = K.min(x5d, axis=1, keepdims=True)
      mean = K.mean(x5d, axis=1, keepdims=True)
      return K.concatenate([max, min, mean], axis=1)
      
  def max_min_mean_shape(x5d_shape):
      in_batch, in_steps, in_window_size, in_cols, in_filters = x5d_shape
      output_shape = (in_batch, 3, in_window_size, in_cols, in_filters)
      return output_shape
      
      
      
  def disarrange(a, axis=-1):
      """
      Shuffle `a` in-place along the given axis.
  
      Apply numpy.random.shuffle to the given axis of `a`.
      Each one-dimensional slice is shuffled independently.
      """
      b = a.swapaxes(axis, -1)
      # Shuffle `b` in-place along the last axis.  `b` is a view of `a`,
      # so `a` is shuffled in place, too.
      shp = b.shape[:-1]
      for ndx in np.ndindex(shp):
          np.random.shuffle(b[ndx])
      return
      
  # batch_size = 5
  batch_size = 1
  
  def loadDataGeneratorBinary(assemblyVectorizedFile, maxNumSamples=10000):
      readMe = gzip.open(assemblyVectorizedFile, "rb")
      numSamples = 0
      
      while True:
          try:
              assert numSamples < maxNumSamples
              
              xArr = []
              answers = []
              # for x in range(0,batch_size):
              fileArr = pickle.load(readMe)
              xArr.append(fileArr[0])
              answers.append(fileArr[1])
              
              train_x = numpy.asarray(xArr, dtype="float32") 
              # print( train_x.shape[1])
              # if train_x.shape[1] > 150000: 
              if train_x.shape[1] > 100000: 
                  # print("skipping", train_x.shape[1])
                  continue
              
              # disarrange(train_x, axis=1)
              
              # ans = []
              # for x in range(0,batch_size):
                  # num = answers.pop(0)[0]
                  # answer = [0]*9
                  # answer[num-1] = 1
                  # ans.append(answer)
                  # print("ANSWER IS", num)
              train_y = numpy.asarray(answers, dtype="float32")
              
              numSamples += 1
              
              yield (train_x, train_y)
                  
              
              
              
          except (EOFError, AssertionError) as e :
              del readMe
              readMe = gzip.open(assemblyVectorizedFile, "rb")
              numSamples = 0
      
  def loadDataGeneratorAllClasses(assemblyVectorizedFile, maxNumSamples=10000):
      readMe = gzip.open(assemblyVectorizedFile, "rb")
      numSamples = 0
      
      while True:
          try:
              assert numSamples < maxNumSamples
              
              xArr = []
              answers = []
              # for x in range(0,batch_size):
              fileArr = pickle.load(readMe)
              xArr.append(fileArr[0])
              answers.append(fileArr[1])
              # print(answers)
              
              train_x = numpy.asarray(xArr, dtype="float32") 
              
              # disarrange(train_x, axis=1)
              
              ans = []
              for x in range(0,batch_size):
                  num = answers.pop(0)
                  answer = [0]*11
                  answer[num-1] = 1
                  ans.append(answer)
                  # print("ANSWER IS", num)
              train_y = numpy.asarray(ans, dtype="float32")
              
              numSamples += 1
              
              yield (train_x, train_y)
                  
              
              
              
          except (EOFError, AssertionError) as e :
              del readMe
              readMe = gzip.open(assemblyVectorizedFile, "rb")
              numSamples = 0
      
  
  def loadDataGeneratorBatches(assemblyVectorizedFile, maxNumSamples=10000):
      readMe = gzip.open(assemblyVectorizedFile, "rb")
      numSamples = 0
      
      while True:
          try:
              assert numSamples < maxNumSamples
              
              xArr = []
              answers = []
              # for x in range(0,batch_size):
              fileArr = pickle.load(readMe)
              xArr.append(fileArr[0])
              answers.append(fileArr[1])
              
              train_x = numpy.asarray(xArr, dtype="float32") 
              
              # disarrange(train_x, axis=1)
              
              ans = []
              for x in range(0,batch_size):
                  num = answers.pop(0)[0]
                  answer = [0]*9
                  answer[num-1] = 1
                  ans.append(answer)
                  # print("ANSWER IS", num)
              train_y = numpy.asarray(ans, dtype="float32")
              
              numSamples += 1
              
              yield (train_x, train_y)
                  
              
              
              
          except (EOFError, AssertionError) as e :
              del readMe
              readMe = gzip.open(assemblyVectorizedFile, "rb")
              numSamples = 0
      
  def loadDataGeneratorPartial(assemblyVectorizedFile, maxNumSamples=10000, percentOfFile=1, loc=0):
      if percentOfFile > 1: 
          print("percentOfFile cannot be > 1")
          print(0/0)
      
      readMe = gzip.open(assemblyVectorizedFile, "rb")
      numSamples = 0
      
      while True:
          try:
              assert numSamples < maxNumSamples
              
              xArr = []
              answers = []
              # for x in range(0,batch_size):
              fileArr = pickle.load(readMe)
              xArr.append(fileArr[0])
              answers.append(fileArr[1])
              
              train_x = numpy.asarray(xArr, dtype="float32") 
              # print(train_x.shape)
              x_len = percentOfFile*train_x.shape[1]
              base = 25
              newEnd = int(base * round(float(x_len)/base))
              
              # if newEnd == 0:
                  # yield (False, False)
                  # newEnd = 25
                  # continue
              
              # print(newEnd)
              if loc == 0:
                  train_x = train_x[:, :newEnd]
              elif loc == 1:
                  train_x = train_x[:, -newEnd:]
              else:
                  print("what percent of file")
              # print(train_x.shape)
              
              # disarrange(train_x, axis=1)
              
              ans = []
              for x in range(0,batch_size):
                  num = answers.pop(0)[0]
                  answer = [0]*9
                  answer[num-1] = 1
                  ans.append(answer)
                  # print("ANSWER IS", num)
              train_y = numpy.asarray(ans, dtype="float32")
              
              numSamples += 1
              
              yield (train_x, train_y)
                  
              
              
              
          except (EOFError, AssertionError) as e :
              del readMe
              readMe = gzip.open(assemblyVectorizedFile, "rb")
              numSamples = 0
      
  def loadDataGeneratorBatchesShuffle(assemblyVectorizedFile, maxNumSamples=10000):
      readMe = gzip.open(assemblyVectorizedFile, "rb")
      numSamples = 0
      
      while True:
          try:
              samples = []
              for n in range(0, 500):
                  assert numSamples < maxNumSamples
                  
                  xArr = []
                  answers = []
                  # for x in range(0,batch_size):
                  fileArr = pickle.load(readMe)
                  xArr.append(fileArr[0])
                  answers.append(fileArr[1])
                  
                  train_x = numpy.asarray(xArr, dtype="float32") 
                  
                  # disarrange(train_x, axis=1)
                  
                  ans = []
                  for x in range(0,batch_size):
                      num = answers.pop(0)[0]
                      answer = [0]*9
                      answer[num-1] = 1
                      ans.append(answer)
                      # print("ANSWER IS", num)
                  train_y = numpy.asarray(ans, dtype="float32")
                  
                  numSamples += 1
                  
                  # yield (train_x, train_y)
                  
                  samples.append((train_x, train_y))
              
              shuffle(samples)
              
              for n in range(0, 50):
                  yield samples[n]
              
              
          except (EOFError, AssertionError) as e :
              del readMe
              readMe = gzip.open(assemblyVectorizedFile, "rb")
              numSamples = 0
              
              
  def loadDataGeneratorBatchesIDK(assemblyVectorizedFile, maxNumSamples=10000):
      readMe = gzip.open(assemblyVectorizedFile, "rb")
      numSamples = 0
      
      
      while True:
          try:
              assert numSamples < maxNumSamples
              
              xArr = []
              answers = []
              for x in range(0,batch_size):
                  fileArr = pickle.load(readMe)
                  xArr.append(np.asarray(fileArr[0]))
                  answers.append(fileArr[1])
                  
                  # train_x = numpy.asarray(xArr, dtype="float32") 
                  
              train_x = xArr
              
              ans = []
              for x in range(0,batch_size):
                  num = answers.pop(0)[0]
                  answer = [0]*9
                  answer[num-1] = 1
                  ans.append(answer)
                  # print("ANSWER IS", num)
              # train_y = numpy.asarray(ans, dtype="float32")
              train_y = ans
              
              numSamples += 1
              yield (train_x, train_y)
              
          except (EOFError, AssertionError) as e :
              del readMe
              readMe = gzip.open(assemblyVectorizedFile, "rb")
              numSamples = 0
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
              
  
  
  class TrackGradient(keras.callbacks.Callback):
      
      def get_model_params(self):
          allWeights = []
          for layer in self.model.layers:
              weights = layer.get_weights()
              
              if not weights: continue
              
              for kernel in weights:
                  allWeights.extend(kernel.flatten().tolist())
                  
          return np.asarray(allWeights)
          
      
      def get_model_params_new(self):
          return [param.get_value() for param in self.model.params]
      
      def on_train_begin(self, logs={}):
          # self.prevPrevWeights = []
          # self.prevWeights = []
          # self.weights = []
          self.losses = []
          self.cnt = 0
          self.gradients = []
          
          self.init_params = self.get_model_params()
          self.previous_model_params_ = self.init_params
  
          
      def on_batch_end(self, epoch, logs={}):
          self.cnt += 1
          
          
  
          loss = logs.get('loss')
          if loss is not None:
              if self.cnt % 500 or np.isnan(loss) or np.isinf(loss):
                  current_model_params = self.get_model_params()
                  gradients = [(param - prev_param) for (param, prev_param) in zip(current_model_params,
                                                                                   self.previous_model_params_)]
                  self.gradients.append(gradients)
                  if len(self.gradients) > 3: self.gradients.pop(0)
                  self.previous_model_params_ = current_model_params
                  # self.model_params.append(current_model_params)
                  
                  self.losses.append(loss)
                  
                  
                  if np.isnan(loss) or np.isinf(loss):
                      print('Batch %d: Invalid loss, terminating training' % (batch))
                      self.model.stop_training = True
                      
                      # for grad in self.gradients[-20:]:
                      for grad in self.gradients:
                          print(np.mean(grad), np.max(grad), np.min(grad))
                  
          
          
      def on_batch_end_OLD(self, epoch, logs={}):
          self.cnt += 1
          
          
          current_model_params = self.get_model_params()
          gradients = [(param - prev_param) for (param, prev_param) in zip(current_model_params,
                                                                           self.previous_model_params_)]
          self.gradients.append(gradients)
          self.previous_model_params_ = current_model_params
          # self.model_params.append(current_model_params)
          
          loss = logs.get('loss')
          self.losses.append(loss)
          if loss is not None:
              if np.isnan(loss) or np.isinf(loss):
          
                  print('Batch %d: Invalid loss, terminating training' % (batch))
                  self.model.stop_training = True
                  
                  # print(self.gradients[-1][:15])
                  
                  '''
                  xAxis = np.arange(0, len(gradients))
                  fig, ax = plt.subplots()
                  line, = ax.plot(xAxis, self.gradients[0])
                  def animate(i):
                      line.set_ydata(self.gradients[i])  # update the data
                      print("grads", i)
                      return line,
                  anim = FuncAnimation(fig, animate, frames=np.arange(0, len(self.gradients)), interval=1000)
                  plt.show()
                  '''
                  
                  for grad in self.gradients[-20:]:
                      print(np.mean(grad), np.max(grad), np.min(grad))
                  
          
                  # writeMe = gzip.open(r"D:\Research\2_Malware_Detection\STOPPED_GRAD_WEIGHTS.pklz", "w+b")
                  # pickle.dump(self.prevWeights, writeMe)
                  # pickle.dump(self.weights, writeMe)
                  # writeMe.close()
                  # print(self.losses)
                  
                  
                  # print(self.prevWeights)
                  # print()
                  # print(self.weights)
                  # print()
                  
                  # self.prevWeights = np.asarray(self.prevWeights)
                  # self.weights = np.asarray(self.weights)
                  
                  # diff = self.weights - self.prevWeights
                  
                  # print(self.prevWeights[np.argmax(self.prevWeights)])
                  # print(self.prevWeights[np.argmin(self.prevWeights)])
                  
              # else:
                  # self.model.save(r"D:\Research\2_Malware_Detection\kaggle_networks\WILL_FAIL.hdf5", overwrite=True)
  
      # def on_epoch_end(self, epoch, logs={}):
          #use W' = W + dW to calculate the gradients
          # current_model_params = self.get_model_params()
          # gradients = [(param - prev_param) for (param, prev_param) in zip(current_model_params,
                                                                           # self.previous_model_params_)]
          # self.gradients.append(gradients)
          # self.previous_model_params_ = current_model_params
          # self.model_params.append(current_model_params)
  
          # self.layer_out.append(self.get_layer_out())
  
          # val_epoch_acc = logs.get('val_acc')
          # self.val_acc.append(val_epoch_acc)
          # train_epoch_acc = self.model.evaluate(self.X_train_subset, self.y_train_subset,
                                                # show_accuracy=True, verbose=0)[1]
          # self.train_acc.append(train_epoch_acc)
          # print('(train accuracy, val accuracy): (%.4f, %.4f)' % (train_epoch_acc, val_epoch_acc))
  
      # def get_layer_out(self):
          # layer_index = self.layer_index
          # get_activation = theano.function([self.model.layers[0].input],
                                         # self.model.layers[layer_index].get_output(train=False), allow_input_downcast=True)
          # return get_activation(self.X_train_subset)
  
      # def get_model_params(self):
          # return [param.get_value() for param in self.model.params]
          
      def minMe(self):
          pass
  