////////////////////////////////////////////////////////////////////////////////
// 
// StairCaseMatrix.cc
//
//    produced: 13/03/98 jr
// 
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <ctype.h>
#include <string.h>

#include "StairCaseMatrix.hh"

namespace topcom {

  void StairCaseMatrix::_eliminate(const parameter_type ridx, const parameter_type cidx, const parameter_type cno) {
    if (cno == 0) {
      return;
    }
    const parameter_type n = rowdim();
  
    if (is_zero((*this)(ridx,ridx))) {
#ifdef SUPER_VERBOSE
      MessageStreams::debug() << message::lock;
      MessageStreams::debug() << "eraser = " << (*this)(ridx,ridx) << " == FieldConstants::ZERO -> try colperm; row : " << ridx << ", col: " << ridx << std::endl;
      MessageStreams::debug() << message::unlock;
#endif
      for (parameter_type k = cidx; k < cidx + cno; ++k) {
	if (!is_zero((*this)(ridx,k))) {
	  Matrix::swap_cols(ridx,k);
	  _coefficient *= FieldConstants::MINUSONE;
#ifdef SUPER_VERBOSE
	  MessageStreams::debug() << message::lock;
	  MessageStreams::debug() << "eraser = " << (*this)(ridx,ridx) << " == FieldConstants::ZERO -> colperm successful; row : " << ridx << ", col: " << ridx << std::endl;
	  MessageStreams::debug() << message::unlock;
#endif
	  break;
	}
      }
      if (is_zero((*this)(ridx,ridx))) {
#ifdef SUPER_VERBOSE
	MessageStreams::debug() << message::lock;
	MessageStreams::debug() << "eraser = " << (*this)(ridx,ridx) << " == FieldConstants::ZERO -> colperm unsuccessful; row : " << ridx << ", col: " << ridx << std::endl;
	MessageStreams::debug() << message::unlock;
#endif
	return;
      }
#ifdef SUPER_VERBOSE
      else {
	MessageStreams::debug() << message::lock;
	MessageStreams::debug() << "eraser = " << (*this)(ridx,ridx) << " != FieldConstants::ZERO; row : " << ridx << ", col: " << ridx << std::endl;
	MessageStreams::debug() << message::unlock;
      }
#endif
    }
#ifdef SUPER_VERBOSE
    else {
      MessageStreams::debug() << message::lock;
      MessageStreams::debug() << "eraser = " << (*this)(ridx,ridx) << " != FieldConstants::ZERO; row : " << ridx << ", col: " << ridx << std::endl;
      MessageStreams::debug() << message::unlock;
    }
#endif
    const Field& eraser = (*this)(ridx,ridx);
#ifdef SUPER_VERBOSE
    MessageStreams::debug() << message::lock;
    MessageStreams::debug() << "eraser = " << eraser << "; row : " << ridx << ", col: " << ridx << std::endl;
    MessageStreams::debug() << message::unlock;
#endif
    for (parameter_type j = cidx; j < cidx + cno; ++j) {
      const Field& delinquent = (*this)(ridx,j);
#ifdef SUPER_VERBOSE
      MessageStreams::debug() << message::lock;
      MessageStreams::debug() << "delinquent = " << delinquent << "; row : " << ridx << ", col: " << j << std::endl;
      MessageStreams::debug() << message::unlock;
#endif
      if (is_zero(delinquent)) {
	continue;
      }
      // An alternative division-free elimination step (slightly slower for large numbers):
      // if (eraser < FieldConstants::ZERO) {
      //   _coefficient /= -eraser;
      //   for (parameter_type k = ridx + 1; k < n; ++k) {
      // 	(*this)(k,j) *= -eraser;
      // 	(*this)(k,j) += delinquent * (*this)(k,ridx);
      //   }
      // }
      // else {
      //   _coefficient /= eraser;
      //   for (parameter_type k = ridx + 1; k < n; ++k) {
      // 	(*this)(k,j) *= eraser;
      // 	(*this)(k,j) -= delinquent * (*this)(k,ridx);
      //   }
      // }
      const Field multiplier = delinquent / eraser;
      for (parameter_type k = ridx + 1; k < n; ++k) {
	(*this)(k,j) -= multiplier * (*this)(k,ridx);
      }
      (*this)(ridx,j) = FieldConstants::ZERO;
    }
#ifdef SUPER_VERBOSE
    if (CommandlineOptions::debug()) {
      MessageStreams::debug() << message::lock;
      MessageStreams::debug() << "after step " << ridx << " of stair case transformation: " << std::endl;
      (*this).pretty_print(MessageStreams::debug());
      MessageStreams::debug() << message::unlock;
    }
#endif  
  }

  StairCaseMatrix& StairCaseMatrix::augment(const Matrix& matrix, const IntegerSet& ignored_cols) {

    // the resulting matrix will have matrix.rowdim() rows;
    // note that (*this) may be empty resulting in rowdim() = 0:
    if (matrix.coldim() == 0) {
      return *this;
    }
    if (empty()) {
      parameter_type j = 0;
      while(j < matrix.coldim()) {
	if (!ignored_cols.contains(j)) {
	  this->push_back(matrix.col(j));
	  return this->augment(matrix, ignored_cols + j);
	}
	else {
	  ++j;
	}
      }
      return *this;
    }
#ifdef INDEX_CHECK
    if (coldim() > 0) {
      assert(rowdim() == matrix.rowdim());
    }
#endif
    const parameter_type m = matrix.rowdim();

    // so many columns are already in stair-case form:
    const parameter_type n = coldim();

    // augment the matrix without elimination:
    Matrix::augment(matrix, ignored_cols);

    // so many columns must be in stair-case form after the transformations:
    const parameter_type n_new = coldim();

    // cnt1 many column elimination are needed with old pivot columns:
    const parameter_type cnt1 = std::min<parameter_type>(n, m);

    // cnt2 - cnt1 many column eliminations are needed with already modified new columns:
    const parameter_type cnt2 = std::min<parameter_type>(n_new, m);
  
#ifdef SUPER_VERBOSE
    if (CommandlineOptions::debug()) {
      MessageStreams::debug() << message::lock;
      MessageStreams::debug() << "before stair case transformation:" << std::endl;
      pretty_print(MessageStreams::debug());
      MessageStreams::debug() << message::unlock;
    }
#endif
    
    // eliminate new columns using all old columns:
    for (parameter_type i = 0; i < cnt1; ++i) {

      // use pivot (i, i) to eliminate (n_new - n) many columns starting at column n:
      _eliminate(i, n, n_new - n);
#ifdef SUPER_VERBOSE
      if (CommandlineOptions::debug()) {
	MessageStreams::debug() << message::lock;
	MessageStreams::debug() << "after elimination with pivot " << i
				<< ", startcol " << n
				<< ", no " << n_new - n << ":" << std::endl;
	(*this).pretty_print(MessageStreams::debug());
	MessageStreams::debug() << message::unlock;
      }
#endif
    }
    
    // eliminate new columns using new columns:
    for (parameter_type i = cnt1; i < cnt2; ++i) {

      // use pivot (i, i) to eliminate (n_new - i - 1) many columns starting at column i + 1:
      _eliminate(i, i + 1, n_new - i - 1);
#ifdef SUPER_VERBOSE
      if (CommandlineOptions::debug()) {
	MessageStreams::debug() << message::lock;
	MessageStreams::debug() << "after elimination with pivot " << i
				<< ", startcol " << i + 1
				<< ", no " << n_new - i - 1 << ":" << std::endl;
	(*this).pretty_print(MessageStreams::debug());
	MessageStreams::debug() << message::unlock;
      }
#endif
    }    
#ifdef SUPER_VERBOSE
    if (CommandlineOptions::debug()) {
      MessageStreams::debug() << message::lock;
      MessageStreams::debug() << "after stair case transformation: " << std::endl;
      (*this).pretty_print(MessageStreams::debug());
    MessageStreams::debug() << message::unlock;
    }
#endif
    return *this;
  }

  // this special form of "det" computes the determinant of the left-top-most
  // square matrix:
  const Field StairCaseMatrix::left_upper_det() const {
    parameter_type n = coldim();
    if (coldim() > rowdim()) {
      n = rowdim();
    }
    Field result(FieldConstants::ONE);
    for (parameter_type i = 0; i < n; ++i) {
      result *= (*this)(i,i);
      if (is_zero(result)) {
	return result;
      }
    }
    return result * _coefficient;
  }

  const Field StairCaseMatrix::det() const {
#ifdef INDEX_CHECK
    assert(coldim() == rowdim());
#endif
    return left_upper_det();
  }

  Message& StairCaseMatrix::pretty_print(Message& msg) const {
    if (msg.is_active()) {
      msg << "matrix: " << '\n';
      Matrix::pretty_print(msg);
      msg << "coefficient: " << _coefficient << '\n';
    }
    return msg;
  }
  
  std::ostream& StairCaseMatrix::pretty_print(std::ostream& ost) const {
    Message msg(ost);
    pretty_print(msg);
    return ost;
  }
  
  std::istream& StairCaseMatrix::read(std::istream& ist) {
    char c;
    
    ist >> std::ws;
    if (!Matrix::read(ist)) {
      MessageStreams::forced() << message::lock;
      MessageStreams::forced() << "StairCaseMatrix::read(std::istream&): "
			       << "error while reading matrix - exiting" << std::endl;
      MessageStreams::forced() << message::unlock;
      exit(1);
    }
    if (!(ist >> std::ws >> c >> std::ws) || (c != ContainerChars::divide_char)) {
      MessageStreams::forced() << message::lock;
      MessageStreams::forced() << "StairCaseMatrix::read(std::istream&): "
			       << "expected `" << ContainerChars::divide_char
			       << "' instead of `" << c << "' - exiting" << std::endl;
      MessageStreams::forced() << message::unlock;
      exit(1);
    }
    if (!(ist >> _coefficient)) {
      MessageStreams::forced() << message::lock;
      MessageStreams::forced() << "StairCaseMatrix::read(std::istream&): "
			       << "error while reading _coefficient - exiting" << std::endl;
      MessageStreams::forced() << message::unlock;
      exit(1);
    }
    return ist;
  }

  Message& StairCaseMatrix::write(Message& ost) const {
    Matrix::write(ost);
    ost << ContainerChars::divide_char
	<< this->_coefficient;
    return ost;
  }
  
  std::ostream& StairCaseMatrix::write(std::ostream& ost) const {
    Message msg(ost);
    write(msg);
    return ost;
  }

}; // namespace topcom

// eof StairCaseMatrix.cc
