bool isDIRKButcherTableau( const RKButcherTableauBase<Scalar>& rkbt )
{
  if (isEmptyRKButcherTableau(rkbt)) {
    return false;
  }
  typedef ScalarTraits<Scalar> ST;
  int numStages_local = rkbt.numStages();
  const Teuchos::SerialDenseMatrix<int,Scalar> A_local = rkbt.A();
  for (int i=0 ; i<numStages_local ; ++i) {
    for (int j=0 ; j<numStages_local ; ++j) {
      if ((j>i) && (A_local(i,j) != ST::zero())) {
        return false;
      }
    }
  }
  return true;
}
bool isSDIRKButcherTableau( const RKButcherTableauBase<Scalar>& rkbt ) 
{
  if (isEmptyRKButcherTableau(rkbt)) {
    return false;
  }
  if (!isDIRKButcherTableau(rkbt)) {
    return false;
  }
  // Verify the diagonal entries are all equal.
  typedef ScalarTraits<Scalar> ST;
  int numStages_local = rkbt.numStages();
  const Teuchos::SerialDenseMatrix<int,Scalar> A_local = rkbt.A();
  Scalar val = A_local(0,0);
  for (int i=0 ; i<numStages_local ; ++i) {
    if (A_local(i,i) != val) {
      return false;
    }
  }
  return true;
}
bool RKButcherTableauBase<Scalar>::operator== (const RKButcherTableauBase<Scalar>& rkbt) const
{ 
  if (this->numStages() != rkbt.numStages()) {
    return false;
  }
  if (this->order() != rkbt.order()) {
    return false;
  }
  int N = rkbt.numStages();
  // Check b and c first:
  const Teuchos::SerialDenseVector<int,Scalar> b_ = this->b();
  const Teuchos::SerialDenseVector<int,Scalar> c_ = this->c();
  const Teuchos::SerialDenseVector<int,Scalar> other_b = rkbt.b();
  const Teuchos::SerialDenseVector<int,Scalar> other_c = rkbt.c();
  for (int i=0 ; i<N ; ++i) {
    if (b_(i) != other_b(i)) {
      return false;
    }
    if (c_(i) != other_c(i)) {
      return false;
    }
  }
  // Then check A:
  const Teuchos::SerialDenseMatrix<int,Scalar>& A_ = this->A();
  const Teuchos::SerialDenseMatrix<int,Scalar>& other_A = rkbt.A();
  for (int i=0 ; i<N ; ++i) {
    for (int j=0 ; j<N ; ++j) {
      if (A_(i,j) != other_A(i,j)) {
        return false;
      }
    } 
  }
  return true;
}
bool isERKButcherTableau( const RKButcherTableauBase<Scalar>& rkbt) 
{
  if (isEmptyRKButcherTableau(rkbt)) {
    return false;
  }
  // Verify the diagonal is zero and the upper triangular part is zero
  typedef ScalarTraits<Scalar> ST;
  int numStages_local = rkbt.numStages();
  const Teuchos::SerialDenseMatrix<int,Scalar> A_local = rkbt.A();
  for (int i=0 ; i<numStages_local ; ++i) {
    for (int j=0 ; j<numStages_local ; ++j) {
      if ((j>=i) && ((A_local(i,j) != ST::zero()))) {
        return false;
      }
    }
  }
  const Teuchos::SerialDenseVector<int,Scalar> c_local = rkbt.c();
  if( c_local(0) != ST::zero() ) {
    return false;
  }
  // 08/13/08 tscoffe:  I'm not sure what else I can check for b & c...
  return true;
}
bool isEmptyRKButcherTableau( const RKButcherTableauBase<Scalar>& rkbt ) {
  typedef ScalarTraits<Scalar> ST;
  
  // Check that numStages > 0
  if (rkbt.numStages() == 0) {
    return true;
  }

  // Check that the b vector has _some_ non-zero entry
  int numNonZero = 0;
  int numStages_local = rkbt.numStages();
  const Teuchos::SerialDenseVector<int,Scalar> b_local = rkbt.b();
  for (int i=0 ; i<numStages_local ; ++i) {
    if (b_local(i) != ST::zero()) {
      numNonZero++;
    }
  }
  if (numNonZero == 0) {
    return true;
  }
  // There is no reason to check A and c because they can be zero and you're
  // producing an explicit method as long as b has something in it.
  return false;
}
E_RKButcherTableauTypes determineRKBTType(const RKButcherTableauBase<Scalar>& rkbt) {
  if (isEmptyRKButcherTableau(rkbt)) {
    return RYTHMOS_RK_BUTCHER_TABLEAU_TYPE_INVALID;
  }
  if (isERKButcherTableau(rkbt)) {
    return RYTHMOS_RK_BUTCHER_TABLEAU_TYPE_ERK;
  }
  if (rkbt.numStages() == 1) { 
    // In this case, its just an IRK method.
    return RYTHMOS_RK_BUTCHER_TABLEAU_TYPE_IRK;
  }
  if (isSDIRKButcherTableau(rkbt)) {
    return RYTHMOS_RK_BUTCHER_TABLEAU_TYPE_SDIRK;
  }
  if (isDIRKButcherTableau(rkbt)) {
    return RYTHMOS_RK_BUTCHER_TABLEAU_TYPE_DIRK;
  }
  if (isIRKButcherTableau(rkbt)) {
    return RYTHMOS_RK_BUTCHER_TABLEAU_TYPE_IRK;
  }
  return RYTHMOS_RK_BUTCHER_TABLEAU_TYPE_INVALID;
}