code: def create_preprocessing_f(X, input_range=None):
      """
      Generically shifts data from interval [a, b] to interval [c, d].
      Assumes that theoretical min and max values are populated.
      """
      if input_range is None:
          input_range = [0, 1]
  
      if len(input_range) != 2:
          raise ValueError(
              f"Input range must be of length 2, but was {len(input_range)}"
          )
      if input_range[0] >= input_range[1]:
          raise ValueError(
              f"Values in input_range must be ascending. It is {input_range}"
          )
  
      a, b = X.min(), X.max()
      c, d = input_range
  
      def preprocessing(X):
          # Make sure images have shape (28, 28, 1) and cast from uint8 to float32
          X = np.expand_dims(X, -1).astype(np.float32)
          # shift original data to [0, b-a] (and copy)
          X = X - a
          # scale to new range gap [0, d-c]
          X /= b - a
          X *= d - c
          # shift to desired output range
          X += c
          return X
  
      def revert_preprocessing(X):
          X = X - c
          X /= d - c
          X *= b - a
          X += a
          return X
  
      return preprocessing, revert_preprocessing
  
file_path: innvestigate/examples/utils/mnist.py
imports: from __future__ import annotations
  import numpy
  import tensorflow.keras
  from tensorflow.keras.backend import image_data_format
  from tensorflow.keras.datasets import mnist
  import innvestigate.utils
  import innvestigate.utils.visualizations
label_desc: Use image node_id 2 for calling a global function with example usage: create_preprocessing_f(X, input_range) and returns: preprocessing,  revert_preprocessing, X, X