// SPDX-License-Identifier: Apache-2.0
// 
// Copyright 2011-2017 Ryan Curtin (http://www.ratml.org/)
// Copyright 2017 National ICT Australia (NICTA)
//
// 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
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

#include <armadillo>

#include "catch.hpp"

using namespace arma;

// Does the matrix correctly report when it is empty?
TEST_CASE("empty_test")
  {
  // bool testPassed = true;

  sp_imat test;
  REQUIRE( test.is_empty() );

  test.set_size(3, 4);
  REQUIRE( test.is_empty() == false );
  }

// Can we insert items into the matrix correctly?
TEST_CASE("insertion_test")
  {
  int correctResult[3][4] =
      {{1, 0, 0, 0},
       {2, 3, 1, 0},
       {0, 9, 4, 0}};

  // Now run the same test for the Armadillo sparse matrix.
  SpMat<int> arma_test;
  arma_test.set_size(3, 4);

  // Fill the matrix (hopefully).
  arma_test(0, 0) = 1;
  arma_test(1, 0) = 2;
  arma_test(1, 1) = 3;
  arma_test(2, 1) = 9;
  arma_test(1, 2) = 1;
  arma_test(2, 2) = 4;

  for (uword i = 0; i < 3; i++)
    {
    for (uword j = 0; j < 4; j++)
      {
      REQUIRE( (int) arma_test(i, j) == correctResult[i][j] );
      }
    }
  }

// Does sparse-sparse matrix multiplication work?
TEST_CASE("full_sparse_sparse_matrix_multiplication_test")
  {
  // Now perform the test again for SpMat.
  SpMat<int> spa(3, 3);
  SpMat<int> spb(3, 2);
  int correctResult[3][2] =
      {{ 46,  60},
       { 40,  52},
       {121, 160}};

  spa(0, 0) = 1;
  spa(0, 1) = 10;
  spa(0, 2) = 3;
  spa(1, 0) = 3;
  spa(1, 1) = 4;
  spa(1, 2) = 5;
  spa(2, 0) = 12;
  spa(2, 1) = 13;
  spa(2, 2) = 14;

  spb(0, 0) = 1;
  spb(0, 1) = 2;
  spb(1, 0) = 3;
  spb(1, 1) = 4;
  spb(2, 0) = 5;
  spb(2, 1) = 6;

  spa *= spb;

  REQUIRE( spa.n_rows == 3 );
  REQUIRE( spa.n_cols == 2 );

  for (uword i = 0; i < 3; i++)
    {
    for (uword j = 0; j < 2; j++)
      {
      REQUIRE( (int) spa(i, j) == correctResult[i][j] );
      }
    }
  }

TEST_CASE("sparse_sparse_matrix_multiplication_test")
  {
  SpMat<double> spaa(10, 10);
  spaa(1, 5) = 0.4;
  spaa(0, 4) = 0.3;
  spaa(0, 8) = 1.2;
  spaa(3, 0) = 1.1;
  spaa(3, 1) = 1.1;
  spaa(3, 2) = 1.1;
  spaa(4, 4) = 0.2;
  spaa(4, 9) = 0.1;
  spaa(6, 2) = 4.1;
  spaa(6, 8) = 4.1;
  spaa(7, 5) = 1.0;
  spaa(8, 9) = 0.4;
  spaa(9, 4) = 0.4;

  double correctResultB[10][10] =
    {{ 0.00, 0.00, 0.00, 0.00, 0.06, 0.00, 0.00, 0.00, 0.00, 0.51 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.33, 0.44, 0.00, 0.00, 1.32, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.08, 0.00, 0.00, 0.00, 0.00, 0.02 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.64 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.16, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.08, 0.00, 0.00, 0.00, 0.00, 0.04 }};

  spaa *= spaa;

  for (uword i = 0; i < 10; i++)
    {
    for (uword j = 0; j < 10; j++)
      {
      REQUIRE( (double) spaa(i, j) == Approx(correctResultB[i][j]) );
      }
    }
  }

TEST_CASE("hadamard_product_test")
  {
  SpMat<int> a(4, 4), b(4, 4);

  a(1, 1) = 1;
  a(2, 1) = 1;
  a(3, 3) = 1;
  a(3, 0) = 1;
  a(0, 2) = 1;

  b(1, 1) = 1;
  b(2, 2) = 1;
  b(3, 3) = 1;
  b(3, 0) = 1;
  b(0, 3) = 1;
  b(3, 1) = 1;

  double correctResult[4][4] =
    {{ 0, 0, 0, 0 },
     { 0, 1, 0, 0 },
     { 0, 0, 0, 0 },
     { 1, 0, 0, 1 }};

  a %= b;

  for (uword i = 0; i < 4; i++)
    {
    for (uword j = 0; j < 4; j++)
      {
      REQUIRE( a(i, j) == correctResult[i][j] );
      }
    }


  SpMat<double> c, d;
  c.sprandu(30, 25, 0.1);
  d.sprandu(30, 25, 0.1);

  mat e, f;
  e = c;
  f = d;

  c %= d;
  e %= f;

  for (uword i = 0; i < 25; ++i)
    {
    for(uword j = 0; j < 30; ++j)
      {
      REQUIRE( (double) c(j, i) == Approx(e(j, i)) );
      }
    }
  }

TEST_CASE("division_test")
  {
  SpMat<double> a(2, 2), b(2, 2);

  a(0, 1) = 0.5;

  b(0, 1) = 1.0;
  b(1, 0) = 5.0;

  a /= b;

  REQUIRE( std::isnan((double) a(0, 0)) );
  REQUIRE( (double) a(0, 1) == Approx(0.5) );
  REQUIRE( (double) a(1, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( std::isnan((double) a(1, 1)) );
  }

TEST_CASE("insert_delete_test")
  {
  SpMat<double> sp;
  sp.set_size(10, 10);

  // Ensure everything is empty.
  for (uword i = 0; i < 100; i++)
    {
    REQUIRE( sp(i) == 0.0 );
    }

  // Add an element.
  sp(5, 5) = 43.234;
  REQUIRE( sp.n_nonzero == 1 );
  REQUIRE( (double) sp(5, 5) == Approx(43.234) );

  // Remove the element.
  sp(5, 5) = 0.0;
  REQUIRE( sp.n_nonzero == 0 );
  }

TEST_CASE("value_operator_test")
  {
  // Test operators that work with a single value.
  // =(double), /=(double), *=(double)
  SpMat<double> sp(3, 4);
  double correctResult[3][4] = {{1.5, 0.0, 0.0, 0.0},
                                {2.1, 3.2, 0.9, 0.0},
                                {0.0, 9.3, 4.0, -1.5}};
  sp(0, 0) = 1.5;
  sp(1, 0) = 2.1;
  sp(1, 1) = 3.2;
  sp(1, 2) = 0.9;
  sp(2, 1) = 9.3;
  sp(2, 2) = 4.0;
  sp(2, 3) = -1.5;

  // operator=(double)
  SpMat<double> work = sp;
  work = 5.0;
  REQUIRE( work.n_nonzero == 1 );
  REQUIRE( work.n_elem == 1 );
  REQUIRE( (double) work(0) == Approx(5.0) );

  // operator*=(double)
  work = sp;
  work *= 2;
  REQUIRE( work.n_nonzero == 7 );
  for (uword i = 0; i < 3; i++)
    {
    for (uword j = 0; j < 4; j++)
      {
      REQUIRE((double) work(i, j) == Approx(correctResult[i][j] * 2.0) );
      }
    }

  // operator/=(double)
  work = sp;
  work /= 5.5;
  REQUIRE( work.n_nonzero == 7 );
  for (uword i = 0; i < 3; i++)
    {
    for (uword j = 0; j < 4; j++)
      {
      REQUIRE((double) work(i, j) == Approx(correctResult[i][j] / 5.5) );
      }
    }
  }

TEST_CASE("iterator_test")
  {
  SpMat<double> x(5, 5);
  x(4, 1) = 3.1;
  x(1, 2) = 4.2;
  x(1, 3) = 3.3;
  x(1, 3) = 5.5; // overwrite
  x(2, 3) = 4.5;
  x(4, 4) = 6.4;

  SpMat<double>::iterator it = x.begin();
  REQUIRE( (double) *it == Approx(3.1) );
  REQUIRE( it.row() == 4 );
  REQUIRE( it.col() == 1 );
  ++it;

  REQUIRE( (double) *it == Approx(4.2) );
  REQUIRE( it.row() == 1 );
  REQUIRE( it.col() == 2 );
  ++it;

  REQUIRE( (double) *it == Approx(5.5) );
  REQUIRE( it.row() == 1 );
  REQUIRE( it.col() == 3 );
  ++it;

  REQUIRE( (double) *it == Approx(4.5) );
  REQUIRE( it.row() == 2 );
  REQUIRE( it.col() == 3 );
  ++it;

  REQUIRE( (double) *it == Approx(6.4) );
  REQUIRE( it.row() == 4 );
  REQUIRE( it.col() == 4 );
  ++it;

  REQUIRE( it == x.end() );

  // Now let's go backwards.
  --it; // Get it off the end.
  REQUIRE( (double) *it == Approx(6.4) );
  REQUIRE( it.row() == 4 );
  REQUIRE( it.col() == 4 );
  --it;

  REQUIRE( (double) *it == Approx(4.5) );
  REQUIRE( it.row() == 2 );
  REQUIRE( it.col() == 3 );
  --it;

  REQUIRE( (double) *it == Approx(5.5) );
  REQUIRE( it.row() == 1 );
  REQUIRE( it.col() == 3 );
  --it;

  REQUIRE( (double) *it == Approx(4.2) );
  REQUIRE( it.row() == 1 );
  REQUIRE( it.col() == 2 );
  --it;

  REQUIRE( (double) *it == Approx(3.1) );
  REQUIRE( it.row() == 4 );
  REQUIRE( it.col() == 1 );

  REQUIRE( it == x.begin() );

  // Try removing an element we iterated to.
  ++it;
  ++it;
  *it = 0;
  REQUIRE( x.n_nonzero == 4 );
}

TEST_CASE("row_iterator_test")
  {
  SpMat<double> x(5, 5);
  x(4, 1) = 3.1;
  x(1, 2) = 4.2;
  x(1, 3) = 3.3;
  x(1, 3) = 5.5; // overwrite
  x(2, 3) = 4.5;
  x(4, 4) = 6.4;

  SpMat<double>::row_iterator it = x.begin_row();
  REQUIRE( (double) *it == Approx(4.2) );
  REQUIRE( it.row() == 1 );
  REQUIRE( it.col() == 2 );
  ++it;

  REQUIRE( (double) *it == Approx(5.5) );
  REQUIRE( it.row() == 1 );
  REQUIRE( it.col() == 3 );
  ++it;

  REQUIRE( (double) *it == Approx(4.5) );
  REQUIRE( it.row() == 2 );
  REQUIRE( it.col() == 3 );
  ++it;

  REQUIRE( (double) *it == Approx(3.1) );
  REQUIRE( it.row() == 4 );
  REQUIRE( it.col() == 1 );
  ++it;

  REQUIRE( (double) *it == Approx(6.4) );
  REQUIRE( it.row() == 4 );
  REQUIRE( it.col() == 4 );
  ++it;

//  REQUIRE( it == x.end_row() );

  // Now let's go backwards.
  --it; // Get it off the end.
  REQUIRE( (double) *it == Approx(6.4) );
  REQUIRE( it.row() == 4 );
  REQUIRE( it.col() == 4 );
  --it;

  REQUIRE( (double) *it == Approx(3.1) );
  REQUIRE( it.row() == 4 );
  REQUIRE( it.col() == 1 );
  --it;

  REQUIRE( (double) *it == Approx(4.5) );
  REQUIRE( it.row() == 2 );
  REQUIRE( it.col() == 3 );
  --it;

  REQUIRE( (double) *it == Approx(5.5) );
  REQUIRE( it.row() == 1 );
  REQUIRE( it.col() == 3 );
  --it;

  REQUIRE( (double) *it == Approx(4.2) );
  REQUIRE( it.row() == 1 );
  REQUIRE( it.col() == 2 );

  REQUIRE( it == x.begin_row() );

  // Try removing an element we iterated to.
  ++it;
  ++it;
  *it = 0;
  REQUIRE( x.n_nonzero == 4 );
  }

TEST_CASE("basic_sp_mat_operator_test")
  {
  // +=, -=, *=, /=, %=
  SpMat<double> a(6, 5);
  a(0, 0) = 3.4;
  a(4, 1) = 4.1;
  a(5, 1) = 1.5;
  a(3, 2) = 2.6;
  a(4, 2) = 3.0;
  a(1, 3) = 9.8;
  a(4, 3) = 0.1;
  a(2, 4) = 0.2;
  a(3, 4) = 0.2;
  a(4, 4) = 0.2;
  a(5, 4) = 8.3;

  SpMat<double> b(6, 5);
  b(0, 0) = 3.4;
  b(3, 0) = 0.4;
  b(3, 1) = 0.5;
  b(4, 1) = 1.2;
  b(4, 2) = 3.0;
  b(5, 2) = 1.1;
  b(1, 3) = 0.6;
  b(3, 3) = 1.0;
  b(4, 4) = 7.3;
  b(5, 4) = 7.4;

  double addResult[6][5] = {{6.8 , 0   , 0   , 0   , 0   },
                            {0   , 0   , 0   , 10.4, 0   },
                            {0   , 0   , 0   , 0   , 0.2 },
                            {0.4 , 0.5 , 2.6 , 1.0 , 0.2 },
                            {0   , 5.3 , 6.0 , 0.1 , 7.5 },
                            {0   , 1.5 , 1.1 , 0   , 15.7}};

  double subResult[6][5] = {{0   , 0   , 0   , 0   , 0   },
                            {0   , 0   , 0   , 9.2 , 0   },
                            {0   , 0   , 0   , 0   , 0.2 },
                            {-0.4, -0.5, 2.6 , -1.0, 0.2 },
                            {0   , 2.9 , 0   , 0.1 , -7.1},
                            {0   , 1.5 , -1.1, 0   , 0.9 }};

  SpMat<double> out = a;
  out += b;
  REQUIRE( out.n_nonzero == 15 );
  for (uword r = 0; r < 6; r++)
    {
    for (uword c = 0; c < 5; c++)
      {
      REQUIRE( (double) out(r, c) == Approx(addResult[r][c]) );
      }
    }

  out = a;
  out -= b;
  REQUIRE( out.n_nonzero == 13 );
  for (uword r = 0; r < 6; r++)
    {
    for (uword c = 0; c < 5; c++)
      {
      REQUIRE( (double) out(r, c) == Approx(subResult[r][c]) );
      }
    }
  }

TEST_CASE("min_max_test")
  {
  SpMat<double> a(6, 5);
  a(0, 0) = 3.4;
  a(4, 1) = 4.1;
  a(5, 1) = 1.5;
  a(3, 2) = 2.6;
  a(4, 2) = 3.0;
  a(1, 3) = 9.8;
  a(4, 3) = 0.1;
  a(2, 4) = 0.2;
  a(3, 4) = -0.2;
  a(4, 4) = 0.2;
  a(5, 4) = 8.3;

  uword index, row, col;
  REQUIRE( a.min() == Approx(-0.2) );
  REQUIRE( a.min(index) == Approx(-0.2) );
  REQUIRE( index == 27 );
  REQUIRE( a.min(row, col) == Approx(-0.2) );
  REQUIRE( row == 3 );
  REQUIRE( col == 4 );

  REQUIRE( a.max() == Approx(9.8) );
  REQUIRE( a.max(index) == Approx(9.8) );
  REQUIRE( index == 19 );
  REQUIRE( a.max(row, col) == Approx(9.8) );
  REQUIRE( row == 1 );
  REQUIRE( col == 3 );
  }

TEST_CASE("swap_row_test")
  {
  SpMat<double> a(6, 5);
  a(0, 0) = 3.4;
  a(4, 1) = 4.1;
  a(5, 1) = 1.5;
  a(3, 2) = 2.6;
  a(4, 2) = 3.0;
  a(1, 3) = 9.8;
  a(4, 3) = 0.1;
  a(2, 4) = 0.2;
  a(3, 4) = -0.2;
  a(4, 4) = 0.2;
  a(5, 4) = 8.3;

  /**
   * [[3.4  0.0  0.0  0.0  0.0]
   *  [0.0  0.0  0.0  9.8  0.0]
   *  [0.0  0.0  0.0  0.0  0.2]
   *  [0.0  0.0  2.6  0.0 -0.2]
   *  [0.0  4.1  3.0  0.1  0.2]
   *  [0.0  1.5  0.0  0.0  8.3]]
   */
  double swapOne[6][5] =
    {{ 0.0,  0.0,  2.6,  0.0, -0.2},
     { 0.0,  0.0,  0.0,  9.8,  0.0},
     { 0.0,  0.0,  0.0,  0.0,  0.2},
     { 3.4,  0.0,  0.0,  0.0,  0.0},
     { 0.0,  4.1,  3.0,  0.1,  0.2},
     { 0.0,  1.5,  0.0,  0.0,  8.3}};

  double swapTwo[6][5] =
    {{ 0.0,  0.0,  2.6,  0.0, -0.2},
     { 0.0,  0.0,  0.0,  9.8,  0.0},
     { 0.0,  0.0,  0.0,  0.0,  0.2},
     { 3.4,  0.0,  0.0,  0.0,  0.0},
     { 0.0,  1.5,  0.0,  0.0,  8.3},
     { 0.0,  4.1,  3.0,  0.1,  0.2}};

  a.swap_rows(0, 3);

  for (uword row = 0; row < a.n_rows; row++)
    {
    for (uword col = 0; col < a.n_cols; col++)
      {
      REQUIRE( (double) a(row, col) == Approx(swapOne[row][col]) );
      }
    }

  a.swap_rows(4, 5);

  for (uword row = 0; row < a.n_rows; row++)
    {
    for (uword col = 0; col < a.n_cols; col++)
      {
      REQUIRE( (double) a(row, col) == Approx(swapTwo[row][col]) );
      }
    }
  }

TEST_CASE("swap_col_test")
  {
  SpMat<double> a(6, 5);
  a(0, 0) = 3.4;
  a(4, 1) = 4.1;
  a(5, 1) = 1.5;
  a(3, 2) = 2.6;
  a(4, 2) = 3.0;
  a(1, 3) = 9.8;
  a(4, 3) = 0.1;
  a(2, 4) = 0.2;
  a(3, 4) = -0.2;
  a(4, 4) = 0.2;
  a(5, 4) = 8.3;

  mat b(6, 5);
  b.zeros(6, 5);
  b(0, 0) = 3.4;
  b(4, 1) = 4.1;
  b(5, 1) = 1.5;
  b(3, 2) = 2.6;
  b(4, 2) = 3.0;
  b(1, 3) = 9.8;
  b(4, 3) = 0.1;
  b(2, 4) = 0.2;
  b(3, 4) = -0.2;
  b(4, 4) = 0.2;
  b(5, 4) = 8.3;

  /**
   * [[3.4  0.0  0.0  0.0  0.0]
   *  [0.0  0.0  0.0  9.8  0.0]
   *  [0.0  0.0  0.0  0.0  0.2]
   *  [0.0  0.0  2.6  0.0 -0.2]
   *  [0.0  4.1  3.0  0.1  0.2]
   *  [0.0  1.5  0.0  0.0  8.3]]
   */

  a.swap_cols(2, 3);
  b.swap_cols(2, 3);

  for (uword row = 0; row < a.n_rows; row++)
    {
    for (uword col = 0; col < a.n_cols; col++)
      {
      REQUIRE( (double) a(row, col) == Approx(b(row, col)) );
      }
    }

  a.swap_cols(0, 4);
  b.swap_cols(0, 4);

  for (uword row = 0; row < a.n_rows; row++)
    {
    for (uword col = 0; col < a.n_cols; col++)
      {
      REQUIRE( (double) a(row, col) == Approx(b(row, col)) );
      }
    }

  a.swap_cols(1, 4);
  b.swap_cols(1, 4);

  for (uword row = 0; row < a.n_rows; row++)
    {
    for (uword col = 0; col < a.n_cols; col++)
      {
      REQUIRE( (double) a(row, col) == Approx(b(row, col)) );
      }
    }
  }

TEST_CASE("shed_col_test")
  {
  SpMat<int> a(2, 2);
  a(0, 0) = 1;
  a(1, 1) = 1;

  /**
   * [[1 0]
   *  [0 1]]
   *
   * becomes
   *
   * [[0]
   *  [1]]
   */

  a.shed_col(0);
  REQUIRE( a.n_cols == 1 );
  REQUIRE( a.n_rows == 2 );
  REQUIRE( a.n_elem == 2 );
  REQUIRE( a.n_nonzero == 1 );
  REQUIRE( a(0, 0) == 0 );
  REQUIRE( a(1, 0) == 1 );
  }

TEST_CASE("shed_cols_test")
  {
  SpMat<int> a(3, 3);
  a(0, 0) = 1;
  a(1, 1) = 1;
  a(2, 2) = 1;
  SpMat<int> b(3, 3);
  b(0, 0) = 1;
  b(1, 1) = 1;
  b(2, 2) = 1;
  SpMat<int> c(3, 3);
  c(0, 0) = 1;
  c(1, 1) = 1;
  c(2, 2) = 1;

  /**
   * [[1 0 0]
   *  [0 1 0]
   *  [0 0 1]]
   *
   * becomes
   *
   * [[0]
   *  [0]
   *  [1]]
   */

  a.shed_cols(0, 1);
  REQUIRE( a.n_cols == 1 );
  REQUIRE( a.n_rows == 3 );
  REQUIRE( a.n_elem == 3 );
  REQUIRE( a.n_nonzero == 1 );
  REQUIRE( a(0, 0) == 0 );
  REQUIRE( a(1, 0) == 0 );
  REQUIRE( a(2, 0) == 1 );
  // Make sure that the row indices are sized appropriately.
  REQUIRE( a.row_indices[a.n_nonzero] == 0 );

  b.shed_cols(1, 2);
  REQUIRE( b.n_cols == 1 );
  REQUIRE( b.n_rows == 3 );
  REQUIRE( b.n_elem == 3 );
  REQUIRE( b.n_nonzero == 1 );
  REQUIRE( b(0, 0) == 1 );
  REQUIRE( b(1, 0) == 0 );
  REQUIRE( b(2, 0) == 0 );
  REQUIRE( b.row_indices[b.n_nonzero] == 0 );

  c.shed_cols(0, 0);
  c.shed_cols(1, 1);
  REQUIRE( c.n_cols == 1 );
  REQUIRE( c.n_rows == 3 );
  REQUIRE( c.n_elem == 3 );
  REQUIRE( c.n_nonzero == 1 );
  REQUIRE( c(0, 0) == 0 );
  REQUIRE( c(1, 0) == 1 );
  REQUIRE( c(2, 0) == 0 );
  REQUIRE( c.row_indices[c.n_nonzero] == 0 );
  }

TEST_CASE("shed_row_test")
  {
  SpMat<int> a(3, 3);
  a(0, 0) = 1;
  a(1, 1) = 1;
  a(2, 2) = 1;
  Mat<int> b(3, 3);
  b.zeros(3, 3);
  b(0, 0) = 1;
  b(1, 1) = 1;
  b(2, 2) = 1;

  /**
   * [[1 0 0]
   *  [0 1 0]
   *  [0 0 1]]
   *
   * becomes
   *
   * [[1 0 0]
   *  [0 1 0]]
   */
  a.shed_row(2);
  b.shed_row(2);
  REQUIRE( a.n_cols == 3 );
  REQUIRE( a.n_rows == 2 );
  REQUIRE( a.n_elem == 6 );
  REQUIRE( a.n_nonzero == 2 );
  for (uword row = 0; row < a.n_rows; row++)
    {
    for (uword col = 0; col < a.n_cols; col++)
      {
      REQUIRE( (double) a(row, col) == Approx(b(row, col)) );
      }
    }
  }

TEST_CASE("shed_rows_test")
  {
  SpMat<int> a(5, 5);
  a(0, 0) = 1;
  a(1, 1) = 1;
  a(2, 2) = 1;
  a(3, 3) = 1;
  a(4, 4) = 1;
  Mat<int> b(5, 5);
  b.zeros(5, 5);
  b(0, 0) = 1;
  b(1, 1) = 1;
  b(2, 2) = 1;
  b(3, 3) = 1;
  b(4, 4) = 1;

  SpMat<int> c = a;
  Mat<int> d = b;

  /**
   * [[1 0 0 0 0]
   *  [0 1 0 0 0]
   *  [0 0 1 0 0]
   *  [0 0 0 1 0]
   *  [0 0 0 0 1]]
   *
   * becomes
   *
   * [[1 0 0 0 0]
   *  [0 1 0 0 0]]
   */
  a.shed_rows(2,4);
  b.shed_rows(2,4);
  REQUIRE( a.n_cols == 5 );
  REQUIRE( a.n_rows == 2 );
  REQUIRE( a.n_elem == 10 );
  REQUIRE( a.n_nonzero == 2 );
  for (uword row = 0; row < a.n_rows; row++)
    {
    for (uword col = 0; col < a.n_cols; col++)
      {
      REQUIRE( (double) a(row, col) == Approx(b(row, col)) );
      }
    }

  c.shed_rows(0, 2);
  d.shed_rows(0, 2);
  REQUIRE( c.n_cols == 5 );
  REQUIRE( c.n_rows == 2 );
  REQUIRE( c.n_elem == 10 );
  REQUIRE( c.n_nonzero == 2 );
  for (uword row = 0; row < c.n_rows; ++row)
    {
    for (uword col = 0; col < c.n_cols; ++col)
      {
      REQUIRE( (int) c(row, col) == d(row, col) );
      }
    }
  }

TEST_CASE("sp_mat_reshape_test")
  {
  // Input matrix:
  // [[0 2 0]
  //  [1 3 0]
  //  [0 0 5]
  //  [0 4 6]]
  //
  // Output matrix:
  // [[0 0 0 0]
  //  [1 2 4 5]
  //  [0 3 0 6]]
  SpMat<unsigned int> ref(4, 3);
  ref(1, 0) = 1;
  ref(0, 1) = 2;
  ref(1, 1) = 3;
  ref(3, 1) = 4;
  ref(2, 2) = 5;
  ref(3, 2) = 6;

  // Now reshape.
  ref.reshape(3, 4);

  // Check everything.
  REQUIRE( ref.n_cols == 4 );
  REQUIRE( ref.n_rows == 3 );

  REQUIRE( (unsigned int) ref(0, 0) == 0 );
  REQUIRE( (unsigned int) ref(1, 0) == 1 );
  REQUIRE( (unsigned int) ref(2, 0) == 0 );
  REQUIRE( (unsigned int) ref(0, 1) == 0 );
  REQUIRE( (unsigned int) ref(1, 1) == 2 );
  REQUIRE( (unsigned int) ref(2, 1) == 3 );
  REQUIRE( (unsigned int) ref(0, 2) == 0 );
  REQUIRE( (unsigned int) ref(1, 2) == 4 );
  REQUIRE( (unsigned int) ref(2, 2) == 0 );
  REQUIRE( (unsigned int) ref(0, 3) == 0 );
  REQUIRE( (unsigned int) ref(1, 3) == 5 );
  REQUIRE( (unsigned int) ref(2, 3) == 6 );
  }

TEST_CASE("sp_mat_zeros_tests")
  {
  SpMat<double> m(4, 3);
  m(1, 0) = 1;
  m(0, 1) = 2;
  m(1, 1) = 3;
  m(3, 1) = 4;
  m(2, 2) = 5;
  m(3, 2) = 6;

  // Now zero it out.
  SpMat<double> d = m;

  d.zeros();

  REQUIRE( d.values[0] == 0 );
  REQUIRE( d.row_indices[0] == 0);
  REQUIRE( d.col_ptrs[0] == 0 );
  REQUIRE( d.col_ptrs[1] == 0 );
  REQUIRE( d.col_ptrs[2] == 0 );
  REQUIRE( d.col_ptrs[3] == 0 );
  REQUIRE( d.n_cols == 3 );
  REQUIRE( d.n_rows == 4 );
  REQUIRE( d.n_elem == 12 );
  REQUIRE( d.n_nonzero == 0 );

  // Now zero it out again.
  d = m;
  d.zeros(10);

  REQUIRE( d.values[0] == 0 );
  REQUIRE( d.row_indices[0] == 0);
  REQUIRE( d.col_ptrs[0] == 0 );
  REQUIRE( d.col_ptrs[1] == 0 );
  REQUIRE( d.n_cols == 1 );
  REQUIRE( d.n_rows == 10 );
  REQUIRE( d.n_elem == 10 );
  REQUIRE( d.n_nonzero == 0 );

  // Now zero it out again.
  d = m;
  d.zeros(5, 5);

  REQUIRE( d.values[0] == 0 );
  REQUIRE( d.row_indices[0] == 0);
  REQUIRE( d.col_ptrs[0] == 0 );
  REQUIRE( d.col_ptrs[1] == 0 );
  REQUIRE( d.col_ptrs[2] == 0 );
  REQUIRE( d.col_ptrs[3] == 0 );
  REQUIRE( d.col_ptrs[4] == 0 );
  REQUIRE( d.col_ptrs[5] == 0 );
  REQUIRE( d.n_cols == 5 );
  REQUIRE( d.n_rows == 5 );
  REQUIRE( d.n_elem == 25 );
  REQUIRE( d.n_nonzero == 0 );
  }



/**
 * Check that eye() works.
 */
TEST_CASE("sp_mat_eye_test")
  {
  SpMat<double> e = eye<SpMat<double> >(5, 5);

  REQUIRE( e.n_elem == 25 );
  REQUIRE( e.n_rows == 5 );
  REQUIRE( e.n_cols == 5 );
  REQUIRE( e.n_nonzero == 5 );

  for (uword i = 0; i < 5; i++)
    {
    for (uword j = 0; j < 5; j++)
      {
      if (i == j)
        REQUIRE( (double) e(i, j) == Approx(1.0) );
      else
        REQUIRE( (double) e(i, j) == Approx(0.0).margin(0.001) );
      }
    }

  // Just check that these compile and run.
  e = eye<SpMat<double> >(5, 5);
  e *= eye<SpMat<double> >(5, 5);
  e %= eye<SpMat<double> >(5, 5);
  e /= eye<SpMat<double> >(5, 5);
  }

/**
 * Check that pow works.
 *
TEST_CASE("sp_mat_pow_test")
  {
  SpMat<double> a(3, 3);
  a(0, 2) = 4.3;
  a(1, 1) = -5.5;
  a(2, 2) = -6.3;

  a += pow(a, 2);

  REQUIRE( (double) a(0, 0) == 0 );
  REQUIRE( (double) a(1, 0) == 0 );
  REQUIRE( (double) a(2, 0) == 0 );
  REQUIRE( (double) a(0, 1) == 0 );
  REQUIRE( (double) a(1, 1) == Approx(24.75) );
  REQUIRE( (double) a(2, 1) == 0 );
  REQUIRE( (double) a(0, 2) == Approx(22.79) );
  REQUIRE( (double) a(1, 2) == 0 );
  REQUIRE( (double) a(2, 2) == Approx(33.39) );

  a = pow(a, 2);
  a *= pow(a, 2);
  a %= pow(a, 2);
  a /= pow(a, 2);
  }
*/


// I hate myself.
#undef TEST_OPERATOR
#define TEST_OPERATOR(EOP_TEST, EOP) \
TEST_CASE(EOP_TEST) \
  {\
  SpMat<double> a(3, 3);\
  a(0, 2) = 4.3;\
  a(1, 1) = -5.5;\
  a(2, 2) = -6.3;\
  a(1, 0) = 0.001;\
  Mat<double> b(3, 3);\
  b.zeros();\
  b(0, 2) = 4.3;\
  b(1, 1) = -5.5;\
  b(2, 2) = -6.3;\
  b(1, 0) = 0.001;\
  \
  SpMat<double> c = EOP(a);\
  Mat<double> d = EOP(b);\
  \
  if (c(0, 0) == c(0, 0) && d(0, 0) == d(0, 0))\
    REQUIRE( c(0, 0) == d(0, 0) );\
  if (c(1, 0) == c(1, 0) && d(1, 0) == d(1, 0))\
    REQUIRE( c(1, 0) == d(1, 0) );\
  if (c(2, 0) == c(2, 0) && d(2, 0) == d(2, 0))\
    REQUIRE( c(2, 0) == d(2, 0) );\
  if (c(0, 1) == c(0, 1) && d(0, 1) == d(0, 1))\
    REQUIRE( c(0, 1) == d(0, 1) );\
  if (c(1, 1) == c(1, 1) && d(1, 1) == d(1, 1))\
    REQUIRE( c(1, 1) == d(1, 1) );\
  if (c(2, 1) == c(2, 1) && d(2, 1) == d(2, 1))\
    REQUIRE( c(2, 1) == d(2, 1) );\
  if (c(0, 2) == c(0, 2) && d(0, 2) == d(0, 2))\
    REQUIRE( c(0, 2) == d(0, 2) );\
  if (c(1, 2) == c(1, 2) && d(1, 2) == d(1, 2))\
    REQUIRE( c(1, 2) == d(1, 2) );\
  if (c(2, 2) == c(2, 2) && d(2, 2) == d(2, 2))\
    REQUIRE( c(2, 2) == d(2, 2) );\
  \
  c -= EOP(a);\
  d -= EOP(b);\
  \
  if (c(0, 0) == c(0, 0) && d(0, 0) == d(0, 0))\
    REQUIRE( c(0, 0) == d(0, 0) );\
  if (c(1, 0) == c(1, 0) && d(1, 0) == d(1, 0))\
    REQUIRE( c(1, 0) == d(1, 0) );\
  if (c(2, 0) == c(2, 0) && d(2, 0) == d(2, 0))\
    REQUIRE( c(2, 0) == d(2, 0) );\
  if (c(0, 1) == c(0, 1) && d(0, 1) == d(0, 1))\
    REQUIRE( c(0, 1) == d(0, 1) );\
  if (c(1, 1) == c(1, 1) && d(1, 1) == d(1, 1))\
    REQUIRE( c(1, 1) == d(1, 1) );\
  if (c(2, 1) == c(2, 1) && d(2, 1) == d(2, 1))\
    REQUIRE( c(2, 1) == d(2, 1) );\
  if (c(0, 2) == c(0, 2) && d(0, 2) == d(0, 2))\
    REQUIRE( c(0, 2) == d(0, 2) );\
  if (c(1, 2) == c(1, 2) && d(1, 2) == d(1, 2))\
    REQUIRE( c(1, 2) == d(1, 2) );\
  if (c(2, 2) == c(2, 2) && d(2, 2) == d(2, 2))\
    REQUIRE( c(2, 2) == d(2, 2) );\
  \
  c %= EOP(a);\
  d %= EOP(b);\
  \
  if (c(0, 0) == c(0, 0) && d(0, 0) == d(0, 0))\
    REQUIRE( c(0, 0) == d(0, 0) );\
  if (c(1, 0) == c(1, 0) && d(1, 0) == d(1, 0))\
    REQUIRE( c(1, 0) == d(1, 0) );\
  if (c(2, 0) == c(2, 0) && d(2, 0) == d(2, 0))\
    REQUIRE( c(2, 0) == d(2, 0) );\
  if (c(0, 1) == c(0, 1) && d(0, 1) == d(0, 1))\
    REQUIRE( c(0, 1) == d(0, 1) );\
  if (c(1, 1) == c(1, 1) && d(1, 1) == d(1, 1))\
    REQUIRE( c(1, 1) == d(1, 1) );\
  if (c(2, 1) == c(2, 1) && d(2, 1) == d(2, 1))\
    REQUIRE( c(2, 1) == d(2, 1) );\
  if (c(0, 2) == c(0, 2) && d(0, 2) == d(0, 2))\
    REQUIRE( c(0, 2) == d(0, 2) );\
  if (c(1, 2) == c(1, 2) && d(1, 2) == d(1, 2))\
    REQUIRE( c(1, 2) == d(1, 2) );\
  if (c(2, 2) == c(2, 2) && d(2, 2) == d(2, 2))\
    REQUIRE( c(2, 2) == d(2, 2) );\
  \
  c *= EOP(a);\
  d *= EOP(b);\
  \
  if (c(0, 0) == c(0, 0) && d(0, 0) == d(0, 0))\
    REQUIRE( c(0, 0) == d(0, 0) );\
  if (c(1, 0) == c(1, 0) && d(1, 0) == d(1, 0))\
    REQUIRE( c(1, 0) == d(1, 0) );\
  if (c(2, 0) == c(2, 0) && d(2, 0) == d(2, 0))\
    REQUIRE( c(2, 0) == d(2, 0) );\
  if (c(0, 1) == c(0, 1) && d(0, 1) == d(0, 1))\
    REQUIRE( c(0, 1) == d(0, 1) );\
  if (c(1, 1) == c(1, 1) && d(1, 1) == d(1, 1))\
    REQUIRE( c(1, 1) == d(1, 1) );\
  if (c(2, 1) == c(2, 1) && d(2, 1) == d(2, 1))\
    REQUIRE( c(2, 1) == d(2, 1) );\
  if (c(0, 2) == c(0, 2) && d(0, 2) == d(0, 2))\
    REQUIRE( c(0, 2) == d(0, 2) );\
  if (c(1, 2) == c(1, 2) && d(1, 2) == d(1, 2))\
    REQUIRE( c(1, 2) == d(1, 2) );\
  if (c(2, 2) == c(2, 2) && d(2, 2) == d(2, 2))\
    REQUIRE( c(2, 2) == d(2, 2) );\
  \
  c /= EOP(a);\
  d /= EOP(b);\
  \
  if (c(0, 0) == c(0, 0) && d(0, 0) == d(0, 0))\
    REQUIRE( c(0, 0) == d(0, 0) );\
  if (c(1, 0) == c(1, 0) && d(1, 0) == d(1, 0))\
    REQUIRE( c(1, 0) == d(1, 0) );\
  if (c(2, 0) == c(2, 0) && d(2, 0) == d(2, 0))\
    REQUIRE( c(2, 0) == d(2, 0) );\
  if (c(0, 1) == c(0, 1) && d(0, 1) == d(0, 1))\
    REQUIRE( c(0, 1) == d(0, 1) );\
  if (c(1, 1) == c(1, 1) && d(1, 1) == d(1, 1))\
    REQUIRE( c(1, 1) == d(1, 1) );\
  if (c(2, 1) == c(2, 1) && d(2, 1) == d(2, 1))\
    REQUIRE( c(2, 1) == d(2, 1) );\
  if (c(0, 2) == c(0, 2) && d(0, 2) == d(0, 2))\
    REQUIRE( c(0, 2) == d(0, 2) );\
  if (c(1, 2) == c(1, 2) && d(1, 2) == d(1, 2))\
    REQUIRE( c(1, 2) == d(1, 2) );\
  if (c(2, 2) == c(2, 2) && d(2, 2) == d(2, 2))\
    REQUIRE( c(2, 2) == d(2, 2) );\
  }

// Now run all the operators...
TEST_OPERATOR("sp_mat_abs_test", abs)
//TEST_OPERATOR("sp_mat_eps_test", eps);
//TEST_OPERATOR(expTest, exp);
//TEST_OPERATOR(exp2Test, exp2);
//TEST_OPERATOR(exp10Test, exp10);
//TEST_OPERATOR(trunc_expTest, trunc_exp);
//TEST_OPERATOR(logTest, log);
//TEST_OPERATOR(log2Test, log2);
//TEST_OPERATOR(log10Test, log10);
//TEST_OPERATOR(trunc_logTest, trunc_log);
TEST_OPERATOR("sp_mat_sqrt_test", sqrt)
TEST_OPERATOR("sp_mat_square_test", square)
TEST_OPERATOR("sp_mat_floor_test", floor)
TEST_OPERATOR("sp_mat_ceil_test", ceil)
//TEST_OPERATOR(cosTest, cos);
//TEST_OPERATOR(acosTest, acos);
//TEST_OPERATOR(coshTest, cosh);
//TEST_OPERATOR(acoshTest, acosh);
//TEST_OPERATOR(sinTest, sin);
//TEST_OPERATOR(asinTest, asin);
//TEST_OPERATOR(sinhTest, sinh);
//TEST_OPERATOR(asinhTest, asinh);
//TEST_OPERATOR(tanTest, tan);
//TEST_OPERATOR(tanhTest, tanh);
//TEST_OPERATOR(atanTest, atan);
//TEST_OPERATOR(atanhTest, atanh);

/*
TEST_CASE("spmat_diskio_tests")
  {
  std::string file_names[] = {"raw_ascii.txt",
                              "raw_binary.bin",
                              "arma_ascii.csv",
                              "csv_ascii.csv",
                              "arma_binary.bin",
                              "pgm_binary.bin",
                              "coord_ascii.txt"};
  diskio dio;
  SpMat<int> m(4, 3);
  m(0, 0) = 1;
  m(3, 0) = 2;
  m(0, 2) = 3;
  m(3, 2) = 4;
  m(2, 1) = 5;
  m(1, 2) = 6;

  // Save the matrix.
  REQUIRE( dio.save_raw_ascii(m, file_names[0]) );
//  REQUIRE( dio.save_raw_binary(m, file_names[1]) );
//  REQUIRE( dio.save_arma_ascii(m, file_names[2]) );
//  REQUIRE( dio.save_csv_ascii(m, file_names[3]) );
  REQUIRE( dio.save_arma_binary(m, file_names[4]) );
//  REQUIRE( dio.save_pgm_binary(m, file_names[5]) );
  REQUIRE( dio.save_coord_ascii(m, file_names[6]) );

  // Load the files.
  SpMat<int> lm[7];
  std::string err;
  REQUIRE( dio.load_raw_ascii(lm[0], file_names[0], err) );
//  REQUIRE( dio.load_raw_binary(lm[1], file_names[1], err) );
//  REQUIRE( dio.load_arma_ascii(lm[2], file_names[2], err) );
//  REQUIRE( dio.load_csv_ascii(lm[3], file_names[3], err) );
  REQUIRE( dio.load_arma_binary(lm[4], file_names[4], err) );
//  REQUIRE( dio.load_pgm_binary(lm[5], file_names[5], err) );
  REQUIRE( dio.load_coord_ascii(lm[6], file_names[6], err) );

  // Now make sure all the matrices are identical.
  for (uword i = 0; i < 7; i++)
    {
    for (uword r = 0; r < 4; r++)
      {
      for (uword c = 0; c < 3; c++)
        {
        REQUIRE( m(r, c) == lm[i](r, c) );
        }
      }
    }

  for (uword i = 0; i < 7; ++i)
    {
    remove(file_names[i].c_str());
    }
  }
*/


TEST_CASE("min_test")
  {
  SpCol<double> a(5);

  a(0) = 3.0;
  a(2) = 1.0;

  double res = min(a);
  REQUIRE( res == Approx(0.0).margin(0.001) );

  a(0) = -3.0;
  a(2) = -1.0;

  res = min(a);
  REQUIRE( res == Approx(-3.0) );

  a(0) = 1.3;
  a(1) = 2.4;
  a(2) = 3.1;
  a(3) = 4.4;
  a(4) = 1.4;

  res = min(a);
  REQUIRE( res == Approx(1.3) );

  SpRow<double> b(5);

  b(0) = 3.0;
  b(2) = 1.0;

  res = min(b);
  REQUIRE( res == Approx(0.0).margin(0.001) );

  b(0) = -3.0;
  b(2) = -1.0;

  res = min(b);
  REQUIRE( res == Approx(-3.0) );

  b(0) = 1.3;
  b(1) = 2.4;
  b(2) = 3.1;
  b(3) = 4.4;
  b(4) = 1.4;

  res = min(b);
  REQUIRE( res == Approx(1.3) );

  SpMat<double> c(6, 5);

  c(0, 0) = 1.0;
  c(1, 0) = 3.0;
  c(2, 0) = 4.0;
  c(3, 0) = 0.6;
  c(4, 0) = 1.4;
  c(5, 0) = 1.2;
  c(3, 2) = 1.3;
  c(2, 3) = -4.0;
  c(4, 3) = -1.4;
  c(5, 2) = -3.4;
  c(5, 3) = -4.1;

  SpMat<double> r = min(c, 0);
  REQUIRE( r.n_rows == 1 );
  REQUIRE( r.n_cols == 5 );
  REQUIRE( (double) r(0, 0) == Approx(0.6) );
  REQUIRE( (double) r(0, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) r(0, 2) == Approx(-3.4) );
  REQUIRE( (double) r(0, 3) == Approx(-4.1) );
  REQUIRE( (double) r(0, 4) == Approx(0.0).margin(0.001) );

  r = min(c, 1);
  REQUIRE( r.n_rows == 6 );
  REQUIRE( r.n_cols == 1 );
  REQUIRE( (double) r(0, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) r(1, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) r(2, 0) == Approx(-4.0) );
  REQUIRE( (double) r(3, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) r(4, 0) == Approx(-1.4) );
  REQUIRE( (double) r(5, 0) == Approx(-4.1) );
  }


TEST_CASE("max_test")
  {
  SpCol<double> a(5);

  a(0) = -3.0;
  a(2) = -1.0;

  double resa = max(a);
  REQUIRE( resa == Approx(0.0).margin(0.001) );

  a(0) = 3.0;
  a(2) = 1.0;

  resa = max(a);
  REQUIRE( resa == Approx(3.0) );

  a(0) = -1.3;
  a(1) = -2.4;
  a(2) = -3.1;
  a(3) = -4.4;
  a(4) = -1.4;

  resa = max(a);
  REQUIRE( resa == Approx(-1.3) );

  SpRow<double> b(5);

  b(0) = -3.0;
  b(2) = -1.0;

  resa = max(b);
  REQUIRE( resa == Approx(0.0).margin(0.001) );

  b(0) = 3.0;
  b(2) = 1.0;

  resa = max(b);
  REQUIRE( resa == Approx(3.0) );

  b(0) = -1.3;
  b(1) = -2.4;
  b(2) = -3.1;
  b(3) = -4.4;
  b(4) = -1.4;

  resa = max(b);
  REQUIRE( resa == Approx(-1.3) );

  SpMat<double> c(6, 5);

  c(0, 0) = 1.0;
  c(1, 0) = 3.0;
  c(2, 0) = 4.0;
  c(3, 0) = 0.6;
  c(4, 0) = -1.4;
  c(5, 0) = 1.2;
  c(3, 2) = 1.3;
  c(2, 3) = -4.0;
  c(4, 3) = -1.4;
  c(5, 2) = -3.4;
  c(5, 3) = -4.1;

  SpMat<double> res = max(c, 0);
  REQUIRE( res.n_rows == 1 );
  REQUIRE( res.n_cols == 5 );
  REQUIRE( (double) res(0, 0) == Approx(4.0) );
  REQUIRE( (double) res(0, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) res(0, 2) == Approx(1.3) );
  REQUIRE( (double) res(0, 3) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) res(0, 4) == Approx(0.0).margin(0.001) );

  res = max(c, 1);
  REQUIRE( res.n_rows == 6 );
  REQUIRE( res.n_cols == 1 );
  REQUIRE( (double) res(0, 0) == Approx(1.0) );
  REQUIRE( (double) res(1, 0) == Approx(3.0) );
  REQUIRE( (double) res(2, 0) == Approx(4.0) );
  REQUIRE( (double) res(3, 0) == Approx(1.3) );
  REQUIRE( (double) res(4, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) res(5, 0) == Approx(1.2) );
  }


TEST_CASE("spmat_min_cx_test")
{
  SpCol<cx_double> a(5);

  a(0) = cx_double(3.0, -2.0);
  a(2) = cx_double(1.0, 1.0);

  cx_double res = min(a);
  REQUIRE( res.real() == Approx(0.0).margin(0.001) );
  REQUIRE( res.imag() == Approx(0.0).margin(0.001) );

  a(0) = cx_double(-3.0, -2.0);
  a(2) = cx_double(-1.0, -1.0);

  res = min(a);
  REQUIRE( res.real() == Approx(0.0).margin(0.001) );
  REQUIRE( res.imag() == Approx(0.0).margin(0.001) );

  a(0) = cx_double(1.0, 0.5);
  a(1) = cx_double(2.4, 1.4);
  a(2) = cx_double(0.5, 0.5);
  a(3) = cx_double(2.0, 2.0);
  a(4) = cx_double(1.4, -1.4);

  res = min(a);
  REQUIRE( res.real() == Approx(0.5) );
  REQUIRE( res.imag() == Approx(0.5) );

  SpRow<cx_double> b(5);

  b(0) = cx_double(3.0, -2.0);
  b(2) = cx_double(1.0, 1.0);

  res = min(b);
  REQUIRE( res.real() == Approx(0.0).margin(0.001) );
  REQUIRE( res.imag() == Approx(0.0).margin(0.001) );

  b(0) = cx_double(-3.0, -2.0);
  b(2) = cx_double(-1.0, -1.0);

  res = min(b);
  REQUIRE( res.real() == Approx(0.0).margin(0.001) );
  REQUIRE( res.imag() == Approx(0.0).margin(0.001) );

  b(0) = cx_double(1.0, 0.5);
  b(1) = cx_double(2.4, 1.4);
  b(2) = cx_double(0.5, 0.5);
  b(3) = cx_double(2.0, 2.0);
  b(4) = cx_double(1.4, -1.4);

  res = min(b);
  REQUIRE( res.real() == Approx(0.5) );
  REQUIRE( res.imag() == Approx(0.5) );

  SpMat<cx_double> c(4, 3);

  c(0, 0) = cx_double(1.0, 2.0);
  c(0, 1) = cx_double(0.5, 0.5);
  c(0, 2) = cx_double(2.0, 4.0);
  c(1, 1) = cx_double(-1.0, -2.0);
  c(2, 1) = cx_double(-3.0, -3.0);
  c(3, 1) = cx_double(0.25, 0.25);

  SpMat<cx_double> r = min(c, 0);
  REQUIRE( r.n_rows == 1 );
  REQUIRE( r.n_cols == 3 );
  REQUIRE( ((cx_double) r(0, 0)).real() == Approx(0.0).margin(0.001) );
  REQUIRE( ((cx_double) r(0, 0)).imag() == Approx(0.0).margin(0.001) );
  REQUIRE( ((cx_double) r(0, 1)).real() == Approx(0.25) );
  REQUIRE( ((cx_double) r(0, 1)).imag() == Approx(0.25) );
  REQUIRE( ((cx_double) r(0, 2)).real() == Approx(0.0).margin(0.001) );
  REQUIRE( ((cx_double) r(0, 2)).imag() == Approx(0.0).margin(0.001) );

  r = min(c, 1);
  REQUIRE( r.n_rows == 4 );
  REQUIRE( r.n_cols == 1 );
  REQUIRE( ((cx_double) r(0, 0)).real() == Approx(0.5) );
  REQUIRE( ((cx_double) r(0, 0)).imag() == Approx(0.5) );
  REQUIRE( ((cx_double) r(1, 0)).real() == Approx(0.0).margin(0.001) );
  REQUIRE( ((cx_double) r(1, 0)).imag() == Approx(0.0).margin(0.001) );
  REQUIRE( ((cx_double) r(2, 0)).real() == Approx(0.0).margin(0.001) );
  REQUIRE( ((cx_double) r(2, 0)).imag() == Approx(0.0).margin(0.001) );
  REQUIRE( ((cx_double) r(3, 0)).real() == Approx(0.0).margin(0.001) );
  REQUIRE( ((cx_double) r(3, 0)).imag() == Approx(0.0).margin(0.001) );
}



TEST_CASE("spmat_max_cx_test")
{
  SpCol<cx_double> a(5);

  a(0) = cx_double(3.0, -2.0);
  a(2) = cx_double(1.0, 1.0);

  cx_double res = max(a);
  REQUIRE( res.real() == Approx(3.0) );
  REQUIRE( res.imag() == Approx(-2.0) );

  a(0) = cx_double(0);
  a(2) = cx_double(0);

  res = max(a);
  REQUIRE( res.real() == Approx(0.0).margin(0.001) );
  REQUIRE( res.imag() == Approx(0.0).margin(0.001) );

  a(0) = cx_double(1.0, 0.5);
  a(1) = cx_double(2.4, 1.4);
  a(2) = cx_double(0.5, 0.5);
  a(3) = cx_double(2.0, 2.0);
  a(4) = cx_double(1.4, -1.4);

  res = max(a);
  REQUIRE( res.real() == Approx(2.0) );
  REQUIRE( res.imag() == Approx(2.0) );

  SpRow<cx_double> b(5);

  b(0) = cx_double(3.0, -2.0);
  b(2) = cx_double(1.0, 1.0);

  res = max(b);
  REQUIRE( res.real() == Approx(3.0) );
  REQUIRE( res.imag() == Approx(-2.0) );

  b(0) = cx_double(0);
  b(2) = cx_double(0);

  res = max(b);
  REQUIRE( res.real() == Approx(0.0).margin(0.001) );
  REQUIRE( res.imag() == Approx(0.0).margin(0.001) );

  b(0) = cx_double(1.0, 0.5);
  b(1) = cx_double(2.4, 1.4);
  b(2) = cx_double(0.5, 0.5);
  b(3) = cx_double(2.0, 2.0);
  b(4) = cx_double(1.4, -1.4);

  res = max(b);
  REQUIRE( res.real() == Approx(2.0) );
  REQUIRE( res.imag() == Approx(2.0) );

  SpMat<cx_double> c(4, 3);

  c(0, 0) = cx_double(1.0, 2.0);
  c(0, 1) = cx_double(0.5, 0.5);
  c(1, 1) = cx_double(-1.0, -2.0);
  c(2, 1) = cx_double(-3.0, -3.0);
  c(3, 1) = cx_double(0.25, 0.25);

  SpMat<cx_double> r = max(c, 0);
  REQUIRE( r.n_rows == 1 );
  REQUIRE( r.n_cols == 3 );
  REQUIRE( ((cx_double) r(0, 0)).real() == Approx(1.0) );
  REQUIRE( ((cx_double) r(0, 0)).imag() == Approx(2.0) );
  REQUIRE( ((cx_double) r(0, 1)).real() == Approx(-3.0) );
  REQUIRE( ((cx_double) r(0, 1)).imag() == Approx(-3.0) );
  REQUIRE( ((cx_double) r(0, 2)).real() == Approx(0.0).margin(0.001) );
  REQUIRE( ((cx_double) r(0, 2)).imag() == Approx(0.0).margin(0.001) );

  r = max(c, 1);
  REQUIRE( r.n_rows == 4 );
  REQUIRE( r.n_cols == 1 );
  REQUIRE( ((cx_double) r(0, 0)).real() == Approx(1.0) );
  REQUIRE( ((cx_double) r(0, 0)).imag() == Approx(2.0) );
  REQUIRE( ((cx_double) r(1, 0)).real() == Approx(-1.0) );
  REQUIRE( ((cx_double) r(1, 0)).imag() == Approx(-2.0) );
  REQUIRE( ((cx_double) r(2, 0)).real() == Approx(-3.0) );
  REQUIRE( ((cx_double) r(2, 0)).imag() == Approx(-3.0) );
  REQUIRE( ((cx_double) r(3, 0)).real() == Approx(0.25) );
  REQUIRE( ((cx_double) r(3, 0)).imag() == Approx(0.25) );
  }



TEST_CASE("spmat_complex_constructor_test")
  {
  // First make two sparse matrices.
  SpMat<double> a(8, 10);
  SpMat<double> b(8, 10);

  a(0, 0) = 4;
  a(4, 2) = 5;
  a(5, 3) = 6;
  a(6, 3) = 7;
  a(1, 4) = 1;
  a(5, 4) = 6;
  a(7, 6) = 3;
  a(0, 7) = 2;
  a(3, 7) = 3;

  b(0, 0) = 4;
  b(4, 2) = 5;
  b(7, 3) = 4;
  b(1, 4) = 1;
  b(3, 4) = 6;
  b(5, 4) = -1;
  b(6, 4) = 2;
  b(7, 4) = 3;
  b(6, 5) = 2;
  b(6, 6) = 3;
  b(3, 7) = 4;
  b(6, 7) = 5;

  SpMat<cx_double> c(a, b);

  REQUIRE( c.n_nonzero == 16 );
  REQUIRE( (cx_double) c(0, 0) == cx_double(4, 4) );
  REQUIRE( (cx_double) c(4, 2) == cx_double(5, 5) );
  REQUIRE( (cx_double) c(5, 3) == cx_double(6, 0) );
  REQUIRE( (cx_double) c(6, 3) == cx_double(7, 0) );
  REQUIRE( (cx_double) c(7, 3) == cx_double(0, 4) );
  REQUIRE( (cx_double) c(1, 4) == cx_double(1, 1) );
  REQUIRE( (cx_double) c(3, 4) == cx_double(0, 6) );
  REQUIRE( (cx_double) c(5, 4) == cx_double(6, -1) );
  REQUIRE( (cx_double) c(6, 4) == cx_double(0, 2) );
  REQUIRE( (cx_double) c(7, 4) == cx_double(0, 3) );
  REQUIRE( (cx_double) c(6, 5) == cx_double(0, 2) );
  REQUIRE( (cx_double) c(6, 6) == cx_double(0, 3) );
  REQUIRE( (cx_double) c(7, 6) == cx_double(3, 0) );
  REQUIRE( (cx_double) c(0, 7) == cx_double(2, 0) );
  REQUIRE( (cx_double) c(3, 7) == cx_double(3, 4) );
  REQUIRE( (cx_double) c(6, 7) == cx_double(0, 5) );
  }



TEST_CASE("spmat_unary_operators_test")
  {
  SpMat<int> a(3, 3);
  SpMat<int> b(3, 3);

  a(0, 0) = 1;
  a(1, 2) = 4;
  a(2, 2) = 5;

  b(0, 1) = 1;
  b(1, 0) = 2;
  b(1, 2) = -4;
  b(2, 2) = 5;

  SpMat<int> c = a + b;

  REQUIRE( c.n_nonzero == 4 );

  REQUIRE( (double) c(0, 0) == 1 );
  REQUIRE( (double) c(1, 0) == 2 );
  REQUIRE( (double) c(2, 0) == 0 );
  REQUIRE( (double) c(0, 1) == 1 );
  REQUIRE( (double) c(1, 1) == 0 );
  REQUIRE( (double) c(2, 1) == 0 );
  REQUIRE( (double) c(0, 2) == 0 );
  REQUIRE( (double) c(1, 2) == 0 );
  REQUIRE( (double) c(2, 2) == 10 );

  c = a - b;

  REQUIRE( c.n_nonzero == 4 );

  REQUIRE( (double) c(0, 0) == 1 );
  REQUIRE( (double) c(1, 0) == -2 );
  REQUIRE( (double) c(2, 0) == 0 );
  REQUIRE( (double) c(0, 1) == -1 );
  REQUIRE( (double) c(1, 1) == 0 );
  REQUIRE( (double) c(2, 1) == 0 );
  REQUIRE( (double) c(0, 2) == 0 );
  REQUIRE( (double) c(1, 2) == 8 );
  REQUIRE( (double) c(2, 2) == 0 );

  c = a % b;

  REQUIRE( c.n_nonzero == 2 );

  REQUIRE( (double) c(0, 0) == 0 );
  REQUIRE( (double) c(1, 0) == 0 );
  REQUIRE( (double) c(2, 0) == 0 );
  REQUIRE( (double) c(0, 1) == 0 );
  REQUIRE( (double) c(1, 1) == 0 );
  REQUIRE( (double) c(2, 1) == 0 );
  REQUIRE( (double) c(0, 2) == 0 );
  REQUIRE( (double) c(1, 2) == -16 );
  REQUIRE( (double) c(2, 2) == 25 );

  a(0, 0) = 4;
  b(0, 0) = 2;
/*
  c = a / b;

  REQUIRE( c.n_nonzero == 3 );

  REQUIRE( (double) c(0, 0) == 2 );
  REQUIRE( (double) c(1, 0) == 0 );
  REQUIRE( (double) c(2, 0) == 0 );
  REQUIRE( (double) c(0, 1) == 0 );
  REQUIRE( (double) c(1, 1) == 0 );
  REQUIRE( (double) c(2, 1) == 0 );
  REQUIRE( (double) c(0, 2) == 0 );
  REQUIRE( (double) c(1, 2) == -1 );
  REQUIRE( (double) c(2, 2) == 1 );
*/
  }



TEST_CASE("spmat_unary_val_operators_test")
  {
  SpMat<double> a(2, 2);

  a(0, 0) = 2.0;
  a(1, 1) = -3.0;

  SpMat<double> b = a * 3.0;

  REQUIRE( b.n_nonzero == 2 );
  REQUIRE( (double) b(0, 0) == Approx(6.0) );
  REQUIRE( (double) b(0, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) b(1, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) b(1, 1) == Approx(-9.0) );

  b = a / 3.0;

  REQUIRE( b.n_nonzero == 2 );
  REQUIRE( (double) b(0, 0) == Approx(2.0 / 3.0) );
  REQUIRE( (double) b(0, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) b(1, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) b(1, 1) == Approx(-1.0) );
  }


TEST_CASE("spmat_sparse_unary_multiplication_test")
  {
  SpMat<double> spaa(10, 10);
  spaa(1, 5) = 0.4;
  spaa(0, 4) = 0.3;
  spaa(0, 8) = 1.2;
  spaa(3, 0) = 1.1;
  spaa(3, 1) = 1.1;
  spaa(3, 2) = 1.1;
  spaa(4, 4) = 0.2;
  spaa(4, 9) = 0.1;
  spaa(6, 2) = 4.1;
  spaa(6, 8) = 4.1;
  spaa(7, 5) = 1.0;
  spaa(8, 9) = 0.4;
  spaa(9, 4) = 0.4;

  double correctResultB[10][10] =
    {{ 0.00, 0.00, 0.00, 0.00, 0.06, 0.00, 0.00, 0.00, 0.00, 0.51 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.33, 0.44, 0.00, 0.00, 1.32, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.08, 0.00, 0.00, 0.00, 0.00, 0.02 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.64 },
     { 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.16, 0.00, 0.00, 0.00, 0.00, 0.00 },
     { 0.00, 0.00, 0.00, 0.00, 0.08, 0.00, 0.00, 0.00, 0.00, 0.04 }};

  SpMat<double> spab = spaa * spaa;

  for (uword i = 0; i < 10; i++)
    {
    for (uword j = 0; j < 10; j++)
      {
      REQUIRE( (double) spab(i, j) == Approx(correctResultB[i][j]) );
      }
    }

  SpMat<double> spac(15, 15);
  spac(6, 10) = 0.4;
  spac(5, 9) = 0.3;
  spac(5, 13) = 1.2;
  spac(8, 5) = 1.1;
  spac(8, 6) = 1.1;
  spac(8, 7) = 1.1;
  spac(9, 9) = 0.2;
  spac(9, 14) = 0.1;
  spac(11, 7) = 4.1;
  spac(11, 13) = 4.1;
  spac(12, 10) = 1.0;
  spac(13, 14) = 0.4;
  spac(14, 9) = 0.4;

  spab = spaa * spac.submat(5, 5, 14, 14);

  for (uword i = 0; i < 10; i++)
    {
    for (uword j = 0; j < 10; j++)
      {
      REQUIRE( (double) spab(i, j) == Approx(correctResultB[i][j]) );
      }
    }
  }



TEST_CASE("spmat_unary_operator_test_2")
  {
  SpMat<double> a(3, 3);
  a(0, 0) = 1;
  a(0, 2) = 3.5;
  a(1, 2) = 4.0;
  a(2, 2) = -3.0;

  mat b(3, 3);
  b.fill(3.0);

  mat c = a + b;

  REQUIRE( c(0, 0) == Approx(4.0) );
  REQUIRE( c(1, 0) == Approx(3.0) );
  REQUIRE( c(2, 0) == Approx(3.0) );
  REQUIRE( c(0, 1) == Approx(3.0) );
  REQUIRE( c(1, 1) == Approx(3.0) );
  REQUIRE( c(2, 1) == Approx(3.0) );
  REQUIRE( c(0, 2) == Approx(6.5) );
  REQUIRE( c(1, 2) == Approx(7.0) );
  REQUIRE( c(2, 2) == Approx(0.0).margin(0.001) );

  c = a - b;

  REQUIRE( c(0, 0) == Approx(-2.0) );
  REQUIRE( c(1, 0) == Approx(-3.0) );
  REQUIRE( c(2, 0) == Approx(-3.0) );
  REQUIRE( c(0, 1) == Approx(-3.0) );
  REQUIRE( c(1, 1) == Approx(-3.0) );
  REQUIRE( c(2, 1) == Approx(-3.0) );
  REQUIRE( c(0, 2) == Approx(0.5) );
  REQUIRE( c(1, 2) == Approx(1.0) );
  REQUIRE( c(2, 2) == Approx(-6.0) );

  SpMat<double> d = a % b;

  REQUIRE( d.n_nonzero == 4 );
  REQUIRE( (double) d(0, 0) == Approx(3.0) );
  REQUIRE( (double) d(1, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(2, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(0, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(1, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(2, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(0, 2) == Approx(10.5) );
  REQUIRE( (double) d(1, 2) == Approx(12.0) );
  REQUIRE( (double) d(2, 2) == Approx(-9.0) );

  d = a / b;

  REQUIRE( d.n_nonzero == 4 );
  REQUIRE( (double) d(0, 0) == Approx((1.0 / 3.0)) );
  REQUIRE( (double) d(1, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(2, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(0, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(1, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(2, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) d(0, 2) == Approx((3.5 / 3.0)) );
  REQUIRE( (double) d(1, 2) == Approx((4.0 / 3.0)) );
  REQUIRE( (double) d(2, 2) == Approx(-1.0) );

  c = a * b;

  REQUIRE( (double) c(0, 0) == Approx(13.5) );
  REQUIRE( (double) c(1, 0) == Approx(12.0) );
  REQUIRE( (double) c(2, 0) == Approx(-9.0) );
  REQUIRE( (double) c(0, 1) == Approx(13.5) );
  REQUIRE( (double) c(1, 1) == Approx(12.0) );
  REQUIRE( (double) c(2, 1) == Approx(-9.0) );
  REQUIRE( (double) c(0, 2) == Approx(13.5) );
  REQUIRE( (double) c(1, 2) == Approx(12.0) );
  REQUIRE( (double) c(2, 2) == Approx(-9.0) );

  c = b * a;

  REQUIRE( (double) c(0, 0) == Approx(3.0) );
  REQUIRE( (double) c(1, 0) == Approx(3.0) );
  REQUIRE( (double) c(2, 0) == Approx(3.0) );
  REQUIRE( (double) c(0, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(1, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(2, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(0, 2) == Approx(13.5) );
  REQUIRE( (double) c(1, 2) == Approx(13.5) );
  REQUIRE( (double) c(2, 2) == Approx(13.5) );
  }



TEST_CASE("spmat_mat_operator_tests")
  {
  SpMat<double> a(3, 3);
  a(0, 0) = 2.0;
  a(1, 2) = 3.5;
  a(2, 1) = -2.0;
  a(2, 2) = 4.5;

  mat b(3, 3);
  b.fill(2.0);

  mat c(b);

  c += a;

  REQUIRE( (double) c(0, 0) == Approx(4.0) );
  REQUIRE( (double) c(1, 0) == Approx(2.0) );
  REQUIRE( (double) c(2, 0) == Approx(2.0) );
  REQUIRE( (double) c(0, 1) == Approx(2.0) );
  REQUIRE( (double) c(1, 1) == Approx(2.0) );
  REQUIRE( (double) c(2, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(0, 2) == Approx(2.0) );
  REQUIRE( (double) c(1, 2) == Approx(5.5) );
  REQUIRE( (double) c(2, 2) == Approx(6.5) );

  c = b + a;

  REQUIRE( (double) c(0, 0) == Approx(4.0) );
  REQUIRE( (double) c(1, 0) == Approx(2.0) );
  REQUIRE( (double) c(2, 0) == Approx(2.0) );
  REQUIRE( (double) c(0, 1) == Approx(2.0) );
  REQUIRE( (double) c(1, 1) == Approx(2.0) );
  REQUIRE( (double) c(2, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(0, 2) == Approx(2.0) );
  REQUIRE( (double) c(1, 2) == Approx(5.5) );
  REQUIRE( (double) c(2, 2) == Approx(6.5) );

  c = b;
  c -= a;

  REQUIRE( (double) c(0, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(1, 0) == Approx(2.0) );
  REQUIRE( (double) c(2, 0) == Approx(2.0) );
  REQUIRE( (double) c(0, 1) == Approx(2.0) );
  REQUIRE( (double) c(1, 1) == Approx(2.0) );
  REQUIRE( (double) c(2, 1) == Approx(4.0) );
  REQUIRE( (double) c(0, 2) == Approx(2.0) );
  REQUIRE( (double) c(1, 2) == Approx(-1.5) );
  REQUIRE( (double) c(2, 2) == Approx(-2.5) );

  c = b - a;

  REQUIRE( (double) c(0, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(1, 0) == Approx(2.0) );
  REQUIRE( (double) c(2, 0) == Approx(2.0) );
  REQUIRE( (double) c(0, 1) == Approx(2.0) );
  REQUIRE( (double) c(1, 1) == Approx(2.0) );
  REQUIRE( (double) c(2, 1) == Approx(4.0) );
  REQUIRE( (double) c(0, 2) == Approx(2.0) );
  REQUIRE( (double) c(1, 2) == Approx(-1.5) );
  REQUIRE( (double) c(2, 2) == Approx(-2.5) );

  c = b;
  c *= a;

  REQUIRE( (double) c(0, 0) == Approx(4.0) );
  REQUIRE( (double) c(1, 0) == Approx(4.0) );
  REQUIRE( (double) c(2, 0) == Approx(4.0) );
  REQUIRE( (double) c(0, 1) == Approx(-4.0) );
  REQUIRE( (double) c(1, 1) == Approx(-4.0) );
  REQUIRE( (double) c(2, 1) == Approx(-4.0) );
  REQUIRE( (double) c(0, 2) == Approx(16.0) );
  REQUIRE( (double) c(1, 2) == Approx(16.0) );
  REQUIRE( (double) c(2, 2) == Approx(16.0) );

  mat e = b * a;

  REQUIRE( (double) e(0, 0) == Approx(4.0) );
  REQUIRE( (double) e(1, 0) == Approx(4.0) );
  REQUIRE( (double) e(2, 0) == Approx(4.0) );
  REQUIRE( (double) e(0, 1) == Approx(-4.0) );
  REQUIRE( (double) e(1, 1) == Approx(-4.0) );
  REQUIRE( (double) e(2, 1) == Approx(-4.0) );
  REQUIRE( (double) e(0, 2) == Approx(16.0) );
  REQUIRE( (double) e(1, 2) == Approx(16.0) );
  REQUIRE( (double) e(2, 2) == Approx(16.0) );

  c = b;
  c %= a;

  REQUIRE( (double) c(0, 0) == Approx(4.0) );
  REQUIRE( (double) c(1, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(2, 0) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(0, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(1, 1) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(2, 1) == Approx(-4.0) );
  REQUIRE( (double) c(0, 2) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) c(1, 2) == Approx(7.0) );
  REQUIRE( (double) c(2, 2) == Approx(9.0) );

  SpMat<double> d = b % a;

  REQUIRE( d.n_nonzero == 4 );
  REQUIRE( (double) c(0, 0) == Approx(4.0) );
  REQUIRE( (double) c(2, 1) == Approx(-4.0) );
  REQUIRE( (double) c(1, 2) == Approx(7.0) );
  REQUIRE( (double) c(2, 2) == Approx(9.0) );

  c = b;
  c /= a;

  REQUIRE( c(0, 0) == Approx(1.0) );
  REQUIRE( std::isinf(c(1, 0)) );
  REQUIRE( std::isinf(c(2, 0)) );
  REQUIRE( std::isinf(c(0, 1)) );
  REQUIRE( std::isinf(c(1, 1)) );
  REQUIRE( c(2, 1) == Approx(-1.0) );
  REQUIRE( std::isinf(c(0, 2)) );
  REQUIRE( c(1, 2) == Approx(2.0 / 3.5) );
  REQUIRE( c(2, 2) == Approx(2.0 / 4.5) );
  }


TEST_CASE("spmat_empty_hadamard")
  {
  SpMat<double> x(5, 5), y(5, 5), z;

  z = x % y;

  REQUIRE( z.n_nonzero == 0 );
  REQUIRE( z.n_rows == 5 );
  REQUIRE( z.n_cols == 5 );
  }



TEST_CASE("spmat_sparse_dense_in_place")
  {
  SpMat<double> a;
  a.sprandu(50, 50, 0.1);
  mat b;
  b.randu(50, 50);
  mat d( a);

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) a(r, c) != 0)
        REQUIRE( (double) a(r, c) == Approx(d(r, c)) );
      else
        REQUIRE( d(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  SpMat<double> x;
  mat y;

  x = a;
  y = d;

  x *= b;
  y *= b;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) a(r, c) != 0)
        REQUIRE( (double) a(r, c) == Approx(d(r, c)) );
      else
        REQUIRE( d(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  x = a;
  y = d;

  x /= b;
  y /= b;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) a(r, c) != 0)
        REQUIRE( (double) a(r, c) == Approx(d(r, c)) );
      else
        REQUIRE( d(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  x = a;
  y = d;

  x %= b;
  y %= b;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) a(r, c) != 0)
        REQUIRE( (double) a(r, c) == Approx(d(r, c)) );
      else
        REQUIRE(d(r, c) == Approx(0.0).margin(0.001) );
      }
    }
  }



TEST_CASE("spmat_sparse_dense_not_in_place")
  {
  SpMat<double> a;
  a.sprandu(50, 50, 0.1);
  mat b;
  b.randu(50, 50);
  mat d(a);

  SpMat<double> x;
  mat y;
  mat z;

  y = a + b;
  z = d + b;

  for (uword c = 0; c < 50; ++c)
    {
    for(uword r = 0; r < 50; ++r)
      {
      if ((double) y(r, c) != 0)
        REQUIRE( (double) y(r, c) == Approx(z(r, c)) );
      else
        REQUIRE( z(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  y = a - b;
  z = d - b;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) y(r, c) != 0)
        REQUIRE( (double) y(r, c) == Approx(z(r, c)) );
      else
        REQUIRE( z(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  y = a * b;
  z = d * b;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) y(r, c) != 0)
        REQUIRE( (double) y(r, c) == Approx(z(r, c)) );
      else
        REQUIRE( z(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  y = a % b;
  z = d % b;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) y(r, c) != 0)
        REQUIRE( (double) y(r, c) == Approx(z(r, c)) );
      else
        REQUIRE( z(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  y = a / b;
  z = d / b;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) y(r, c) != 0)
        REQUIRE( (double) y(r, c) == Approx(z(r, c)) );
      else
        REQUIRE( z(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  y = b + a;
  z = b + d;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) y(r, c) != 0)
        REQUIRE( (double) y(r, c) == Approx(z(r, c)) );
      else
        REQUIRE( z(r, c) == Approx(0.0).margin(0.001) );
    }
  }

  y = b - a;
  z = b - d;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) y(r, c) != 0)
        REQUIRE( (double) y(r, c) == Approx(z(r, c)) );
      else
        REQUIRE( z(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  y = b * a;
  z = b * d;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) y(r, c) != 0)
        REQUIRE( (double) y(r, c) == Approx(z(r, c)) );
      else
        REQUIRE( z(r, c) == Approx(0.0).margin(0.001) );
      }
    }

  y = b % a;
  z = b % d;

  for (uword c = 0; c < 50; ++c)
    {
    for (uword r = 0; r < 50; ++r)
      {
      if ((double) y(r, c) != 0)
        REQUIRE( (double) y(r, c) == Approx(z(r, c)) );
      else
        REQUIRE( z(r, c) == Approx(0.0).margin(0.001) );
      }
    }
  }



TEST_CASE("spmat_batch_insert_test")
  {
  Mat<uword> locations(2, 5);
  locations(1, 0) = 1;
  locations(0, 0) = 2;
  locations(1, 1) = 1;
  locations(0, 1) = 7;
  locations(1, 2) = 4;
  locations(0, 2) = 0;
  locations(1, 3) = 4;
  locations(0, 3) = 9;
  locations(1, 4) = 5;
  locations(0, 4) = 0;

  Col<double> values(5);
  values(0) = 1.5;
  values(1) = -15.15;
  values(2) = 2.2;
  values(3) = 3.0;
  values(4) = 5.0;

  SpMat<double> m(locations, values, 10, 10, true);

  REQUIRE( m.n_nonzero == 5 );
  REQUIRE( m.n_rows == 10 );
  REQUIRE( m.n_cols == 10 );
  REQUIRE( (double) m(2, 1) == Approx(1.5) );
  REQUIRE( (double) m(7, 1) == Approx(-15.15) );
  REQUIRE( (double) m(0, 4) == Approx(2.2) );
  REQUIRE( (double) m(9, 4) == Approx(3.0) );
  REQUIRE( (double) m(0, 5) == Approx(5.0) );
  REQUIRE( m.col_ptrs[11] == std::numeric_limits<uword>::max() );

  // Auto size detection.
  SpMat<double> n(locations, values, true);

  REQUIRE( n.n_nonzero == 5 );
  REQUIRE( n.n_rows == 10 );
  REQUIRE( n.n_cols == 6 );
  REQUIRE( (double) n(2, 1) == Approx(1.5) );
  REQUIRE( (double) n(7, 1) == Approx(-15.15) );
  REQUIRE( (double) n(0, 4) == Approx(2.2) );
  REQUIRE( (double) n(9, 4) == Approx(3.0) );
  REQUIRE( (double) n(0, 5) == Approx(5.0) );
  REQUIRE( n.col_ptrs[7] == std::numeric_limits<uword>::max() );
  }



TEST_CASE("spmat_batch_insert_unsorted_test")
  {
  Mat<uword> locations(2, 5);
  locations(1, 0) = 4;
  locations(0, 0) = 0;
  locations(1, 1) = 1;
  locations(0, 1) = 2;
  locations(1, 2) = 4;
  locations(0, 2) = 9;
  locations(1, 3) = 5;
  locations(0, 3) = 0;
  locations(1, 4) = 1;
  locations(0, 4) = 7;

  Col<double> values(5);
  values(1) = 1.5;
  values(4) = -15.15;
  values(0) = 2.2;
  values(2) = 3.0;
  values(3) = 5.0;

  SpMat<double> m(locations, values, 10, 10, true);

  REQUIRE( m.n_nonzero == 5 );
  REQUIRE( m.n_rows == 10 );
  REQUIRE( m.n_cols == 10 );
  REQUIRE( (double) m(2, 1) == Approx(1.5) );
  REQUIRE( (double) m(7, 1) == Approx(-15.15) );
  REQUIRE( (double) m(0, 4) == Approx(2.2) );
  REQUIRE( (double) m(9, 4) == Approx(3.0) );
  REQUIRE( (double) m(0, 5) == Approx(5.0) );

  // Auto size detection.
  SpMat<double> n(locations, values, true);

  REQUIRE( n.n_nonzero == 5 );
  REQUIRE( n.n_rows == 10 );
  REQUIRE( n.n_cols == 6 );
  REQUIRE( (double) n(2, 1) == Approx(1.5) );
  REQUIRE( (double) n(7, 1) == Approx(-15.15) );
  REQUIRE( (double) n(0, 4) == Approx(2.2) );
  REQUIRE( (double) n(9, 4) == Approx(3.0) );
  REQUIRE( (double) n(0, 5) == Approx(5.0) );
  }



TEST_CASE("spmat_batch_insert_empty_test")
  {
  Mat<uword> locations(2, 0);
  Col<double> values;

  SpMat<double> m(locations, values, 10, 10, false);

  REQUIRE( m.n_nonzero == 0 );
  REQUIRE( m.n_rows == 10 );
  REQUIRE( m.n_cols == 10 );
  REQUIRE( m.col_ptrs[11] == std::numeric_limits<uword>::max() );

  SpMat<double> n(locations, values, false);

  REQUIRE( n.n_nonzero == 0 );
  REQUIRE( n.n_rows == 0 );
  REQUIRE( n.n_cols == 0 );
  REQUIRE( n.col_ptrs[1] == std::numeric_limits<uword>::max() );

  SpMat<double> o(locations, values, 10, 10, true);

  REQUIRE( o.n_nonzero == 0 );
  REQUIRE( o.n_rows == 10 );
  REQUIRE( o.n_cols == 10 );
  REQUIRE( o.col_ptrs[11] == std::numeric_limits<uword>::max() );

  SpMat<double> p(locations, values, true);

  REQUIRE( p.n_nonzero == 0 );
  REQUIRE( p.n_rows == 0 );
  REQUIRE( p.n_cols == 0 );
  REQUIRE( p.col_ptrs[1] == std::numeric_limits<uword>::max() );
  }


// Make sure a matrix is the same as the other one.
template<typename T1, typename T2>
void CheckMatrices(const T1& a, const T2& b)
{
  REQUIRE( a.n_rows == b.n_rows );
  REQUIRE( a.n_cols == b.n_cols );
  for (uword i = 0; i < a.n_elem; ++i)
    REQUIRE( (double) a(i) == Approx((double) b(i)) );
}

// Test the constructor written by Dirk.
TEST_CASE("spmat_dirk_constructor_test")
  {
  // Come up with some values and stuff.
  vec values = "4.0 2.0 1.0 3.2 1.2 3.5";
  Col<uword> row_indices = "1 3 1 2 4 5";
  Col<uword> col_ptrs = "0 2 2 3 4 6";

  // Ok, now make a matrix.
  sp_mat M(row_indices, col_ptrs, values, 6, 5);
  
  REQUIRE( M.n_nonzero == 6 );

  // Make the equivalent dense matrix.
  mat D(6, 5);
  D.fill(0);
  D(1, 0) = 4.0;
  D(3, 0) = 2.0;
  D(1, 2) = 1.0;
  D(2, 3) = 3.2;
  D(4, 4) = 1.2;
  D(5, 4) = 3.5;

  // So now let's just do a bunch of operations and make sure everything is the
  // same.
  sp_mat dm = M * M.t();
  mat dd = D * D.t();

  CheckMatrices(dm, dd);

  dm = M.t() * M;
  dd = D.t() * D;

  CheckMatrices(dm, dd);

  sp_mat am = M + M;
  mat ad = D + D;

  CheckMatrices(am, ad);

  dm = M + D;
  ad = D + M;

  CheckMatrices(dm, ad);
  }



TEST_CASE("spmat_dirk_constructor_test2")
  {
  // note the zero at (1,1)
   vec values      = "4.0 2.0 0.0 1.0 3.2 1.2 3.5";
  uvec row_indices = "1 3 1 1 2 4 5";
  uvec col_ptrs    = "0 2 3 4 5 7";
  
  // Ok, now make a matrix.
  sp_mat M(row_indices, col_ptrs, values, 6, 5);
  
  REQUIRE( M.n_nonzero == 6 );

  // Make the equivalent dense matrix.
  mat D(6, 5);
  D.fill(0);
  D(1, 0) = 4.0;
  D(3, 0) = 2.0;
  D(1, 1) = 0.0;
  D(1, 2) = 1.0;
  D(2, 3) = 3.2;
  D(4, 4) = 1.2;
  D(5, 4) = 3.5;

  // So now let's just do a bunch of operations and make sure everything is the
  // same.
  sp_mat dm = M * M.t();
  mat dd = D * D.t();

  CheckMatrices(dm, dd);

  dm = M.t() * M;
  dd = D.t() * D;

  CheckMatrices(dm, dd);

  sp_mat am = M + M;
  mat ad = D + D;

  CheckMatrices(am, ad);

  dm = M + D;
  ad = D + M;

  CheckMatrices(dm, ad);
  }



TEST_CASE("spmat_clear_test")
  {
  sp_mat x;
  x.sprandu(10, 10, 0.6);

  x.clear();

  REQUIRE( x.n_cols == 0 );
  REQUIRE( x.n_rows == 0 );
  REQUIRE( x.n_nonzero == 0 );
  }



TEST_CASE("spmat_batch_insert_zeroes_test")
  {
  Mat<uword> locations(2, 5);
  locations(1, 0) = 1;
  locations(0, 0) = 2;
  locations(1, 1) = 1;
  locations(0, 1) = 7;
  locations(1, 2) = 4;
  locations(0, 2) = 0;
  locations(1, 3) = 4;
  locations(0, 3) = 9;
  locations(1, 4) = 5;
  locations(0, 4) = 0;

  Col<double> values(5);
  values(0) = 1.5;
  values(1) = -15.15;
  values(2) = 2.2;
  values(3) = 0.0;
  values(4) = 5.0;

  SpMat<double> m(locations, values, 10, 10, false, true);

  REQUIRE( m.n_nonzero == 4 );
  REQUIRE( m.n_rows == 10 );
  REQUIRE( m.n_cols == 10 );
  REQUIRE( (double) m(2, 1) == Approx(1.5) );
  REQUIRE( (double) m(7, 1) == Approx(-15.15) );
  REQUIRE( (double) m(0, 4) == Approx(2.2) );
  REQUIRE( (double) m(9, 4) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) m(0, 5) == Approx(5.0) );

  // Auto size detection.
  SpMat<double> n(locations, values, false);

  REQUIRE( n.n_nonzero == 4 );
  REQUIRE( n.n_rows == 10 );
  REQUIRE( n.n_cols == 6 );
  REQUIRE( (double) n(2, 1) == Approx(1.5) );
  REQUIRE( (double) n(7, 1) == Approx(-15.15) );
  REQUIRE( (double) n(0, 4) == Approx(2.2) );
  REQUIRE( (double) n(9, 4) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) n(0, 5) == Approx(5.0) );
  }



TEST_CASE("spmat_batch_insert_unsorted_case_zeroes")
  {
  Mat<uword> locations(2, 5);
  locations(1, 0) = 4;
  locations(0, 0) = 0;
  locations(1, 1) = 1;
  locations(0, 1) = 2;
  locations(1, 2) = 4;
  locations(0, 2) = 9;
  locations(1, 3) = 5;
  locations(0, 3) = 0;
  locations(1, 4) = 1;
  locations(0, 4) = 7;

  Col<double> values(5);
  values(1) = 1.5;
  values(4) = -15.15;
  values(0) = 2.2;
  values(2) = 0.0;
  values(3) = 5.0;

  SpMat<double> m(locations, values, 10, 10, true);

  REQUIRE( m.n_nonzero == 4 );
  REQUIRE( m.n_rows == 10 );
  REQUIRE( m.n_cols == 10 );
  REQUIRE( (double) m(2, 1) == Approx(1.5) );
  REQUIRE( (double) m(7, 1) == Approx(-15.15) );
  REQUIRE( (double) m(0, 4) == Approx(2.2) );
  REQUIRE( (double) m(9, 4) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) m(0, 5) == Approx(5.0) );
  REQUIRE( m.col_ptrs[11] == std::numeric_limits<uword>::max() );

  // Auto size detection.
  SpMat<double> n(locations, values, true);

  REQUIRE( n.n_nonzero == 4 );
  REQUIRE( n.n_rows == 10 );
  REQUIRE( n.n_cols == 6 );
  REQUIRE( (double) n(2, 1) == Approx(1.5) );
  REQUIRE( (double) n(7, 1) == Approx(-15.15) );
  REQUIRE( (double) n(0, 4) == Approx(2.2) );
  REQUIRE( (double) n(9, 4) == Approx(0.0).margin(0.001) );
  REQUIRE( (double) n(0, 5) == Approx(5.0) );
  REQUIRE( n.col_ptrs[7] == std::numeric_limits<uword>::max() );
  }



TEST_CASE("spmat_const_row_col_iterator_test")
  {
  mat X;
  X.zeros(5, 5);
  for (uword i = 0; i < 5; ++i)
    {
    X.col(i) += i;
    }

  for (uword i = 0; i < 5; ++i)
    {
    X.row(i) += 3 * i;
    }

  // Make sure default constructor works okay.
  mat::const_row_col_iterator it;
  // Make sure ++ operator, operator* and comparison operators work fine.
  uword count = 0;
  for (it = X.begin_row_col(); it != X.end_row_col(); ++it)
    {
    // Check iterator value.
    REQUIRE( *it == (count % 5) * 3 + (count / 5) );

    // Check iterator position.
    REQUIRE( it.row() == count % 5 );
    REQUIRE( it.col() == count / 5 );

    count++;
    }
  REQUIRE( count == 25 );
  it = X.end_row_col();
  do
    {
    --it;
    count--;

    // Check iterator value.
    REQUIRE( *it == (count % 5) * 3 + (count / 5) );

    // Check iterator position.
    REQUIRE( it.row() == count % 5 );
    REQUIRE( it.col() == count / 5 );
    } while (it != X.begin_row_col());

  REQUIRE( count == 0 );
  }



TEST_CASE("spmat_row_col_iterator_test")
  {
  mat X;
  X.zeros(5, 5);
  for (uword i = 0; i < 5; ++i)
    {
    X.col(i) += i;
    }

  for (uword i = 0; i < 5; ++i)
    {
    X.row(i) += 3 * i;
    }

  // Make sure default constructor works okay.
  mat::row_col_iterator it;
  // Make sure ++ operator, operator* and comparison operators work fine.
  uword count = 0;
  for (it = X.begin_row_col(); it != X.end_row_col(); ++it)
    {
    // Check iterator value.
    REQUIRE( *it == (count % 5) * 3 + (count / 5) );

    // Check iterator position.
    REQUIRE( it.row() == count % 5 );
    REQUIRE( it.col() == count / 5 );

    count++;
    }
  REQUIRE( count == 25 );
  it = X.end_row_col();
  do
    {
    --it;
    count--;

    // Check iterator value.
    REQUIRE( *it == (count % 5) * 3 + (count / 5) );

    // Check iterator position.
    REQUIRE( it.row() == count % 5 );
    REQUIRE( it.col() == count / 5 );
    } while (it != X.begin_row_col());

  REQUIRE( count == 0 );
  }



TEST_CASE("spmat_const_sprow_col_iterator_test")
  {
  sp_mat X(5, 5);
  for (uword i = 0; i < 5; ++i)
    {
    X.col(i) += i;
    }

  for (uword i = 0; i < 5; ++i)
    {
    X.row(i) += 3 * i;
    }

  // Make sure default constructor works okay.
  sp_mat::const_row_col_iterator it;
  // Make sure ++ operator, operator* and comparison operators work fine.
  uword count = 1;
  for (it = X.begin_row_col(); it != X.end_row_col(); ++it)
    {
    // Check iterator value.
    REQUIRE( *it == (count % 5) * 3 + (count / 5) );

    // Check iterator position.
    REQUIRE( it.row() == count % 5 );
    REQUIRE( it.col() == count / 5 );

    count++;
    }
  REQUIRE( count == 25 );
  it = X.end_row_col();
  do
    {
    --it;
    count--;

    // Check iterator value.
    REQUIRE( *it == (count % 5) * 3 + (count / 5) );

    // Check iterator position.
    REQUIRE( it.row() == count % 5 );
    REQUIRE( it.col() == count / 5 );
    } while (it != X.begin_row_col());

  REQUIRE( count == 1 );
  }



TEST_CASE("spmat_sprow_col_iterator_test")
  {
  sp_mat X(5, 5);
  for (uword i = 0; i < 5; ++i)
    {
    X.col(i) += i;
    }

  for (uword i = 0; i < 5; ++i)
    {
    X.row(i) += 3 * i;
    }

  // Make sure default constructor works okay.
  sp_mat::row_col_iterator it;
  // Make sure ++ operator, operator* and comparison operators work fine.
  uword count = 1;
  for (it = X.begin_row_col(); it != X.end_row_col(); ++it)
    {
    // Check iterator value.
    REQUIRE( *it == (count % 5) * 3 + (count / 5) );

    // Check iterator position.
    REQUIRE( it.row() == count % 5 );
    REQUIRE( it.col() == count / 5 );

    count++;
    }
  REQUIRE( count == 25 );
  it = X.end_row_col();
  do
    {
    --it;
    count--;

    // Check iterator value.
    REQUIRE( *it == (count % 5) * 3 + (count / 5) );

    // Check iterator position.
    REQUIRE( it.row() == count % 5 );
    REQUIRE( it.col() == count / 5 );
    } while (it != X.begin_row_col());

  REQUIRE( count == 1 );
  }


TEST_CASE("spmat_row_iterator_constructor")
  {
  // Create a row iterator with an exact position.
  Mat<double> tmp =
      { { 5.5, 0.0, 0.0 },
        { 0.0, 0.0, 6.5 },
        { 0.0, 7.5, 0.0 } };

  SpMat<double> X(tmp);

  SpMat<double>::const_row_iterator cri(X, 0, 1);

  // This should end up at (1, 2) with value 6.5.
  REQUIRE( cri.row() == 1 );
  REQUIRE( cri.col() == 2 );
  REQUIRE( (*cri) == Approx(6.5) );

  cri = SpMat<double>::const_row_iterator(X, 0, 0);

  // This should end up at (0, 0) with value 5.5.
  REQUIRE( cri.row() == 0 );
  REQUIRE( cri.col() == 0 );
  REQUIRE( (*cri) == Approx(5.5) );

  cri = SpMat<double>::const_row_iterator(X, 2, 1);

  // This should end up at (2, 1) with value 7.5.
  REQUIRE( cri.row() == 2 );
  REQUIRE( cri.col() == 1 );
  REQUIRE( (*cri) == Approx(7.5) );
  }


// Check that sparse + scalar works.
TEST_CASE("spmat_scalar_add")
  {
  sp_mat m;
  m.sprandu(100, 200, 0.1);

  mat y = m + 3.0;
  mat z = 3.0 + m;

  for (uword i = 0; i < m.n_cols; ++i)
    {
    for (uword j = 0; j < m.n_rows; ++j)
      {
      REQUIRE(m(j, i) == Approx(z(j, i) - 3));
      REQUIRE(m(j, i) == Approx(y(j, i) - 3));
      }
    }
  }


// Check that sparse - scalar works.
TEST_CASE("spmat_scalar_minus")
  {
  sp_mat m;
  m.sprandu(100, 200, 0.1);

  mat y = m - 3.0;
  mat z = 3.0 - m;

  for (uword i = 0; i < m.n_cols; ++i)
    {
    for (uword j = 0; j < m.n_rows; ++j)
      {
      REQUIRE(m(j, i) == Approx(3 - z(j, i)));
      REQUIRE(m(j, i) == Approx(y(j, i) + 3));
      }
    }
  }


// Check that sparse / (sparse + eps) works.  (and also for (sparse - eps) and (eps - sparse).
TEST_CASE("spmat_div_test")
  {
  sp_mat m;
  m.sprandu(100, 200, 0.1);

  sp_mat m2;
  m2.sprandu(100, 200, 0.5); // higher probability of collision

  sp_mat out = m / (m2 + 1.0);
  sp_mat out2 = m / (m2 - 2.0);
  sp_mat out3 = m / (2.0 - m2);

  REQUIRE(out.n_rows == m.n_rows);
  REQUIRE(out.n_cols == m.n_cols);
  REQUIRE(out.n_nonzero == m.n_nonzero);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      if (m(r, c) != 0.0)
        {
        REQUIRE((m(r, c) / (m2(r, c) + 1.0)) == Approx(out(r, c)));
        REQUIRE((m(r, c) / (m2(r, c) - 2.0)) == Approx(out2(r, c)));
        REQUIRE((m(r, c) / (2.0 - m2(r, c))) == Approx(out3(r, c)));
        }
      else
        {
        REQUIRE(out(r, c) == 0.0);
        REQUIRE(out2(r, c) == 0.0);
        REQUIRE(out3(r, c) == 0.0);
        }
      }
    }
  }



// Check that sparse % (sparse + eps) works.  (and also for (sparse - eps) and (eps - sparse).
TEST_CASE("spmat_schur_test")
  {
  sp_mat m;
  m.sprandu(100, 200, 0.1);

  sp_mat m2;
  m2.sprandu(100, 200, 0.5); // higher probability of collision

  sp_mat out = m % (m2 + 1.0);
  sp_mat out2 = m % (m2 - 2.0);
  sp_mat out3 = m % (2.0 - m2);

  REQUIRE(out.n_rows == m.n_rows);
  REQUIRE(out.n_cols == m.n_cols);
  REQUIRE(out.n_nonzero == m.n_nonzero);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      if (m(r, c) != 0.0)
        {
        REQUIRE((m(r, c) * (m2(r, c) + 1.0)) == Approx(out(r, c)));
        REQUIRE((m(r, c) * (m2(r, c) - 2.0)) == Approx(out2(r, c)));
        REQUIRE((m(r, c) * (2.0 - m2(r, c))) == Approx(out3(r, c)));
        }
      else
        {
        REQUIRE(out(r, c) == 0.0);
        REQUIRE(out2(r, c) == 0.0);
        REQUIRE(out3(r, c) == 0.0);
        }
      }
    }
  }



// Make sure this compiles and works.
TEST_CASE("spmat_repeated_add_subtract")
  {
  sp_mat m;
  m.sprandu(100, 200, 0.1);

  // p: plus, m: minus, n: pre-minus
  mat out_pp = m + 3 + 3;
  mat out_pm = m + 3 - 3;
  mat out_pn = 3 - (m + 3);
  mat out_mp = m - 3 + 3;
  mat out_mm = m - 3 - 3;
  mat out_mn = 3 - (m - 3);
  mat out_np = (3 - m) + 3;
  mat out_nm = (3 - m) - 3;
  mat out_nn = 3 - (3 - m);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      REQUIRE(out_pp(r, c) == Approx(m(r, c) + 6));
      REQUIRE(out_pm(r, c) == Approx(m(r, c)));
      REQUIRE(out_pn(r, c) == Approx(-m(r, c)));
      REQUIRE(out_mp(r, c) == Approx(m(r, c)));
      REQUIRE(out_mm(r, c) == Approx(m(r, c) - 6));
      REQUIRE(out_mn(r, c) == Approx(6 - m(r, c)));
      REQUIRE(out_np(r, c) == Approx(6 - m(r, c)));
      REQUIRE(out_nm(r, c) == Approx(-m(r, c)));
      REQUIRE(out_nn(r, c) == Approx(m(r, c)));
      }
    }
  }



// If we wrap an sp_mat() constructor around a (sparse + plus) it should force
// evaluate into a sparse matrix.
TEST_CASE("spmat_force_plus_minus_sparse")
  {
  // We can't test that our desired optimization is used but we can test that it
  // compiles.
  sp_mat m;
  m.sprandu(100, 200, 0.1);

  sp_mat out1(m + 1);
  sp_mat out2(m - 1);
  sp_mat out3(2 - m);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      REQUIRE(out1(r, c) == Approx(m(r, c) + 1));
      REQUIRE(out2(r, c) == Approx(m(r, c) - 1));
      REQUIRE(out3(r, c) == Approx(2 - m(r, c)));
      }
    }
  }



// Test elementwise max().
TEST_CASE("spmat_elementwise_max")
  {
  sp_mat m, n;
  m.sprandu(100, 200, 0.1);
  n.sprandu(100, 200, 0.2);

  sp_mat out = max(m, n);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      REQUIRE(out(r, c) == Approx(std::max((double) m(r, c), (double) n(r, c))));
      }
    }
  }



// Test elementwise max() with a dense object.
TEST_CASE("spmat_mat_elementwise_max")
  {
  sp_mat m;
  mat n;
  m.sprandu(100, 200, 0.1);
  n.randu(100, 200);
  n -= 0.5;

  mat out1 = max(m, n);
  mat out2 = max(n, m);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      REQUIRE(out1(r, c) == Approx(std::max((double) m(r, c), (double) n(r, c))));
      REQUIRE(out2(r, c) == Approx(std::max((double) m(r, c), (double) n(r, c))));
      }
    }
  }



// Test elementwise complex max().
TEST_CASE("spmat_elementwise_max_cx")
  {
  sp_cx_mat m, n;
  m.sprandu(100, 200, 0.1);
  n.sprandu(100, 200, 0.2);

  sp_cx_mat out = arma::max(m, n);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      if (std::abs(cx_double(m(r, c))) > std::abs(cx_double(n(r, c))))
        REQUIRE(std::abs(cx_double(out(r, c)) - cx_double(m(r, c))) == Approx(0.0));
      else
        REQUIRE(std::abs(cx_double(out(r, c)) - cx_double(n(r, c))) == Approx(0.0));
      }
    }
  }



// Test elementwise min().
TEST_CASE("spmat_elementwise_min")
  {
  sp_mat m, n;
  m.sprandu(100, 200, 0.1);
  n.sprandu(100, 200, 0.2);

  sp_mat out = min(m, n);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      REQUIRE(out(r, c) == Approx(std::min((double) m(r, c), (double) n(r, c))));
      }
    }
  }



// Test elementwise min() with a dense object.
TEST_CASE("spmat_mat_elementwise_min")
  {
  sp_mat m;
  mat n;
  m.sprandu(100, 200, 0.1);
  n.randu(100, 200);
  n -= 0.5;

  mat out1 = min(m, n);
  mat out2 = min(n, m);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      REQUIRE(out1(r, c) == Approx(std::min((double) m(r, c), (double) n(r, c))));
      REQUIRE(out2(r, c) == Approx(std::min((double) m(r, c), (double) n(r, c))));
      }
    }
  }



// Test elementwise complex min().
TEST_CASE("spmat_elementwise_min_cx")
  {
  sp_cx_mat m, n;
  m.sprandu(100, 200, 0.1);
  n.sprandu(100, 200, 0.2);

  sp_cx_mat out = arma::min(m, n);

  for (uword c = 0; c < m.n_cols; ++c)
    {
    for (uword r = 0; r < m.n_rows; ++r)
      {
      if (std::abs(cx_double(m(r, c))) < std::abs(cx_double(n(r, c))))
        REQUIRE(std::abs(cx_double(out(r, c)) - cx_double(m(r, c))) == Approx(0.0));
      else
        REQUIRE(std::abs(cx_double(out(r, c)) - cx_double(n(r, c))) == Approx(0.0));
      }
    }
  }


// Test vectorise() on a matrix.
TEST_CASE("spmat_vectorise_matrix")
  {
  sp_mat m;
  m.sprandu(10, 10, 0.1);

  sp_vec c = vectorise(m);
  sp_mat d = vectorise(m);
  sp_rowvec e = vectorise(m).t();

  for (uword i = 0; i < c.n_elem; ++i)
    {
    REQUIRE(c(i) == Approx(m(i)));
    REQUIRE(d(i) == Approx(m(i)));
    REQUIRE(e(i) == Approx(m(i)));
    }
  }



// Test vectorise() as an alias.
TEST_CASE("spmat_vectorise_alias")
  {
  sp_mat m;
  m.sprandu(10, 10, 0.1);

  sp_mat n(m);
  n = vectorise(n);

  REQUIRE(n.n_rows == 100);
  REQUIRE(n.n_cols == 1);
  for (uword i = 0; i < n.n_elem; ++i)
    {
    REQUIRE(n(i) == Approx(m(i)));
    }
  }



// Test vectorise() with the dimension argument.
TEST_CASE("spmat_vectorise_dimension")
  {
  sp_mat m;
  m.sprandu(10, 10, 0.1);
  sp_mat n = m.t();

  sp_vec c = vectorise(m, 0);
  sp_rowvec d = vectorise(m, 1);
  sp_rowvec e = vectorise(m.t(), 1);
  sp_vec f = vectorise(m.t(), 0);

  for (uword i = 0; i < m.n_elem; ++i)
    {
    REQUIRE(c(i) == Approx(m(i)));
    REQUIRE(d(i) == Approx(n(i)));
    REQUIRE(e(i) == Approx(m(i)));
    REQUIRE(f(i) == Approx(n(i)));
    }
  }



// Test vectorise() with an alias and a dimension argument.
TEST_CASE("spmat_vectorise_dimension_alias")
  {
  sp_mat m;
  m.sprandu(10, 10, 0.1);
  sp_mat n(m);

  m = arma::vectorise(m, 0);

  REQUIRE(m.n_rows == 100);
  REQUIRE(m.n_cols == 1);
  for (uword i = 0; i < m.n_elem; ++i)
    {
    REQUIRE(m(i) == Approx(n(i)));
    }

  m.sprandu(10, 10, 0.1);
  n = m.t();

  m = arma::vectorise(m, 1);

  REQUIRE(m.n_rows == 1);
  REQUIRE(m.n_cols == 100);
  for (uword i = 0; i < m.n_elem; ++i)
    {
    REQUIRE(m(i) == Approx(n(i)));
    }
  }
