code: def element_wise(self, other, op):
          
          if not isscalar(other) and not self.shape == other.shape:
              raise ValueError("shapes %s and %s must be equal" % (self.shape, other.shape))
  
          if not isscalar(other) and isinstance(other, Data) and not self.mode == other.mode:
              raise NotImplementedError
  
          if isscalar(other):
              return self.map(lambda x: op(x, other))
  
          if self.mode == 'local' and isinstance(other, ndarray):
              return self._constructor(op(self.values, other)).__finalize__(self)
  
          if self.mode == 'local' and isinstance(other, Data):
              return self._constructor(op(self.values, other.values)).__finalize__(self)
  
          if self.mode == 'spark' and isinstance(other, Data):
  
              def func(record):
                  (k1, x), (k2, y) = record
                  return k1, op(x, y)
  
              rdd = self.tordd().zip(other.tordd()).map(func)
              barray = BoltArraySpark(rdd, shape=self.shape, dtype=self.dtype, split=self.values.split)
              return self._constructor(barray).__finalize__(self)
docs: Apply an elementwise operation to data.
  
          Both self and other data must have the same mode.
          If self is in local mode, other can also be a numpy array.
          Self and other must have the same shape, or other must be a scalar.
  
          Parameters
          ----------
          other : Data or numpy array
              Data to apply elementwise operation to
  
          op : function
              Binary operator to use for elementwise operations, e.g. add, subtract