text: // matrix/sparse-matrix.cc
  
  // Copyright 2015     Johns Hopkins University (author: Daniel Povey)
  //           2015     Guoguo Chen
  
  // See ../../COPYING for clarification regarding multiple authors
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  //
  //  http://www.apache.org/licenses/LICENSE-2.0
  //
  // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  // MERCHANTABLITY OR NON-INFRINGEMENT.
  // See the Apache 2 License for the specific language governing permissions and
  // limitations under the License.
  
  #include <algorithm>
  #include <limits>
  #include <string>
  
  #include "matrix/sparse-matrix.h"
  #include "matrix/kaldi-matrix.h"
  
  namespace kaldi {
  
  template <typename Real>
  std::pair<MatrixIndexT, Real>* SparseVector<Real>::Data() {
    if (pairs_.empty())
      return NULL;
    else
      return &(pairs_[0]);
  }
  
  template <typename Real>
  const std::pair<MatrixIndexT, Real>* SparseVector<Real>::Data() const {
    if (pairs_.empty())
      return NULL;
    else
      return &(pairs_[0]);
  }
  
  template <typename Real>
  Real SparseVector<Real>::Sum() const {
    Real sum = 0;
    for (int32 i = 0; i < pairs_.size(); ++i) {
      sum += pairs_[i].second;
    }
    return sum;
  }
  
  template<typename Real>
  void SparseVector<Real>::Scale(Real alpha) {
    for (int32 i = 0; i < pairs_.size(); ++i)
      pairs_[i].second *= alpha;
  }
  
  template <typename Real>
  template <typename OtherReal>
  void SparseVector<Real>::CopyElementsToVec(VectorBase<OtherReal> *vec) const {
    KALDI_ASSERT(vec->Dim() == this->dim_);
    vec->SetZero();
    OtherReal *other_data = vec->Data();
    typename std::vector<std::pair<MatrixIndexT, Real> >::const_iterator
        iter = pairs_.begin(), end = pairs_.end();
    for (; iter != end; ++iter)
      other_data[iter->first] = iter->second;
  }
  template
  void SparseVector<float>::CopyElementsToVec(VectorBase<float> *vec) const;
  template
  void SparseVector<float>::CopyElementsToVec(VectorBase<double> *vec) const;
  template
  void SparseVector<double>::CopyElementsToVec(VectorBase<float> *vec) const;
  template
  void SparseVector<double>::CopyElementsToVec(VectorBase<double> *vec) const;
  
  template <typename Real>
  template <typename OtherReal>
  void SparseVector<Real>::AddToVec(Real alpha,
                                    VectorBase<OtherReal> *vec) const {
    KALDI_ASSERT(vec->Dim() == dim_);
    OtherReal *other_data = vec->Data();
    typename std::vector<std::pair<MatrixIndexT, Real> >::const_iterator
        iter = pairs_.begin(), end = pairs_.end();
    if (alpha == 1.0) {  // treat alpha==1.0 case specially.
      for (; iter != end; ++iter)
        other_data[iter->first] += iter->second;
    } else {
      for (; iter != end; ++iter)
        other_data[iter->first] += alpha * iter->second;
    }
  }
  
  template
  void SparseVector<float>::AddToVec(float alpha, VectorBase<float> *vec) const;
  template
  void SparseVector<float>::AddToVec(float alpha, VectorBase<double> *vec) const;
  template
  void SparseVector<double>::AddToVec(double alpha, VectorBase<float> *vec) const;
  template
  void SparseVector<double>::AddToVec(double alpha,
                                      VectorBase<double> *vec) const;
  
  template <typename Real>
  template <typename OtherReal>
  void SparseVector<Real>::CopyFromSvec(const SparseVector<OtherReal> &other) {
    dim_ = other.Dim();
    pairs_.clear();
    if (dim_ == 0) return;
    for (int32 i = 0; i < other.NumElements(); ++i) {
      pairs_.push_back(std::make_pair(
          other.GetElement(i).first,
          static_cast<Real>(other.GetElement(i).second)));
    }
  }
  template
  void SparseVector<float>::CopyFromSvec(const SparseVector<float> &svec);
  template
  void SparseVector<float>::CopyFromSvec(const SparseVector<double> &svec);
  template
  void SparseVector<double>::CopyFromSvec(const SparseVector<float> &svec);
  template
  void SparseVector<double>::CopyFromSvec(const SparseVector<double> &svec);
  
  
  template <typename Real>
  SparseVector<Real>& SparseVector<Real>::operator = (
      const SparseVector<Real> &other) {
    this->CopyFromSvec(other);
    dim_ = other.dim_;
    pairs_ = other.pairs_;
    return *this;
  }
  
  template <typename Real>
  void SparseVector<Real>::Swap(SparseVector<Real> *other) {
    pairs_.swap(other->pairs_);
    std::swap(dim_, other->dim_);
  }
  
  template <typename Real>
  void SparseVector<Real>::Write(std::ostream &os, bool binary) const {
    if (binary) {
      WriteToken(os, binary, "SV");
      WriteBasicType(os, binary, dim_);
      MatrixIndexT num_elems = pairs_.size();
      WriteBasicType(os, binary, num_elems);
      typename std::vector<std::pair<MatrixIndexT, Real> >::const_iterator
          iter = pairs_.begin(), end = pairs_.end();
      for (; iter != end; ++iter) {
        WriteBasicType(os, binary, iter->first);
        WriteBasicType(os, binary, iter->second);
      }
    } else {
      // In text-mode, use a human-friendly, script-friendly format;
      // format is "dim=5 [ dim=5 0 0.2 3 0.9 ] "
      os << "dim=" << dim_ << " [ ";
      typename std::vector<std::pair<MatrixIndexT, Real> >::const_iterator
          iter = pairs_.begin(), end = pairs_.end();
      for (; iter != end; ++iter)
        os << iter->first << ' ' << iter->second << ' ';
      os << "] ";
    }
  }
  
  
  template <typename Real>
  void SparseVector<Real>::Read(std::istream &is, bool binary) {
    if (binary) {
      ExpectToken(is, binary, "SV");
      ReadBasicType(is, binary, &dim_);
      KALDI_ASSERT(dim_ >= 0);
      int32 num_elems;
      ReadBasicType(is, binary, &num_elems);
      KALDI_ASSERT(num_elems >= 0 && num_elems <= dim_);
      pairs_.resize(num_elems);
      typename std::vector<std::pair<MatrixIndexT, Real> >::iterator
          iter = pairs_.begin(), end = pairs_.end();
      for (; iter != end; ++iter) {
        ReadBasicType(is, binary, &(iter->first));
        ReadBasicType(is, binary, &(iter->second));
      }
    } else {
      // In text-mode, format is "dim=5 [ 0 0.2 3 0.9 ]
      std::string str;
      is >> str;
      if (str.substr(0, 4) != "dim=")
        KALDI_ERR << "Reading sparse vector, expected 'dim=xxx', got " << str;
      std::string dim_str = str.substr(4, std::string::npos);
      std::istringstream dim_istr(dim_str);
      int32 dim = -1;
      dim_istr >> dim;
      if (dim < 0 || dim_istr.fail()) {
        KALDI_ERR << "Reading sparse vector, expected 'dim=[int]', got " << str;
      }
      dim_ = dim;
      is >> std::ws;
      is >> str;
      if (str != "[")
        KALDI_ERR << "Reading sparse vector, expected '[', got " << str;
      pairs_.clear();
      while (1) {
        is >> std::ws;
        if (is.peek() == ']') {
          is.get();
          break;
        }
        MatrixIndexT i;
        BaseFloat p;
        is >> i >> p;
        if (is.fail())
          KALDI_ERR << "Error reading sparse vector, expecting numbers.";
        KALDI_ASSERT(i >= 0 && i < dim
                     && (pairs_.empty() || i > pairs_.back().first));
        pairs_.push_back(std::pair<MatrixIndexT, BaseFloat>(i, p));
      }
    }
  }
  
  
  namespace sparse_vector_utils {
  template <typename Real>
  struct CompareFirst {
    inline bool operator() (const std::pair<MatrixIndexT, Real> &p1,
                             const std::pair<MatrixIndexT, Real> &p2) const {
      return p1.first < p2.first;
    }
  };
  }
  
  template <typename Real>
  SparseVector<Real>::SparseVector(
      MatrixIndexT dim, const std::vector<std::pair<MatrixIndexT, Real> > &pairs):
      dim_(dim),
      pairs_(pairs) {
    std::sort(pairs_.begin(), pairs_.end(),
              sparse_vector_utils::CompareFirst<Real>());
    typename std::vector<std::pair<MatrixIndexT, Real> >::iterator
        out = pairs_.begin(), in = out,  end = pairs_.end();
    // special case: while there is nothing to be changed, skip over
    // initial input (avoids unnecessary copying).
    while (in + 1 < end && in[0].first != in[1].first && in[0].second != 0.0) {
      in++;
      out++;
    }
    while (in < end) {
      // We reach this point only at the first element of
      // each stretch of identical .first elements.
      *out = *in;
      ++in;
      while (in < end && in->first == out->first) {
        out->second += in->second;  // this is the merge operation.
        ++in;
      }
      if (out->second != Real(0.0))  // Don't keep zero elements.
        out++;
    }
    pairs_.erase(out, end);
    if (!pairs_.empty()) {
      // range check.
      KALDI_ASSERT(pairs_.front().first >= 0 && pairs_.back().first < dim_);
    }
  }
  
  template <typename Real>
  void SparseVector<Real>::SetRandn(BaseFloat zero_prob) {
    pairs_.clear();
    KALDI_ASSERT(zero_prob >= 0 && zero_prob <= 1.0);
    for (MatrixIndexT i = 0; i < dim_; i++)
      if (WithProb(1.0 - zero_prob))
        pairs_.push_back(std::pair<MatrixIndexT, Real>(i, RandGauss()));
  }
  
  template <typename Real>
  void SparseVector<Real>::Resize(MatrixIndexT dim,
                                  MatrixResizeType resize_type) {
    if (resize_type != kCopyData || dim == 0)
      pairs_.clear();
    KALDI_ASSERT(dim >= 0);
    if (dim < dim_ && resize_type == kCopyData)
      while (!pairs_.empty() && pairs_.back().first >= dim)
        pairs_.pop_back();
    dim_ = dim;
  }
  
  template <typename Real>
  MatrixIndexT SparseMatrix<Real>::NumRows() const {
    return rows_.size();
  }
  
  template <typename Real>
  MatrixIndexT SparseMatrix<Real>::NumCols() const {
    if (rows_.empty())
      return 0.0;
    else
      return rows_[0].Dim();
  }
  
  template <typename Real>
  MatrixIndexT SparseMatrix<Real>::NumElements() const {
    int32 num_elements = 0;
    for (int32 i = 0; i < rows_.size(); ++i) {
      num_elements += rows_[i].NumElements();
    }
    return num_elements;
  }
  
  template <typename Real>
  SparseVector<Real>* SparseMatrix<Real>::Data() {
    if (rows_.empty())
      return NULL;
    else
      return rows_.data();
  }
  
  template <typename Real>
  const SparseVector<Real>* SparseMatrix<Real>::Data() const {
    if (rows_.empty())
      return NULL;
    else
      return rows_.data();
  }
  
  template <typename Real>
  Real SparseMatrix<Real>::Sum() const {
    Real sum = 0;
    for (int32 i = 0; i < rows_.size(); ++i) {
      sum += rows_[i].Sum();
    }
    return sum;
  }
  
  template<typename Real>
  Real SparseMatrix<Real>::FrobeniusNorm() const {
    Real squared_sum = 0;
    for (int32 i = 0; i < rows_.size(); ++i) {
      const std::pair<MatrixIndexT, Real> *row_data = rows_[i].Data();
      for (int32 j = 0; j < rows_[i].NumElements(); ++j) {
        squared_sum += row_data[j].second * row_data[j].second;
      }
    }
    return std::sqrt(squared_sum);
  }
  
  template <typename Real>
  template <typename OtherReal>
  void SparseMatrix<Real>::CopyToMat(MatrixBase<OtherReal> *other,
                                     MatrixTransposeType trans) const {
    if (trans == kNoTrans) {
      MatrixIndexT num_rows = rows_.size();
      KALDI_ASSERT(other->NumRows() == num_rows);
      for (MatrixIndexT i = 0; i < num_rows; i++) {
        SubVector<OtherReal> vec(*other, i);
        rows_[i].CopyElementsToVec(&vec);
      }
    } else {
      OtherReal *other_col_data = other->Data();
      MatrixIndexT other_stride = other->Stride(),
          num_rows = NumRows(), num_cols = NumCols();
      KALDI_ASSERT(num_rows == other->NumCols() && num_cols == other->NumRows());
      other->SetZero();
      for (MatrixIndexT row = 0; row < num_rows; row++, other_col_data++) {
        const SparseVector<Real> &svec = rows_[row];
        MatrixIndexT num_elems = svec.NumElements();
        const std::pair<MatrixIndexT, Real> *sdata = svec.Data();
        for (MatrixIndexT e = 0; e < num_elems; e++)
          other_col_data[sdata[e].first * other_stride] = sdata[e].second;
      }
    }
  }
  
  template
  void SparseMatrix<float>::CopyToMat(MatrixBase<float> *other,
                                      MatrixTransposeType trans) const;
  template
  void SparseMatrix<float>::CopyToMat(MatrixBase<double> *other,
                                      MatrixTransposeType trans) const;
  template
  void SparseMatrix<double>::CopyToMat(MatrixBase<float> *other,
                                      MatrixTransposeType trans) const;
  template
  void SparseMatrix<double>::CopyToMat(MatrixBase<double> *other,
                                      MatrixTransposeType trans) const;
  
  template <typename Real>
  void SparseMatrix<Real>::CopyElementsToVec(VectorBase<Real> *other) const {
    KALDI_ASSERT(other->Dim() == NumElements());
    Real *dst_data = other->Data();
    int32 dst_index = 0;
    for (int32 i = 0; i < rows_.size(); ++i) {
      for (int32 j = 0; j < rows_[i].NumElements(); ++j) {
        dst_data[dst_index] =
            static_cast<Real>(rows_[i].GetElement(j).second);
        dst_index++;
      }
    }
  }
  
  template <typename Real>
  template <typename OtherReal>
  void SparseMatrix<Real>::CopyFromSmat(const SparseMatrix<OtherReal> &other) {
    rows_.resize(other.NumRows());
    if (rows_.size() == 0) return;
    for (int32 r = 0; r < rows_.size(); ++r) {
      rows_[r].CopyFromSvec(other.Row(r));
    }
  }
  template
  void SparseMatrix<float>::CopyFromSmat(const SparseMatrix<float> &other);
  template
  void SparseMatrix<float>::CopyFromSmat(const SparseMatrix<double> &other);
  template
  void SparseMatrix<double>::CopyFromSmat(const SparseMatrix<float> &other);
  template
  void SparseMatrix<double>::CopyFromSmat(const SparseMatrix<double> &other);
  
  template <typename Real>
  void SparseMatrix<Real>::Write(std::ostream &os, bool binary) const {
    if (binary) {
      // Note: we can use the same marker for float and double SparseMatrix,
      // because internally we use WriteBasicType and ReadBasicType to read the
      // floats and doubles, and this will automatically take care of type
      // conversion.
      WriteToken(os, binary, "SM");
      int32 num_rows = rows_.size();
      WriteBasicType(os, binary, num_rows);
      for (int32 row = 0; row < num_rows; row++)
        rows_[row].Write(os, binary);
    } else {
      // The format is "rows=10 dim=20 [ 1 0.4  9 1.2 ] dim=20 [ 3 1.7 19 0.6 ] ..
      // not 100% efficient, but easy to work with, and we can re-use the
      // read/write code from SparseVector.
      int32 num_rows = rows_.size();
      os << "rows=" << num_rows << " ";
      for (int32 row = 0; row < num_rows; row++)
        rows_[row].Write(os, binary);
      os << "\n";  // Might make it a little more readable.
    }
  }
  
  template <typename Real>
  void SparseMatrix<Real>::Read(std::istream &is, bool binary) {
    if (binary) {
      ExpectToken(is, binary, "SM");
      int32 num_rows;
      ReadBasicType(is, binary, &num_rows);
      KALDI_ASSERT(num_rows >= 0 && num_rows < 10000000);
      rows_.resize(num_rows);
      for (int32 row = 0; row < num_rows; row++)
        rows_[row].Read(is, binary);
    } else {
      std::string str;
      is >> str;
      if (str.substr(0, 5) != "rows=")
        KALDI_ERR << "Reading sparse matrix, expected 'rows=xxx', got " << str;
      std::string rows_str = str.substr(5, std::string::npos);
      std::istringstream rows_istr(rows_str);
      int32 num_rows = -1;
      rows_istr >> num_rows;
      if (num_rows < 0 || rows_istr.fail()) {
        KALDI_ERR << "Reading sparse vector, expected 'rows=[int]', got " << str;
      }
      rows_.resize(num_rows);
      for (int32 row = 0; row < num_rows; row++)
        rows_[row].Read(is, binary);
    }
  }
  
  
  template <typename Real>
  void SparseMatrix<Real>::AddToMat(BaseFloat alpha,
                                    MatrixBase<Real> *other,
                                    MatrixTransposeType trans) const {
    if (trans == kNoTrans) {
      MatrixIndexT num_rows = rows_.size();
      KALDI_ASSERT(other->NumRows() == num_rows);
      for (MatrixIndexT i = 0; i < num_rows; i++) {
        SubVector<Real> vec(*other, i);
        rows_[i].AddToVec(alpha, &vec);
      }
    } else {
      Real *other_col_data = other->Data();
      MatrixIndexT other_stride = other->Stride(),
          num_rows = NumRows(), num_cols = NumCols();
      KALDI_ASSERT(num_rows == other->NumCols() && num_cols == other->NumRows());
      for (MatrixIndexT row = 0; row < num_rows; row++, other_col_data++) {
        const SparseVector<Real> &svec = rows_[row];
        MatrixIndexT num_elems = svec.NumElements();
        const std::pair<MatrixIndexT, Real> *sdata = svec.Data();
        for (MatrixIndexT e = 0; e < num_elems; e++)
          other_col_data[sdata[e].first * other_stride] +=
              alpha * sdata[e].second;
      }
    }
  }
  
  template <typename Real>
  Real VecSvec(const VectorBase<Real> &vec,
               const SparseVector<Real> &svec) {
    KALDI_ASSERT(vec.Dim() == svec.Dim());
    MatrixIndexT n = svec.NumElements();
    const std::pair<MatrixIndexT, Real> *sdata = svec.Data();
    const Real *data = vec.Data();
    Real ans = 0.0;
    for (MatrixIndexT i = 0; i < n; i++)
      ans += data[sdata[i].first] * sdata[i].second;
    return ans;
  }
  
  template
  float VecSvec(const VectorBase<float> &vec,
                const SparseVector<float> &svec);
  template
  double VecSvec(const VectorBase<double> &vec,
                const SparseVector<double> &svec);
  
  template <typename Real>
  const SparseVector<Real> &SparseMatrix<Real>::Row(MatrixIndexT r) const {
    KALDI_ASSERT(static_cast<size_t>(r) < rows_.size());
    return rows_[r];
  }
  
  template <typename Real>
  void SparseMatrix<Real>::SetRow(int32 r, const SparseVector<Real> &vec) {
    KALDI_ASSERT(static_cast<size_t>(r) < rows_.size() &&
                 vec.Dim() == rows_[0].Dim());
    rows_[r] = vec;
  }
  
  template <typename Real>
  SparseMatrix<Real>& SparseMatrix<Real>::operator = (
      const SparseMatrix<Real> &other) {
    rows_ = other.rows_;
    return *this;
  }
  
  template <typename Real>
  void SparseMatrix<Real>::Swap(SparseMatrix<Real> *other) {
    rows_.swap(other->rows_);
  }
  
  template<typename Real>
  SparseMatrix<Real>::SparseMatrix(
      MatrixIndexT dim,
      const std::vector<std::vector<std::pair<MatrixIndexT, Real> > > &pairs):
      rows_(pairs.size()) {
    MatrixIndexT num_rows = pairs.size();
    for (MatrixIndexT row = 0; row < num_rows; row++) {
      SparseVector<Real> svec(dim, pairs[row]);
      rows_[row].Swap(&svec);
    }
  }
  
  template <typename Real>
  void SparseMatrix<Real>::SetRandn(BaseFloat zero_prob) {
    MatrixIndexT num_rows = rows_.size();
    for (MatrixIndexT row = 0; row < num_rows; row++)
      rows_[row].SetRandn(zero_prob);
  }
  
  template <typename Real>
  void SparseMatrix<Real>::Resize(MatrixIndexT num_rows,
                                  MatrixIndexT num_cols,
                                  MatrixResizeType resize_type) {
    KALDI_ASSERT(num_rows >= 0 && num_cols >= 0);
    if (resize_type == kSetZero || resize_type == kUndefined) {
      rows_.clear();
      Resize(num_rows, num_cols, kCopyData);
    } else {
      // Assume resize_type == kCopyData from here.
      int32 old_num_rows = rows_.size(), old_num_cols = NumCols();
      SparseVector<Real> initializer(num_cols);
      rows_.resize(num_rows, initializer);
      if (num_cols != old_num_cols)
        for (int32 row = 0; row < old_num_rows; row++)
          rows_[row].Resize(num_cols, kCopyData);
    }
  }
  
  template <typename Real>
  void SparseMatrix<Real>::AppendSparseMatrixRows(
      std::vector<SparseMatrix<Real> > *inputs) {
    rows_.clear();
    size_t num_rows = 0;
    typename std::vector<SparseMatrix<Real> >::iterator
        input_iter = inputs->begin(),
        input_end = inputs->end();
    for (; input_iter != input_end; ++input_iter)
      num_rows += input_iter->rows_.size();
    rows_.resize(num_rows);
    typename std::vector<SparseVector<Real> >::iterator
        row_iter = rows_.begin(),
        row_end = rows_.end();
    for (input_iter = inputs->begin(); input_iter != input_end; ++input_iter) {
      typename std::vector<SparseVector<Real> >::iterator
          input_row_iter = input_iter->rows_.begin(),
          input_row_end = input_iter->rows_.end();
      for (; input_row_iter != input_row_end; ++input_row_iter, ++row_iter)
        row_iter->Swap(&(*input_row_iter));
    }
    KALDI_ASSERT(row_iter == row_end);
    int32 num_cols = NumCols();
    for (row_iter = rows_.begin(); row_iter != row_end; ++row_iter) {
      if (row_iter->Dim() != num_cols)
        KALDI_ERR << "Appending rows with inconsistent dimensions, "
                  << row_iter->Dim() << " vs. " << num_cols;
    }
    inputs->clear();
  }
  
  template<typename Real>
  void SparseMatrix<Real>::Scale(Real alpha) {
    MatrixIndexT num_rows = rows_.size();
    for (MatrixIndexT row = 0; row < num_rows; row++)
      rows_[row].Scale(alpha);
  }
  
  template<typename Real>
  Real TraceMatSmat(const MatrixBase<Real> &A,
                    const SparseMatrix<Real> &B,
                    MatrixTransposeType trans) {
    Real sum = 0.0;
    if (trans == kTrans) {
      MatrixIndexT num_rows = A.NumRows();
      KALDI_ASSERT(B.NumRows() == num_rows);
      for (MatrixIndexT r = 0; r < num_rows; r++)
        sum += VecSvec(A.Row(r), B.Row(r));
    } else {
      const Real *A_col_data = A.Data();
      MatrixIndexT Astride = A.Stride(), Acols = A.NumCols(), Arows = A.NumRows();
      KALDI_ASSERT(Arows == B.NumCols() && Acols == B.NumRows());
      sum = 0.0;
      for (MatrixIndexT i = 0; i < Acols; i++, A_col_data++) {
        Real col_sum = 0.0;
        const SparseVector<Real> &svec = B.Row(i);
        MatrixIndexT num_elems = svec.NumElements();
        const std::pair<MatrixIndexT, Real> *sdata = svec.Data();
        for (MatrixIndexT e = 0; e < num_elems; e++)
          col_sum += A_col_data[Astride * sdata[e].first] * sdata[e].second;
        sum += col_sum;
      }
    }
    return sum;
  }
  
  template
  float TraceMatSmat(const MatrixBase<float> &A,
                     const SparseMatrix<float> &B,
                     MatrixTransposeType trans);
  template
  double TraceMatSmat(const MatrixBase<double> &A,
                     const SparseMatrix<double> &B,
                     MatrixTransposeType trans);
  
  void GeneralMatrix::Clear() {
    mat_.Resize(0, 0);
    cmat_.Clear();
    smat_.Resize(0, 0);
  }
  
  GeneralMatrix& GeneralMatrix::operator= (const MatrixBase<BaseFloat> &mat) {
    Clear();
    mat_ = mat;
    return *this;
  }
  
  GeneralMatrix& GeneralMatrix::operator= (const CompressedMatrix &cmat) {
    Clear();
    cmat_ = cmat;
    return *this;
  }
  
  GeneralMatrix& GeneralMatrix::operator= (const SparseMatrix<BaseFloat> &smat) {
    Clear();
    smat_ = smat;
    return *this;
  }
  
  GeneralMatrix& GeneralMatrix::operator= (const GeneralMatrix &gmat) {
    mat_ = gmat.mat_;
    smat_ = gmat.smat_;
    cmat_ = gmat.cmat_;
    return *this;
  }
  
  
  GeneralMatrixType GeneralMatrix::Type() const {
    if (smat_.NumRows() != 0)
      return kSparseMatrix;
    else if (cmat_.NumRows() != 0)
      return kCompressedMatrix;
    else
      return kFullMatrix;
  }
  
  MatrixIndexT GeneralMatrix::NumRows() const {
    MatrixIndexT r = smat_.NumRows();
    if (r != 0)
      return r;
    r = cmat_.NumRows();
    if (r != 0)
      return r;
    return mat_.NumRows();
  }
  
  MatrixIndexT GeneralMatrix::NumCols() const {
    MatrixIndexT r = smat_.NumCols();
    if (r != 0)
      return r;
    r = cmat_.NumCols();
    if (r != 0)
      return r;
    return mat_.NumCols();
  }
  
  
  void GeneralMatrix::Compress() {
    if (mat_.NumRows() != 0) {
      cmat_.CopyFromMat(mat_);
      mat_.Resize(0, 0);
    }
  }
  
  void GeneralMatrix::Uncompress() {
    if (cmat_.NumRows() != 0) {
      cmat_.CopyToMat(&mat_);
      cmat_.Clear();
    }
  }
  
  void GeneralMatrix::GetMatrix(Matrix<BaseFloat> *mat) const {
    if (mat_.NumRows() !=0) {
      *mat = mat_;
    } else if (cmat_.NumRows() != 0) {
      mat->Resize(cmat_.NumRows(), cmat_.NumCols(), kUndefined);
      cmat_.CopyToMat(mat);
    } else if (smat_.NumRows() != 0) {
      mat->Resize(smat_.NumRows(), smat_.NumCols(), kUndefined);
      smat_.CopyToMat(mat);
    } else {
      mat->Resize(0, 0);
    }
  }
  
  void GeneralMatrix::CopyToMat(MatrixBase<BaseFloat> *mat,
                                MatrixTransposeType trans) const {
    if (mat_.NumRows() !=0) {
      mat->CopyFromMat(mat_, trans);
    } else if (cmat_.NumRows() != 0) {
      cmat_.CopyToMat(mat, trans);
    } else if (smat_.NumRows() != 0) {
      smat_.CopyToMat(mat, trans);
    } else {
      KALDI_ASSERT(mat->NumRows() == 0);
    }
  }
  
  void GeneralMatrix::Scale(BaseFloat alpha) {
    if (mat_.NumRows() != 0) {
      mat_.Scale(alpha);
    } else if (cmat_.NumRows() != 0) {
      cmat_.Scale(alpha);
    } else if (smat_.NumRows() != 0) {
      smat_.Scale(alpha);
    }
  
  }
  const SparseMatrix<BaseFloat>& GeneralMatrix::GetSparseMatrix() const {
    if (mat_.NumRows() != 0 || cmat_.NumRows() != 0)
      KALDI_ERR << "GetSparseMatrix called on GeneralMatrix of wrong type.";
    return smat_;
  }
  
  void GeneralMatrix::SwapSparseMatrix(SparseMatrix<BaseFloat> *smat) {
    if (mat_.NumRows() != 0 || cmat_.NumRows() != 0)
      KALDI_ERR << "GetSparseMatrix called on GeneralMatrix of wrong type.";
    smat->Swap(&smat_);
  }
  
  void GeneralMatrix::SwapCompressedMatrix(CompressedMatrix *cmat) {
    if (mat_.NumRows() != 0 || smat_.NumRows() != 0)
      KALDI_ERR << "GetSparseMatrix called on GeneralMatrix of wrong type.";
    cmat->Swap(&cmat_);
  }
  
  const CompressedMatrix &GeneralMatrix::GetCompressedMatrix() const {
    if (mat_.NumRows() != 0 || smat_.NumRows() != 0)
      KALDI_ERR << "GetCompressedMatrix called on GeneralMatrix of wrong type.";
    return cmat_;
  }
  
  const Matrix<BaseFloat> &GeneralMatrix::GetFullMatrix() const {
    if (smat_.NumRows() != 0 || cmat_.NumRows() != 0)
      KALDI_ERR << "GetFullMatrix called on GeneralMatrix of wrong type.";
    return mat_;
  }
  
  
  void GeneralMatrix::SwapFullMatrix(Matrix<BaseFloat> *mat) {
    if (cmat_.NumRows() != 0 || smat_.NumRows() != 0)
      KALDI_ERR << "SwapMatrix called on GeneralMatrix of wrong type.";
    mat->Swap(&mat_);
  }
  
  void GeneralMatrix::Write(std::ostream &os, bool binary) const {
    if (smat_.NumRows() != 0) {
      smat_.Write(os, binary);
    } else if (cmat_.NumRows() != 0) {
      cmat_.Write(os, binary);
    } else {
      mat_.Write(os, binary);
    }
  }
  
  void GeneralMatrix::Read(std::istream &is, bool binary) {
    Clear();
    if (binary) {
      int peekval = is.peek();
      if (peekval == 'C') {
        // Token CM for compressed matrix
        cmat_.Read(is, binary);
      } else if (peekval == 'S') {
        // Token SM for sparse matrix
        smat_.Read(is, binary);
      } else {
        mat_.Read(is, binary);
      }
    } else {
      // note: in text mode we will only ever read regular
      // or sparse matrices, because the compressed-matrix format just
      // gets written as a regular matrix in text mode.
      is >> std::ws;  // Eat up white space.
      int peekval = is.peek();
      if (peekval == 'r') {  // sparse format starts rows=[int].
        smat_.Read(is, binary);
      } else {
        mat_.Read(is, binary);
      }
    }
  }
  
  
  void AppendGeneralMatrixRows(const std::vector<const GeneralMatrix *> &src,
                               GeneralMatrix *mat) {
    mat->Clear();
    int32 size = src.size();
    if (size == 0)
      return;
    bool all_sparse = true;
    for (int32 i = 0; i < size; i++) {
      if (src[i]->Type() != kSparseMatrix && src[i]->NumRows() != 0) {
        all_sparse = false;
        break;
      }
    }
    if (all_sparse) {
      std::vector<SparseMatrix<BaseFloat> > sparse_mats(size);
      for (int32 i = 0; i < size; i++)
        sparse_mats[i] = src[i]->GetSparseMatrix();
      SparseMatrix<BaseFloat> appended_mat;
      appended_mat.AppendSparseMatrixRows(&sparse_mats);
      mat->SwapSparseMatrix(&appended_mat);
    } else {
      int32 tot_rows = 0, num_cols = -1;
      for (int32 i = 0; i < size; i++) {
        const GeneralMatrix &src_mat = *(src[i]);
        int32 src_rows = src_mat.NumRows(), src_cols = src_mat.NumCols();
        if (src_rows != 0) {
          tot_rows += src_rows;
          if (num_cols == -1) num_cols = src_cols;
          else if (num_cols != src_cols)
            KALDI_ERR << "Appending rows of matrices with inconsistent num-cols: "
                      << num_cols << " vs. " << src_cols;
        }
      }
      Matrix<BaseFloat> appended_mat(tot_rows, num_cols, kUndefined);
      int32 row_offset = 0;
      for (int32 i = 0; i < size; i++) {
        const GeneralMatrix &src_mat = *(src[i]);
        int32 src_rows = src_mat.NumRows();
        if (src_rows != 0) {
          SubMatrix<BaseFloat> dest_submat(appended_mat, row_offset, src_rows,
                                           0, num_cols);
          src_mat.CopyToMat(&dest_submat);
          row_offset += src_rows;
        }
      }
      KALDI_ASSERT(row_offset == tot_rows);
      mat->SwapFullMatrix(&appended_mat);
    }
  }
  
  void FilterCompressedMatrixRows(const CompressedMatrix &in,
                                  const std::vector<bool> &keep_rows,
                                  Matrix<BaseFloat> *out) {
    KALDI_ASSERT(keep_rows.size() == static_cast<size_t>(in.NumRows()));
    int32 num_kept_rows = 0;
    std::vector<bool>::const_iterator iter = keep_rows.begin(),
                                       end = keep_rows.end();
    for (; iter != end; ++iter)
      if (*iter)
        num_kept_rows++;
    if (num_kept_rows == 0)
      KALDI_ERR << "No kept rows";
    if (num_kept_rows == static_cast<int32>(keep_rows.size())) {
      out->Resize(in.NumRows(), in.NumCols(), kUndefined);
      in.CopyToMat(out);
      return;
    }
    const BaseFloat heuristic = 0.33;
    // should be > 0 and < 1.0.  represents the performance hit we get from
    // iterating row-wise versus column-wise in compressed-matrix uncompression.
  
    if (num_kept_rows > heuristic * in.NumRows()) {
      // if quite a few of the the rows are kept, it may be more efficient
      // to uncompress the entire compressed matrix, since per-column operation
      // is more efficient.
      Matrix<BaseFloat> full_mat(in);
      FilterMatrixRows(full_mat, keep_rows, out);
    } else {
      out->Resize(num_kept_rows, in.NumCols(), kUndefined);
  
      iter = keep_rows.begin();
      int32 out_row = 0;
      for (int32 in_row = 0; iter != end; ++iter, ++in_row) {
        if (*iter) {
          SubVector<BaseFloat> dest(*out, out_row);
          in.CopyRowToVec(in_row, &dest);
          out_row++;
        }
      }
      KALDI_ASSERT(out_row == num_kept_rows);
    }
  }
  
  template <typename Real>
  void FilterMatrixRows(const Matrix<Real> &in,
                        const std::vector<bool> &keep_rows,
                        Matrix<Real> *out) {
    KALDI_ASSERT(keep_rows.size() == static_cast<size_t>(in.NumRows()));
    int32 num_kept_rows = 0;
    std::vector<bool>::const_iterator iter = keep_rows.begin(),
                                       end = keep_rows.end();
    for (; iter != end; ++iter)
      if (*iter)
        num_kept_rows++;
    if (num_kept_rows == 0)
      KALDI_ERR << "No kept rows";
    if (num_kept_rows == static_cast<int32>(keep_rows.size())) {
      *out = in;
      return;
    }
    out->Resize(num_kept_rows, in.NumCols(), kUndefined);
    iter = keep_rows.begin();
    int32 out_row = 0;
    for (int32 in_row = 0; iter != end; ++iter, ++in_row) {
      if (*iter) {
        SubVector<Real> src(in, in_row);
        SubVector<Real> dest(*out, out_row);
        dest.CopyFromVec(src);
        out_row++;
      }
    }
    KALDI_ASSERT(out_row == num_kept_rows);
  }
  
  template
  void FilterMatrixRows(const Matrix<float> &in,
                        const std::vector<bool> &keep_rows,
                        Matrix<float> *out);
  template
  void FilterMatrixRows(const Matrix<double> &in,
                        const std::vector<bool> &keep_rows,
                        Matrix<double> *out);
  
  template <typename Real>
  void FilterSparseMatrixRows(const SparseMatrix<Real> &in,
                              const std::vector<bool> &keep_rows,
                              SparseMatrix<Real> *out) {
    KALDI_ASSERT(keep_rows.size() == static_cast<size_t>(in.NumRows()));
    int32 num_kept_rows = 0;
    std::vector<bool>::const_iterator iter = keep_rows.begin(),
                                       end = keep_rows.end();
    for (; iter != end; ++iter)
      if (*iter)
        num_kept_rows++;
    if (num_kept_rows == 0)
      KALDI_ERR << "No kept rows";
    if (num_kept_rows == static_cast<int32>(keep_rows.size())) {
      *out = in;
      return;
    }
    out->Resize(num_kept_rows, in.NumCols(), kUndefined);
    iter = keep_rows.begin();
    int32 out_row = 0;
    for (int32 in_row = 0; iter != end; ++iter, ++in_row) {
      if (*iter) {
        out->SetRow(out_row, in.Row(in_row));
        out_row++;
      }
    }
    KALDI_ASSERT(out_row == num_kept_rows);
  }
  
  template
  void FilterSparseMatrixRows(const SparseMatrix<float> &in,
                              const std::vector<bool> &keep_rows,
                              SparseMatrix<float> *out);
  template
  void FilterSparseMatrixRows(const SparseMatrix<double> &in,
                              const std::vector<bool> &keep_rows,
                              SparseMatrix<double> *out);
  
  
  void FilterGeneralMatrixRows(const GeneralMatrix &in,
                               const std::vector<bool> &keep_rows,
                               GeneralMatrix *out) {
    out->Clear();
    KALDI_ASSERT(keep_rows.size() == static_cast<size_t>(in.NumRows()));
    int32 num_kept_rows = 0;
    std::vector<bool>::const_iterator iter = keep_rows.begin(),
                                       end = keep_rows.end();
    for (; iter != end; ++iter)
      if (*iter)
        num_kept_rows++;
    if (num_kept_rows == 0)
      KALDI_ERR << "No kept rows";
    if (num_kept_rows == static_cast<int32>(keep_rows.size())) {
      *out = in;
      return;
    }
    switch (in.Type()) {
      case kCompressedMatrix: {
        const CompressedMatrix &cmat = in.GetCompressedMatrix();
        Matrix<BaseFloat> full_mat;
        FilterCompressedMatrixRows(cmat, keep_rows, &full_mat);
        out->SwapFullMatrix(&full_mat);
        return;
      }
      case kSparseMatrix: {
        const SparseMatrix<BaseFloat> &smat = in.GetSparseMatrix();
        SparseMatrix<BaseFloat> smat_out;
        FilterSparseMatrixRows(smat, keep_rows, &smat_out);
        out->SwapSparseMatrix(&smat_out);
        return;
      }
      case kFullMatrix: {
        const Matrix<BaseFloat> &full_mat = in.GetFullMatrix();
        Matrix<BaseFloat> full_mat_out;
        FilterMatrixRows(full_mat, keep_rows, &full_mat_out);
        out->SwapFullMatrix(&full_mat_out);
        return;
      }
      default:
        KALDI_ERR << "Invalid general-matrix type.";
    }
  }
  
  void GeneralMatrix::AddToMat(BaseFloat alpha, MatrixBase<BaseFloat> *mat,
                               MatrixTransposeType trans) const {
    switch (this->Type()) {
      case kFullMatrix: {
        mat->AddMat(alpha, mat_, trans);
        break;
      }
      case kSparseMatrix: {
        smat_.AddToMat(alpha, mat, trans);
        break;
      }
      case kCompressedMatrix: {
        Matrix<BaseFloat> temp_mat(cmat_);
        mat->AddMat(alpha, temp_mat, trans);
        break;
      }
      default:
        KALDI_ERR << "Invalid general-matrix type.";
    }
  }
  
  template <class Real>
  Real SparseVector<Real>::Max(int32 *index_out) const {
    KALDI_ASSERT(dim_ > 0 && pairs_.size() <= static_cast<size_t>(dim_));
    Real ans = -std::numeric_limits<Real>::infinity();
    int32 index = 0;
    typename std::vector<std::pair<MatrixIndexT, Real> >::const_iterator
        iter = pairs_.begin(), end = pairs_.end();
    for (; iter != end; ++iter) {
      if (iter->second > ans) {
        ans = iter->second;
        index = iter->first;
      }
    }
    if (ans >= 0 || pairs_.size() == dim_) {
      // ans >= 0 will be the normal case.
      // if pairs_.size() == dim_ then we need to return
      // even a negative answer as there are no spaces (hence no unlisted zeros).
      *index_out = index;
      return ans;
    }
    // all the stored elements are < 0, but there are unlisted
    // elements -> pick the first unlisted element.
    // Note that this class requires that the indexes are sorted
    // and unique.
    index = 0;  // "index" will always be the next index, that
                // we haven't seen listed yet.
    iter = pairs_.begin();
    for (; iter != end; ++iter) {
      if (iter->first > index) {  // index "index" is not listed.
        *index_out = index;
        return 0.0;
      } else {
        // index is the next potential gap in the indexes.
        index = iter->first + 1;
      }
    }
    // we can reach here if either pairs_.empty(), or
    // pairs_ is nonempty but contains a sequence (0, 1, 2,...).
    if (!pairs_.empty())
      index = pairs_.back().first + 1;
    // else leave index at zero
    KALDI_ASSERT(index < dim_);
    *index_out = index;
    return 0.0;
  }
  
  void GeneralMatrix::Swap(GeneralMatrix *other) {
    mat_.Swap(&(other->mat_));
    cmat_.Swap(&(other->cmat_));
    smat_.Swap(&(other->smat_));
  }
  
  
  void ExtractRowRangeWithPadding(
      const GeneralMatrix &in,
      int32 row_offset,
      int32 num_rows,
      GeneralMatrix *out) {
    // make sure 'out' is empty to start with.
    Matrix<BaseFloat> empty_mat;
    *out = empty_mat;
    if (num_rows == 0) return;
    switch (in.Type()) {
      case kFullMatrix: {
        const Matrix<BaseFloat> &mat_in = in.GetFullMatrix();
        int32 num_rows_in = mat_in.NumRows(), num_cols = mat_in.NumCols();
        KALDI_ASSERT(num_rows_in > 0);  // we can't extract >0 rows from an empty
                                        // matrix.
        Matrix<BaseFloat> mat_out(num_rows, num_cols, kUndefined);
        for (int32 row = 0; row < num_rows; row++) {
          int32 row_in = row + row_offset;
          if (row_in < 0) row_in = 0;
          else if (row_in >= num_rows_in) row_in = num_rows_in - 1;
          SubVector<BaseFloat> vec_in(mat_in, row_in),
              vec_out(mat_out, row);
          vec_out.CopyFromVec(vec_in);
        }
        out->SwapFullMatrix(&mat_out);
        break;
      }
      case kSparseMatrix: {
        const SparseMatrix<BaseFloat> &smat_in = in.GetSparseMatrix();
        int32 num_rows_in = smat_in.NumRows(),
            num_cols = smat_in.NumCols();
        KALDI_ASSERT(num_rows_in > 0);  // we can't extract >0 rows from an empty
                                        // matrix.
        SparseMatrix<BaseFloat> smat_out(num_rows, num_cols);
        for (int32 row = 0; row < num_rows; row++) {
          int32 row_in = row + row_offset;
          if (row_in < 0) row_in = 0;
          else if (row_in >= num_rows_in) row_in = num_rows_in - 1;
          smat_out.SetRow(row, smat_in.Row(row_in));
        }
        out->SwapSparseMatrix(&smat_out);
        break;
      }
      case kCompressedMatrix: {
        const CompressedMatrix &cmat_in = in.GetCompressedMatrix();
        bool allow_padding = true;
        CompressedMatrix cmat_out(cmat_in, row_offset, num_rows,
                                  0, cmat_in.NumCols(), allow_padding);
        out->SwapCompressedMatrix(&cmat_out);
        break;
      }
      default:
        KALDI_ERR << "Bad matrix type.";
    }
  }
  
  
  template class SparseVector<float>;
  template class SparseVector<double>;
  template class SparseMatrix<float>;
  template class SparseMatrix<double>;
  
  }  // namespace kaldi
  