input: Write a function to convert LibSVM input to tensors using TensorFlow. 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(\[b"1 1:1 2:2", b"0 1:1", b"-1 1:1 3:1"\], 4, tf.float32, tf.int32) == \(<tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 0, -1], dtype=int32)>, <tf.Tensor: shape=(4, 2), dtype=int64, numpy=array([[0, 1], [0, 2], [1, 1], [2, 1], [2, 3]], dtype=int64)>, <tf.Tensor: shape=(4,), dtype=float32, numpy=array([1., 2., 1., 1., 1.], dtype=float32)>, <tf.Tensor: shape=(2,), dtype=int64, numpy=array([3, 4], dtype=int64)>)
  
  assert decode_libsvm(\[b"1 1:1 2:2", b"2 1:1", b"3 1:1 3:1"\], 4, tf.float32, tf.int32) == \(<tf.Tensor: shape=(3,), dtype=int32, numpy=array([1, 2, 3], dtype=int32)>, <tf.Tensor: shape=(5, 2), dtype=int64, numpy=array([[0, 1], [0, 2], [1, 1], [2, 1], [2, 3]], dtype=int64)>, <tf.Tensor: shape=(5,), dtype=float32, numpy=array([1., 2., 1., 1., 1.], dtype=float32)>, <tf.Tensor: shape=(2,), dtype=int64, numpy=array([3, 4], dtype=int64)>)
  
  assert decode_libsvm(\[b"1 1:1 2:2", b"0 1:1", b"-1 1:1 3:1"\], 4, tf.float64, tf.int64) == \(<tf.Tensor: shape=(3,), dtype=int64, numpy=array([ 1,  0, -1])>, <tf.Tensor: shape=(5, 2), dtype=int64, numpy=array([[0, 1], [0, 2], [1, 1], [2, 1], [2, 3]], dtype=int64)>, <tf.Tensor: shape=(5,), dtype=float64, numpy=array([1., 2., 1., 1., 1.], dtype=float64)>, <tf.Tensor: shape=(2,), dtype=int64, numpy=array([3, 4], dtype=int64)>)
  
  assert decode_libsvm([], 4, tf.float32, tf.int32) == \(<tf.Tensor: shape=(0,), dtype=int32, numpy=array([], dtype=int32)>, <tf.Tensor: shape=(0, 2), dtype=int64, numpy=array([], shape=(0, 2), dtype=int64)>, <tf.Tensor: shape=(0,), dtype=float32, numpy=array([], dtype=float32)>, <tf.Tensor: shape=(2,), dtype=int64, numpy=array([0, 4], dtype=int64)>)
output: 
  def decode_libsvm(input, num_features, dtype=tf.float32, label_dtype=tf.int64, name=None):
      def parse_libsvm(line):
          tokens = tf.strings.split([line], ' ')
          label = tf.strings.to_number(tokens[0], out_type=label_dtype)
          indices = []
          values = []
          for token in tokens[1:]:
              idx, val = tf.strings.split([token], ':')
              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_libsvm,
          input,
          dtype=(label_dtype, tf.int64, dtype),
          infer_shape=False
      )
  
      labels = tf.stack(labels)
      indices = tf.concat(indices, axis=0)
      values = tf.concat(values, axis=0)
  
      batch_size = tf.shape(input)[0]
      feature_shape = tf.stack([batch_size, num_features])
  
      return labels, indices, values, feature_shape
  