input: Write a function to decode a TensorFlow LibSVM input into tensors. The function should be named `decode_libsvm` and should take the following arguments: `input` (a `Tensor` of type `string`), `num_features` (an `int` that is `>= 1`), `dtype` (an optional `tf.DType` from: `tf.float32, tf.float64, tf.int32, tf.int64`. Defaults to `tf.float32`), `label_dtype` (an optional `tf.DType` from: `tf.float32, tf.float64, tf.int32, tf.int64`. Defaults to `tf.int64`), and `name` (a name for the operation, optional). The function should return a tuple of `Tensor` objects `(label, feature_indices, feature_values, feature_shape)`, where `label` is a `Tensor` of type `label_dtype`, `feature_indices` is a `Tensor` of type `int64`, `feature_values` is a `Tensor` of type `dtype`, and `feature_shape` is a `Tensor` of type `int64`.
  
  
  assert decode_libsvm(tf.constant(["1 1:1", "2 2:2"]), 2, tf.float32, tf.int32) == (
      tf.constant([1, 2], dtype=tf.int32),
      tf.constant([[0, 1], [1, 2]], dtype=tf.int64),
      tf.constant([1.0, 2.0], dtype=tf.float32),
      tf.constant([2, 2], dtype=tf.int64)
  )
  assert decode_libsvm(tf.constant(["1 1:1", "2 2:2"]), 2, tf.int32, tf.int32) == (
      tf.constant([1, 2], dtype=tf.int32),
      tf.constant([[0, 1], [1, 2]], dtype=tf.int64),
      tf.constant([1, 2], dtype=tf.int32),
      tf.constant([2, 2], dtype=tf.int64)
  )
  assert decode_libsvm(tf.constant(["1 1:1", "2 2:2"]), 2, tf.float64, tf.int64) == (
      tf.constant([1, 2], dtype=tf.int64),
      tf.constant([[0, 1], [1, 2]], dtype=tf.int64),
      tf.constant([1.0, 2.0], dtype=tf.float64),
      tf.constant([2, 2], dtype=tf.int64)
  )
  assert decode_libsvm(tf.constant([""]), 1, tf.float32, tf.int32) == (
      tf.constant([0], dtype=tf.int32),
      tf.constant([[0, 0]], dtype=tf.int64),
      tf.constant([0.0], dtype=tf.float32),
      tf.constant([1, 1], dtype=tf.int64)
  )
  assert decode_libsvm(tf.constant([]), 1, tf.float32, tf.int32) == (
      tf.constant([], dtype=tf.int32),
      tf.constant([], dtype=tf.int64),
      tf.constant([], dtype=tf.float32),
      tf.constant([0, 1], dtype=tf.int64)
  )
  
output: def decode_libsvm(input, num_features, dtype=tf.float32, label_dtype=tf.int64, name=None):
      with tf.name_scope(name, "decode_libsvm", [input, num_features, dtype, label_dtype]):
          def parse_line(line):
              parts = tf.strings.split([line], ' ')
              label = tf.strings.to_number(parts[0], out_type=label_dtype)
              indices = []
              values = []
              for part in parts[1:]:
                  if part:
                      idx, val = tf.strings.split([part], ':')
                      indices.append(tf.strings.to_number(idx, out_type=tf.int64))
                      values.append(tf.strings.to_number(val, out_type=dtype))
              return label, indices, values
  
          labels, indices, values = tf.map_fn(
              parse_line,
              input,
              dtype=(label_dtype, tf.int64, dtype),
              back_prop=False
          )
  
          labels = tf.stack(labels)
          indices = tf.ragged.constant(indices, dtype=tf.int64).flat_values
          values = tf.ragged.constant(values, dtype=dtype).flat_values
  
          feature_indices = tf.reshape(indices, [-1, 1])
          feature_values = values
          feature_shape = tf.stack([tf.shape(input, out_type=tf.int64)[0], tf.constant(num_features, dtype=tf.int64)])
  
          return labels, feature_indices, feature_values, feature_shape