text: #ifndef CAFFE_COMMON_LAYERS_HPP_
  #define CAFFE_COMMON_LAYERS_HPP_
  
  #include <string>
  #include <utility>
  #include <vector>
  
  #include "caffe/blob.hpp"
  #include "caffe/common.hpp"
  #include "caffe/data_layers.hpp"
  #include "caffe/layer.hpp"
  #include "caffe/loss_layers.hpp"
  #include "caffe/neuron_layers.hpp"
  #include "caffe/proto/caffe.pb.h"
  
  namespace caffe {
  
  /**
   * @brief Compute the index of the @f$ K @f$ max values for each datum across
   *        all dimensions @f$ (C \times H \times W) @f$.
   *
   * Intended for use after a classification layer to produce a prediction.
   * If parameter out_max_val is set to true, output is a vector of pairs
   * (max_ind, max_val) for each image.
   *
   * NOTE: does not implement Backwards operation.
   */
  template <typename Dtype>
  class ArgMaxLayer : public Layer<Dtype> {
   public:
    /**
     * @param param provides ArgMaxParameter argmax_param,
     *     with ArgMaxLayer options:
     *   - top_k (\b optional uint, default 1).
     *     the number @f$ K @f$ of maximal items to output.
     *   - out_max_val (\b optional bool, default false).
     *     if set, output a vector of pairs (max_ind, max_val) for each image.
     */
    explicit ArgMaxLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "ArgMax"; }
    virtual inline int ExactNumBottomBlobs() const { return 1; }
    virtual inline int ExactNumTopBlobs() const { return 1; }
  
   protected:
    /**
     * @param bottom input Blob vector (length 1)
     *   -# @f$ (N \times C \times H \times W) @f$
     *      the inputs @f$ x @f$
     * @param top output Blob vector (length 1)
     *   -# @f$ (N \times 1 \times K \times 1) @f$ or, if out_max_val
     *      @f$ (N \times 2 \times K \times 1) @f$
     *      the computed outputs @f$
     *       y_n = \arg\max\limits_i x_{ni}
     *      @f$ (for @f$ K = 1 @f$).
     */
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    /// @brief Not implemented (non-differentiable function)
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
      NOT_IMPLEMENTED;
    }
    bool out_max_val_;
    size_t top_k_;
  };
  
  /**
   * @brief Takes at least two Blob%s and concatenates them along either the num
   *        or channel dimension, outputting the result.
   */
  template <typename Dtype>
  class ConcatLayer : public Layer<Dtype> {
   public:
    explicit ConcatLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "Concat"; }
    virtual inline int MinBottomBlobs() const { return 2; }
    virtual inline int ExactNumTopBlobs() const { return 1; }
  
   protected:
    /**
     * @param bottom input Blob vector (length 2+)
     *   -# @f$ (N \times C \times H \times W) @f$
     *      the inputs @f$ x_1 @f$
     *   -# @f$ (N \times C \times H \times W) @f$
     *      the inputs @f$ x_2 @f$
     *   -# ...
     *   - K @f$ (N \times C \times H \times W) @f$
     *      the inputs @f$ x_K @f$
     * @param top output Blob vector (length 1)
     *   -# @f$ (KN \times C \times H \times W) @f$ if axis == 0, or
     *      @f$ (N \times KC \times H \times W) @f$ if axis == 1:
     *      the concatenated output @f$
     *        y = [\begin{array}{cccc} x_1 & x_2 & ... & x_K \end{array}]
     *      @f$
     */
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    /**
     * @brief Computes the error gradient w.r.t. the concatenate inputs.
     *
     * @param top output Blob vector (length 1), providing the error gradient with
     *        respect to the outputs
     *   -# @f$ (KN \times C \times H \times W) @f$ if axis == 0, or
     *      @f$ (N \times KC \times H \times W) @f$ if axis == 1:
     *      containing error gradients @f$ \frac{\partial E}{\partial y} @f$
     *      with respect to concatenated outputs @f$ y @f$
     * @param propagate_down see Layer::Backward.
     * @param bottom input Blob vector (length K), into which the top gradient
     *        @f$ \frac{\partial E}{\partial y} @f$ is deconcatenated back to the
     *        inputs @f$
     *        \left[ \begin{array}{cccc}
     *          \frac{\partial E}{\partial x_1} &
     *          \frac{\partial E}{\partial x_2} &
     *          ... &
     *          \frac{\partial E}{\partial x_K}
     *        \end{array} \right] =
     *        \frac{\partial E}{\partial y}
     *        @f$
     */
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    int count_;
    int num_concats_;
    int concat_input_size_;
    int concat_axis_;
  };
  
  /**
   * @brief Compute elementwise operations, such as product and sum,
   *        along multiple input Blobs.
   *
   * TODO(dox): thorough documentation for Forward, Backward, and proto params.
   */
  template <typename Dtype>
  class EltwiseLayer : public Layer<Dtype> {
   public:
    explicit EltwiseLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "Eltwise"; }
    virtual inline int MinBottomBlobs() const { return 2; }
    virtual inline int ExactNumTopBlobs() const { return 1; }
  
   protected:
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    EltwiseParameter_EltwiseOp op_;
    vector<Dtype> coeffs_;
    Blob<int> max_idx_;
  
    bool stable_prod_grad_;
  };
  
  /**
   * @brief Takes two+ Blobs, interprets last Blob as a selector and
   *  filter remaining Blobs accordingly with selector data (0 means that
   * the corresponding item has to be filtered, non-zero means that corresponding
   * item needs to stay).
   */
  template <typename Dtype>
  class FilterLayer : public Layer<Dtype> {
   public:
    explicit FilterLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "Filter"; }
    virtual inline int MinBottomBlobs() const { return 2; }
    virtual inline int MinTopBlobs() const { return 1; }
  
   protected:
    /**
     * @param bottom input Blob vector (length 2+)
     *   -# @f$ (N \times C \times H \times W) @f$
     *      the inputs to be filtered @f$ x_1 @f$
     *   -# ...
     *   -# @f$ (N \times C \times H \times W) @f$
     *      the inputs to be filtered @f$ x_K @f$
     *   -# @f$ (N \times 1 \times 1 \times 1) @f$
     *      the selector blob
     * @param top output Blob vector (length 1+)
     *   -# @f$ (S \times C \times H \times W) @f$ ()
     *        the filtered output @f$ x_1 @f$
     *        where S is the number of items
     *        that haven't been filtered
     *      @f$ (S \times C \times H \times W) @f$
     *        the filtered output @f$ x_K @f$
     *        where S is the number of items
     *        that haven't been filtered
     */
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
      const vector<Blob<Dtype>*>& top);
  
    /**
     * @brief Computes the error gradient w.r.t. the forwarded inputs.
     *
     * @param top output Blob vector (length 1+), providing the error gradient with
     *        respect to the outputs
     * @param propagate_down see Layer::Backward.
     * @param bottom input Blob vector (length 2+), into which the top error
     *        gradient is copied
     */
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
      const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    bool first_reshape_;
    vector<int> indices_to_forward_;
  };
  
  /**
   * @brief Reshapes the input Blob into flat vectors.
   *
   * Note: because this layer does not change the input values -- merely the
   * dimensions -- it can simply copy the input. The copy happens "virtually"
   * (thus taking effectively 0 real time) by setting, in Forward, the data
   * pointer of the top Blob to that of the bottom Blob (see Blob::ShareData),
   * and in Backward, the diff pointer of the bottom Blob to that of the top Blob
   * (see Blob::ShareDiff).
   */
  template <typename Dtype>
  class FlattenLayer : public Layer<Dtype> {
   public:
    explicit FlattenLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "Flatten"; }
    virtual inline int ExactNumBottomBlobs() const { return 1; }
    virtual inline int ExactNumTopBlobs() const { return 1; }
  
   protected:
    /**
     * @param bottom input Blob vector (length 2+)
     *   -# @f$ (N \times C \times H \times W) @f$
     *      the inputs
     * @param top output Blob vector (length 1)
     *   -# @f$ (N \times CHW \times 1 \times 1) @f$
     *      the outputs -- i.e., the (virtually) copied, flattened inputs
     */
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    /**
     * @brief Computes the error gradient w.r.t. the concatenate inputs.
     *
     * @param top output Blob vector (length 1), providing the error gradient with
     *        respect to the outputs
     * @param propagate_down see Layer::Backward.
     * @param bottom input Blob vector (length K), into which the top error
     *        gradient is (virtually) copied
     */
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  };
  
  /**
   * @brief Also known as a "fully-connected" layer, computes an inner product
   *        with a set of learned weights, and (optionally) adds biases.
   *
   * TODO(dox): thorough documentation for Forward, Backward, and proto params.
   */
  template <typename Dtype>
  class InnerProductLayer : public Layer<Dtype> {
   public:
    explicit InnerProductLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "InnerProduct"; }
    virtual inline int ExactNumBottomBlobs() const { return 1; }
    virtual inline int ExactNumTopBlobs() const { return 1; }
  
   protected:
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    int M_;
    int K_;
    int N_;
    bool bias_term_;
    Blob<Dtype> bias_multiplier_;
  };
  
  /**
   * @brief Normalizes the input to have 0-mean and/or unit (1) variance.
   *
   * TODO(dox): thorough documentation for Forward, Backward, and proto params.
   */
  template <typename Dtype>
  class MVNLayer : public Layer<Dtype> {
   public:
    explicit MVNLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "MVN"; }
    virtual inline int ExactNumBottomBlobs() const { return 1; }
    virtual inline int ExactNumTopBlobs() const { return 1; }
  
   protected:
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    Blob<Dtype> mean_, variance_, temp_;
  
    /// sum_multiplier is used to carry out sum using BLAS
    Blob<Dtype> sum_multiplier_;
    Dtype eps_;
  };
  
  /*
   * @brief Reshapes the input Blob into an arbitrary-sized output Blob.
   *
   * Note: similarly to FlattenLayer, this layer does not change the input values
   * (see FlattenLayer, Blob::ShareData and Blob::ShareDiff).
   */
  template <typename Dtype>
  class ReshapeLayer : public Layer<Dtype> {
   public:
    explicit ReshapeLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "Reshape"; }
    virtual inline int ExactNumBottomBlobs() const { return 1; }
    virtual inline int ExactNumTopBlobs() const { return 1; }
  
   protected:
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top) {}
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {}
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top) {}
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {}
  
    /// @brief vector of axes indices whose dimensions we'll copy from the bottom
    vector<int> copy_axes_;
    /// @brief the index of the axis whose dimension we infer, or -1 if none
    int inferred_axis_;
    /// @brief the product of the "constant" output dimensions
    int constant_count_;
  };
  
  /**
   * @brief Compute "reductions" -- operations that return a scalar output Blob
   *        for an input Blob of arbitrary size, such as the sum, absolute sum,
   *        and sum of squares.
   *
   * TODO(dox): thorough documentation for Forward, Backward, and proto params.
   */
  template <typename Dtype>
  class ReductionLayer : public Layer<Dtype> {
   public:
    explicit ReductionLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "Reduction"; }
    virtual inline int ExactNumBottomBlobs() const { return 1; }
    virtual inline int ExactNumTopBlobs() const { return 1; }
  
   protected:
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    /// @brief the reduction operation performed by the layer
    ReductionParameter_ReductionOp op_;
    /// @brief a scalar coefficient applied to all outputs
    Dtype coeff_;
    /// @brief the index of the first input axis to reduce
    int axis_;
    /// @brief the number of reductions performed
    int num_;
    /// @brief the input size of each reduction
    int dim_;
    /// @brief a helper Blob used for summation (op_ == SUM)
    Blob<Dtype> sum_multiplier_;
  };
  
  /**
   * @brief Ignores bottom blobs while producing no top blobs. (This is useful
   *        to suppress outputs during testing.)
   */
  template <typename Dtype>
  class SilenceLayer : public Layer<Dtype> {
   public:
    explicit SilenceLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top) {}
  
    virtual inline const char* type() const { return "Silence"; }
    virtual inline int MinBottomBlobs() const { return 1; }
    virtual inline int ExactNumTopBlobs() const { return 0; }
  
   protected:
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top) {}
    // We can't define Forward_gpu here, since STUB_GPU will provide
    // its own definition for CPU_ONLY mode.
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  };
  
  /**
   * @brief Computes the softmax function.
   *
   * TODO(dox): thorough documentation for Forward, Backward, and proto params.
   */
  template <typename Dtype>
  class SoftmaxLayer : public Layer<Dtype> {
   public:
    explicit SoftmaxLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "Softmax"; }
    virtual inline int ExactNumBottomBlobs() const { return 1; }
    virtual inline int ExactNumTopBlobs() const { return 1; }
  
   protected:
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    int outer_num_;
    int inner_num_;
    int softmax_axis_;
    /// sum_multiplier is used to carry out sum using BLAS
    Blob<Dtype> sum_multiplier_;
    /// scale is an intermediate Blob to hold temporary results.
    Blob<Dtype> scale_;
  };
  
  #ifdef USE_CUDNN
  /**
   * @brief cuDNN implementation of SoftmaxLayer.
   *        Fallback to SoftmaxLayer for CPU mode.
   */
  template <typename Dtype>
  class CuDNNSoftmaxLayer : public SoftmaxLayer<Dtype> {
   public:
    explicit CuDNNSoftmaxLayer(const LayerParameter& param)
        : SoftmaxLayer<Dtype>(param), handles_setup_(false) {}
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual ~CuDNNSoftmaxLayer();
  
   protected:
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
       const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    bool handles_setup_;
    cudnnHandle_t             handle_;
    cudnnTensorDescriptor_t bottom_desc_;
    cudnnTensorDescriptor_t top_desc_;
  };
  #endif
  
  /**
   * @brief Creates a "split" path in the network by copying the bottom Blob
   *        into multiple top Blob%s to be used by multiple consuming layers.
   *
   * TODO(dox): thorough documentation for Forward, Backward, and proto params.
   */
  template <typename Dtype>
  class SplitLayer : public Layer<Dtype> {
   public:
    explicit SplitLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "Split"; }
    virtual inline int ExactNumBottomBlobs() const { return 1; }
    virtual inline int MinTopBlobs() const { return 1; }
  
   protected:
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    int count_;
  };
  
  /**
   * @brief Takes a Blob and slices it along either the num or channel dimension,
   *        outputting multiple sliced Blob results.
   *
   * TODO(dox): thorough documentation for Forward, Backward, and proto params.
   */
  template <typename Dtype>
  class SliceLayer : public Layer<Dtype> {
   public:
    explicit SliceLayer(const LayerParameter& param)
        : Layer<Dtype>(param) {}
    virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Reshape(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
  
    virtual inline const char* type() const { return "Slice"; }
    virtual inline int ExactNumBottomBlobs() const { return 1; }
    virtual inline int MinTopBlobs() const { return 2; }
  
   protected:
    virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Forward_gpu(const vector<Blob<Dtype>*>& bottom,
        const vector<Blob<Dtype>*>& top);
    virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
    virtual void Backward_gpu(const vector<Blob<Dtype>*>& top,
        const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom);
  
    int count_;
    int num_slices_;
    int slice_size_;
    int slice_axis_;
    vector<int> slice_point_;
  };
  
  }  // namespace caffe
  
  #endif  // CAFFE_COMMON_LAYERS_HPP_
  