/*
 * GraphBLAS Template Library (GBTL), Version 3.0
 *
 * Copyright 2020 Carnegie Mellon University, Battelle Memorial Institute, and
 * Authors.
 *
 * THIS MATERIAL WAS PREPARED AS AN ACCOUNT OF WORK SPONSORED BY AN AGENCY OF
 * THE UNITED STATES GOVERNMENT.  NEITHER THE UNITED STATES GOVERNMENT NOR THE
 * UNITED STATES DEPARTMENT OF ENERGY, NOR THE UNITED STATES DEPARTMENT OF
 * DEFENSE, NOR CARNEGIE MELLON UNIVERSITY, NOR BATTELLE, NOR ANY OF THEIR
 * EMPLOYEES, NOR ANY JURISDICTION OR ORGANIZATION THAT HAS COOPERATED IN THE
 * DEVELOPMENT OF THESE MATERIALS, MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR
 * ASSUMES ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS,
 * OR USEFULNESS OR ANY INFORMATION, APPARATUS, PRODUCT, SOFTWARE, OR PROCESS
 * DISCLOSED, OR REPRESENTS THAT ITS USE WOULD NOT INFRINGE PRIVATELY OWNED
 * RIGHTS.
 *
 * Released under a BSD-style license, please see LICENSE file or contact
 * permission@sei.cmu.edu for full terms.
 *
 * [DISTRIBUTION STATEMENT A] This material has been approved for public release
 * and unlimited distribution.  Please see Copyright notice for non-US
 * Government use and distribution.
 *
 * This Software includes and/or makes use of the following Third-Party Software
 * subject to its own license:
 *
 * 1. Boost Unit Test Framework
 * (https://www.boost.org/doc/libs/1_45_0/libs/test/doc/html/utf.html)
 * Copyright 2001 Boost software license, Gennadiy Rozental.
 *
 * DM20-0442
 */

#define GRAPHBLAS_LOGGING_LEVEL 0

#include <graphblas/graphblas.hpp>

using namespace grb;

#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE extract_stdmat_test_suite

#include <boost/test/included/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE)

//****************************************************************************
// extract standard matrix error tests
//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_bad_dimensions)
{
    IndexArrayType i      = {0, 0, 0, 1, 1, 1, 2, 2};
    IndexArrayType j      = {1, 2, 3, 0, 2, 3, 0, 1};
    std::vector<double> v = {1, 2, 3, 4, 6, 7, 8, 9};
    Matrix<double, DirectedMatrixTag> A(3, 4);
    A.build(i, j, v);

    Matrix<double, DirectedMatrixTag> C(2, 3);

    // Standard matrix version:
    // 1. nrows(C) != nrows(M)
    {
        // Too many mask rows
        std::vector<std::vector<bool>> matMask = {{true, false, true},
                                                  {true, true,  false},
                                                  {false, true, true}};
        Matrix<bool, DirectedMatrixTag> M(matMask, false);
        IndexArrayType vect_I({0, 2});
        IndexArrayType vect_J({0, 1, 2});
        BOOST_CHECK_THROW(
            extract(C, M, NoAccumulate(), A, vect_I, vect_J),
            DimensionException);
    }

    {
        // Too few mask rows
        std::vector<std::vector<bool>> matMask = {{false, true, true}};
        Matrix<bool, DirectedMatrixTag> M(matMask, false);
        IndexArrayType vect_I({0, 2});
        IndexArrayType vect_J({0, 1, 2});
        BOOST_CHECK_THROW(
            extract(C, M, NoAccumulate(), A, vect_I, vect_J),
            DimensionException);
    }

    // 2. ncols(C) != ncols(M)
    {
        // Too many mask cols
        std::vector<std::vector<bool>> matMask = {{false, true, false, true},
                                                  {true, true, false, false}};
        Matrix<bool, DirectedMatrixTag> M(matMask, false);
        IndexArrayType vect_I({0, 2});
        IndexArrayType vect_J({0, 1, 2});
        BOOST_CHECK_THROW(
            extract(C, M, NoAccumulate(), A, vect_I, vect_J),
            DimensionException);
    }

    {
        // Too few mask cols
        std::vector<std::vector<bool>> matMask = {{false, true},
                                                  {true, true}};
        Matrix<bool, DirectedMatrixTag> M(matMask, false);
        IndexArrayType vect_I({0, 2});
        IndexArrayType vect_J({0, 1, 2});
        BOOST_CHECK_THROW(
            extract(C, M, NoAccumulate(), A, vect_I, vect_J),
            DimensionException);
    }

    // 3. nrows(C) != |I|
    {
        IndexArrayType vect_I({0, 2, 1}); // too many rows for C
        IndexArrayType vect_J({0, 1, 2});
        BOOST_CHECK_THROW(
            extract(C, NoMask(), NoAccumulate(), A, vect_I, vect_J),
            DimensionException);
    }

    {
        IndexArrayType vect_I({0}); // too few rows for C
        IndexArrayType vect_J({0, 1, 2});
        BOOST_CHECK_THROW(
            extract(C, NoMask(), NoAccumulate(), A, vect_I, vect_J),
            DimensionException);
    }

    // 4. ncols(C) != |J|
    {
        IndexArrayType vect_I({0, 2});
        IndexArrayType vect_J({0, 2, 1, 2}); // too many cols for C
        BOOST_CHECK_THROW(
            extract(C, NoMask(), NoAccumulate(), A, vect_I, vect_J),
            DimensionException);
    }

    {
        IndexArrayType vect_I({0, 2});
        IndexArrayType vect_J({0, 1}); // too many cols for C
        BOOST_CHECK_THROW(
            extract(C, NoMask(), NoAccumulate(), A, vect_I, vect_J),
            DimensionException);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_index_out_of_bounds)
{
    IndexArrayType i    = {0, 0, 0, 1, 1, 1, 2, 2};
    IndexArrayType j    = {1, 2, 3, 0, 2, 3, 0, 1};
    std::vector<double> v = {1, 2, 3, 4, 6, 7, 8, 9};
    Matrix<double, DirectedMatrixTag> A(3, 4);
    A.build(i, j, v);

    Matrix<double, DirectedMatrixTag> C(2, 3);

    // Standard matrix version:
    // J index out of range
    {
        std::vector<std::vector<bool>> matMask = {{true, false, true},
                                                  {true, true,  false}};
        Matrix<bool, DirectedMatrixTag> M(matMask, false);
        IndexArrayType vect_I({0, 2});
        IndexArrayType vect_J({0, 4, 2});
        BOOST_CHECK_THROW(
            extract(C, M, NoAccumulate(), A, vect_I, vect_J),
            IndexOutOfBoundsException);

        BOOST_CHECK_THROW(
            extract(C, NoMask(), NoAccumulate(), A, vect_I, vect_J),
            IndexOutOfBoundsException);
    }

    // I index out of range
    {
        std::vector<std::vector<bool>> matMask = {{true, false, true},
                                                  {true, true,  false}};
        Matrix<bool, DirectedMatrixTag> M(matMask, false);
        IndexArrayType vect_I({3, 2});
        IndexArrayType vect_J({0, 1, 2});
        BOOST_CHECK_THROW(
            extract(C, M, NoAccumulate(), A, vect_I, vect_J),
            IndexOutOfBoundsException);
        BOOST_CHECK_THROW(
            extract(C, NoMask(), NoAccumulate(), A, vect_I, vect_J),
            IndexOutOfBoundsException);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_allindices_too_small)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double, DirectedMatrixTag> mA(matA, 0);

    // result is too small when I = AllIndices will pass
    {
        std::vector<std::vector<double>> matAnswer = {{8, 1, 6},
                                                      {0, 5, 7}};
        Matrix<double> answer(matAnswer, 0);
        Matrix<double> result(2, 3);

        extract(result, NoMask(), NoAccumulate(),
                mA, AllIndices(), AllIndices());

        BOOST_CHECK_EQUAL(result, answer);
    }

    // result is too big when I = AllIndices will pass
    {
        std::vector<std::vector<double>> matAnswer = {{8, 1, 6, 0, 0, 0},
                                                      {0, 5, 7, 9, 0, 0},
                                                      {4, 0, 2, 0, 0, 0},
                                                      {0, 0, 0, 0, 0, 0}};
        Matrix<double> answer(matAnswer, 0);
        Matrix<double> result(4, 6);

        extract(result, NoMask(), NoAccumulate(),
                mA, AllIndices(), AllIndices());

        BOOST_CHECK_EQUAL(result, answer);
    }
}

//****************************************************************************
// Standard matrix passing test cases:
//
// Simplified test structure (12 test cases total):
//
// Mask cases (x3):  nomask,  noscmp, scmp
// Accum cases (x2): noaccum, accum
// Trans cases (x2): notrans, trans
//
// Within each test case: 5 checks will be run:
//
// row_ind: AllIndices, nodup/ordered, nodup/permute, dup/ordered, dup/permute
// col_ind: AllIndices, nodup/ordered, nodup/permute, dup/ordered, dup/permute
//****************************************************************************

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_nomask_noaccum_notrans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);

    // I,J - AllIndices
    {
        Matrix<double> C(3, 4);
        extract(C, NoMask(), NoAccumulate(), A, AllIndices(), AllIndices());

        Matrix<double> answer(A);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(2,3);
        extract(C, NoMask(), NoAccumulate(), A, arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{8, 1, 0},
                                                  {4, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(2,3);
        extract(C, NoMask(), NoAccumulate(), A, arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{0, 4, 0},
                                                  {0, 8, 1}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(3,4);
        extract(C, NoMask(), NoAccumulate(), A, arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{8, 1, 1, 0},
                                                  {8, 1, 1, 0},
                                                  {4, 0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(3,4);
        extract(C, NoMask(), NoAccumulate(), A, arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{0, 4, 0, 4},
                                                  {0, 8, 1, 8},
                                                  {0, 4, 0, 4}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_nomask_noaccum_trans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);

    // I,J - AllIndices
    {
        Matrix<double> C(4,3);
        extract(C, NoMask(), NoAccumulate(), transpose(A),
                AllIndices(), AllIndices());

        std::vector<std::vector<double>> ansMat = {{8, 0, 4},
                                                   {1, 5, 0},
                                                   {6, 7, 2},
                                                   {0, 9, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(3,2);
        extract(C, NoMask(), NoAccumulate(), transpose(A), arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{8, 4},
                                                  {1, 0},
                                                  {0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(3,2);
        extract(C, NoMask(), NoAccumulate(), transpose(A), arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{0, 0},
                                                  {4, 8},
                                                  {0, 1}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(4,3);
        extract(C, NoMask(), NoAccumulate(), transpose(A), arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{8, 8, 4},
                                                  {1, 1, 0},
                                                  {1, 1, 0},
                                                  {0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(4,3);
        extract(C, NoMask(), NoAccumulate(), transpose(A), arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{0, 0, 0},
                                                  {4, 8, 4},
                                                  {0, 1, 0},
                                                  {4, 8, 4}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_nomask_accum_notrans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    std::vector<std::vector<double>> matC3x4 = {{9, 9, 9, 9},
                                                {9, 9, 9, 9},
                                                {9, 9, 9, 0}};
    std::vector<std::vector<double>> matC2x3 = {{9, 9, 9},
                                                {9, 9, 0}};
    Matrix<double> A(matA, 0);

    // I,J - AllIndices
    {
        Matrix<double> C(matC3x4, 0);
        extract(C, NoMask(), Plus<double>(), A, AllIndices(), AllIndices());

        std::vector<std::vector<double>> ansMat = {{17, 10, 15,  9},
                                                   { 9, 14, 16, 18},
                                                   {13,  9, 11,  0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(matC2x3, 0);
        extract(C, NoMask(), Plus<double>(), A, arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{17, 10, 9},
                                                  {13,  9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(matC2x3, 0);
        extract(C, NoMask(), Plus<double>(), A, arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{9, 13,  9},
                                                  {9, 17,  1}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(matC3x4, 0);
        extract(C, NoMask(), Plus<double>(), A, arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{17, 10, 10, 9},
                                                  {17, 10, 10, 9},
                                                  {13,  9,  9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(matC3x4, 0);
        extract(C, NoMask(), Plus<double>(), A, arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{9, 13,  9, 13},
                                                  {9, 17, 10, 17},
                                                  {9, 13,  9, 4}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_nomask_accum_trans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    std::vector<std::vector<double>> matC4x3 = {{9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 0}};
    std::vector<std::vector<double>> matC3x2 = {{9, 9},
                                                {9, 9},
                                                {9, 0}};
    Matrix<double> A(matA, 0);

    // I,J - AllIndices
    {
        Matrix<double> C(matC4x3, 0);
        extract(C, NoMask(), Plus<double>(), transpose(A),
                AllIndices(), AllIndices());

        std::vector<std::vector<double>> ansMat = {{17,  9, 13},
                                                   {10, 14,  9},
                                                   {15, 16, 11},
                                                   { 9, 18,  0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(matC3x2, 0);
        extract(C, NoMask(), Plus<double>(), transpose(A), arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{17, 13},
                                                  {10,  9},
                                                  { 9,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(matC3x2, 0);
        extract(C, NoMask(), Plus<double>(), transpose(A), arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{ 9,  9},
                                                  {13, 17},
                                                  { 9, 1}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, NoMask(), Plus<double>(), transpose(A), arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{17, 17, 13},
                                                  {10, 10,  9},
                                                  {10, 10,  9},
                                                  { 9,  9,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, NoMask(), Plus<double>(), transpose(A), arrayI, arrayJ);

        std::vector<std::vector<double>> ansMat ={{ 9,  9,  9},
                                                  {13, 17, 13},
                                                  { 9, 10,  9},
                                                  {13, 17,  4}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_noscmp_noaccum_notrans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);

    std::vector<std::vector<uint8_t>> matMask2x3 = {{1, 1, 0},    // stored 0
                                                    {1,99, 0}};
    std::vector<std::vector<uint8_t>> matMask3x4 = {{1, 1, 1, 0}, // stored 0
                                                    {1, 1,99, 0},
                                                    {1,99,99, 0}};
    Matrix<uint8_t> mask2x3(matMask2x3, 99); // turn 99's into implicit 0's
    Matrix<uint8_t> mask3x4(matMask3x4, 99);

    std::vector<std::vector<double>> matC3x4 = {{9, 9, 9, 9},
                                                {9, 9, 9, 9},
                                                {9, 9, 9, 0}};
    std::vector<std::vector<double>> matC2x3 = {{9, 9, 9},
                                                {9, 9, 0}};

    // I,J - AllIndices
    {
        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, NoAccumulate(), A, AllIndices(), AllIndices(), REPLACE);

        std::vector<std::vector<double>> ansMat = {{8, 1, 6, 0},
                                                   {0, 5, 0, 0},
                                                   {4, 0, 0, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, NoAccumulate(), A, AllIndices(), AllIndices(), MERGE);

        std::vector<std::vector<double>> ansMat = {{8, 1, 6, 9},
                                                   {0, 5, 9, 9},
                                                   {4, 9, 9, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(matC2x3, 0);
        extract(C, mask2x3, NoAccumulate(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{8, 1, 0},
                                                  {4, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(matC2x3, 0);
        extract(C, mask2x3, NoAccumulate(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{8, 1, 9},
                                                  {4, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(matC2x3, 0);
        extract(C, mask2x3, NoAccumulate(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{0, 4, 0},
                                                  {0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(matC2x3, 0);
        extract(C, mask2x3, NoAccumulate(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{0, 4, 9},
                                                  {0, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, NoAccumulate(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{8, 1, 1, 0},
                                                  {8, 1, 0, 0},
                                                  {4, 0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, NoAccumulate(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{8, 1, 1, 9},
                                                  {8, 1, 9, 9},
                                                  {4, 9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, NoAccumulate(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{0, 4, 0, 0},
                                                  {0, 8, 0, 0},
                                                  {0, 0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, NoAccumulate(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{0, 4, 0, 9},
                                                  {0, 8, 9, 9},
                                                  {0, 9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_noscmp_noaccum_trans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);
    std::vector<std::vector<uint8_t>> matMask3x2 = {{1, 1},    // stored 0
                                                    {1,99},    // stored 0
                                                    {0, 0}};
    std::vector<std::vector<uint8_t>> matMask4x3 = {{1, 1, 1}, // stored 0
                                                    {1, 1,99}, // stored 0
                                                    {1,99,99},
                                                    {0, 0, 0}};
    Matrix<uint8_t> mask3x2(matMask3x2, 99); // turn 99's into implicit 0's
    Matrix<uint8_t> mask4x3(matMask4x3, 99);

    std::vector<std::vector<double>> matC4x3 = {{9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 0}};
    std::vector<std::vector<double>> matC3x2 = {{9, 9},
                                                {9, 9},
                                                {9, 0}};

    // I,J - AllIndices
    {
        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, NoAccumulate(), transpose(A),
                AllIndices(), AllIndices(), REPLACE);

        std::vector<std::vector<double>> ansMat = {{8, 0, 4},
                                                   {1, 5, 0},
                                                   {6, 0, 0},
                                                   {0, 0, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, NoAccumulate(), transpose(A),
                AllIndices(), AllIndices(), MERGE);

        std::vector<std::vector<double>> ansMat = {{8, 0, 4},
                                                   {1, 5, 9},
                                                   {6, 9, 9},
                                                   {9, 9, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(matC3x2, 0);
        extract(C, mask3x2, NoAccumulate(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{8, 4},
                                                  {1, 0},
                                                  {0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(matC3x2, 0);
        extract(C, mask3x2, NoAccumulate(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{8, 4},
                                                  {1, 9},
                                                  {9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(matC3x2, 0);
        extract(C, mask3x2, NoAccumulate(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{0, 0},
                                                  {4, 0},
                                                  {0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(matC3x2, 0);
        extract(C, mask3x2, NoAccumulate(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{0, 0},
                                                  {4, 9},
                                                  {9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, NoAccumulate(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{8, 8, 4},
                                                  {1, 1, 0},
                                                  {1, 0, 0},
                                                  {0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, NoAccumulate(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{8, 8, 4},
                                                  {1, 1, 9},
                                                  {1, 9, 9},
                                                  {9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, NoAccumulate(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{0, 0, 0},
                                                  {4, 8, 0},
                                                  {0, 0, 0},
                                                  {0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, NoAccumulate(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{0, 0, 0},
                                                  {4, 8, 9},
                                                  {0, 9, 9},
                                                  {9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_noscmp_accum_notrans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);

    std::vector<std::vector<uint8_t>> matMask2x3 = {{1, 1, 0},    // stored 0
                                                    {1,99, 0}};
    std::vector<std::vector<uint8_t>> matMask3x4 = {{1, 1, 1, 0}, // stored 0
                                                    {1, 1,99, 0},
                                                    {1,99,99, 0}};
    Matrix<uint8_t> mask2x3(matMask2x3, 99); // turn 99's into implicit 0's
    Matrix<uint8_t> mask3x4(matMask3x4, 99);

    std::vector<std::vector<double>> matC3x4 = {{9, 9, 9, 9},
                                                {9, 9, 9, 9},
                                                {9, 9, 9, 0}};
    std::vector<std::vector<double>> matC2x3 = {{9, 9, 9},
                                                {9, 9, 0}};

    // I,J - AllIndices
    {
        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, Plus<double>(), A, AllIndices(), AllIndices(), REPLACE);

        std::vector<std::vector<double>> ansMat = {{17, 10, 15, 0},
                                                   { 9, 14,  0, 0},
                                                   {13,  0,  0, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, Plus<double>(), A, AllIndices(), AllIndices(), MERGE);

        std::vector<std::vector<double>> ansMat = {{17, 10, 15, 9},
                                                   { 9, 14,  9, 9},
                                                   {13,  9,  9, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(matC2x3, 0);
        extract(C, mask2x3, Plus<double>(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{17, 10, 0},
                                                  {13,  0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(matC2x3, 0);
        extract(C, mask2x3, Plus<double>(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{17, 10, 9},
                                                  {13,  9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(matC2x3, 0);
        extract(C, mask2x3, Plus<double>(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{9,13, 0},
                                                  {9, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(matC2x3, 0);
        extract(C, mask2x3, Plus<double>(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{9,13, 9},
                                                  {9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, Plus<double>(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{17, 10, 10, 0},
                                                  {17, 10,  0, 0},
                                                  {13,  0,  0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, Plus<double>(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{17, 10, 10, 9},
                                                  {17, 10,  9, 9},
                                                  {13,  9,  9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, Plus<double>(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{9,13, 9, 0},
                                                  {9,17, 0, 0},
                                                  {9, 0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(matC3x4, 0);
        extract(C, mask3x4, Plus<double>(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{9,13, 9, 9},
                                                  {9,17, 9, 9},
                                                  {9, 9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_noscmp_accum_trans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);
    std::vector<std::vector<uint8_t>> matMask3x2 = {{1, 1},    // stored 0
                                                    {1,99},    // stored 0
                                                    {0, 0}};
    std::vector<std::vector<uint8_t>> matMask4x3 = {{1, 1, 1}, // stored 0
                                                    {1, 1,99}, // stored 0
                                                    {1,99,99},
                                                    {0, 0, 0}};
    Matrix<uint8_t> mask3x2(matMask3x2, 99); // turn 99's into implicit 0's
    Matrix<uint8_t> mask4x3(matMask4x3, 99);

    std::vector<std::vector<double>> matC4x3 = {{9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 0}};
    std::vector<std::vector<double>> matC3x2 = {{9, 9},
                                                {9, 9},
                                                {9, 0}};

    // I,J - AllIndices
    {
        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, Plus<double>(), transpose(A),
                AllIndices(), AllIndices(), REPLACE);

        std::vector<std::vector<double>> ansMat = {{17,  9, 13},
                                                   {10, 14,  0},
                                                   {15,  0,  0},
                                                   { 0,  0,  0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, Plus<double>(), transpose(A),
                AllIndices(), AllIndices(), MERGE);

        std::vector<std::vector<double>> ansMat = {{17,  9, 13},
                                                   {10, 14,  9},
                                                   {15,  9,  9},
                                                   { 9,  9,  0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(matC3x2, 0);
        extract(C, mask3x2, Plus<double>(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{17, 13},
                                                  {10,  0},
                                                  { 0,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(matC3x2, 0);
        extract(C, mask3x2, Plus<double>(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{17, 13},
                                                  {10,  9},
                                                  { 9,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(matC3x2, 0);
        extract(C, mask3x2, Plus<double>(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{ 9, 9},
                                                  {13, 0},
                                                  { 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(matC3x2, 0);
        extract(C, mask3x2, Plus<double>(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{ 9, 9},
                                                  {13, 9},
                                                  { 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, Plus<double>(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{17, 17, 13},
                                                  {10, 10,  0},
                                                  {10,  0,  0},
                                                  { 0,  0,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, Plus<double>(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{17, 17, 13},
                                                  {10, 10,  9},
                                                  {10,  9,  9},
                                                  { 9,  9,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, Plus<double>(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{ 9,  9, 9},
                                                  {13, 17, 0},
                                                  { 9,  0, 0},
                                                  { 0,  0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, mask4x3, Plus<double>(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{ 9,  9, 9},
                                                  {13, 17, 9},
                                                  { 9,  9, 9},
                                                  { 9,  9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_scmp_noaccum_notrans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);

    // complements of masks from the noscmp case
    std::vector<std::vector<uint8_t>> matMask2x3 = {{0,99, 1},    // stored 0
                                                    {0, 1, 1}};
    std::vector<std::vector<uint8_t>> matMask3x4 = {{0,99,99, 1}, // stored 0
                                                    {0,99, 1, 1},
                                                    {0, 1, 1, 1}};
    Matrix<uint8_t> mask2x3(matMask2x3, 99); // turn 99's into implicit 0's
    Matrix<uint8_t> mask3x4(matMask3x4, 99);

    std::vector<std::vector<double>> matC3x4 = {{9, 9, 9, 9},
                                                {9, 9, 9, 9},
                                                {9, 9, 9, 0}};
    std::vector<std::vector<double>> matC2x3 = {{9, 9, 9},
                                                {9, 9, 0}};

    // I,J - AllIndices
    {
        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), NoAccumulate(), A, AllIndices(), AllIndices(), REPLACE);

        std::vector<std::vector<double>> ansMat = {{8, 1, 6, 0},
                                                   {0, 5, 0, 0},
                                                   {4, 0, 0, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), NoAccumulate(), A, AllIndices(), AllIndices(), MERGE);

        std::vector<std::vector<double>> ansMat = {{8, 1, 6, 9},
                                                   {0, 5, 9, 9},
                                                   {4, 9, 9, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(matC2x3, 0);
        extract(C, complement(mask2x3), NoAccumulate(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{8, 1, 0},
                                                  {4, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(matC2x3, 0);
        extract(C, complement(mask2x3), NoAccumulate(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{8, 1, 9},
                                                  {4, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(matC2x3, 0);
        extract(C, complement(mask2x3), NoAccumulate(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{0, 4, 0},
                                                  {0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(matC2x3, 0);
        extract(C, complement(mask2x3), NoAccumulate(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{0, 4, 9},
                                                  {0, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), NoAccumulate(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{8, 1, 1, 0},
                                                  {8, 1, 0, 0},
                                                  {4, 0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), NoAccumulate(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{8, 1, 1, 9},
                                                  {8, 1, 9, 9},
                                                  {4, 9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), NoAccumulate(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{0, 4, 0, 0},
                                                  {0, 8, 0, 0},
                                                  {0, 0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), NoAccumulate(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{0, 4, 0, 9},
                                                  {0, 8, 9, 9},
                                                  {0, 9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_scmp_noaccum_trans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);
    std::vector<std::vector<uint8_t>> matMask3x2 = {{ 0, 0},    // stored 0
                                                    {99, 1},    // stored 0
                                                    { 1, 1}};
    std::vector<std::vector<uint8_t>> matMask4x3 = {{ 0, 0, 0}, // stored 0
                                                    {99,99, 1}, // stored 0
                                                    {99, 1, 1},
                                                    { 1, 1, 1}};
    Matrix<uint8_t> mask3x2(matMask3x2, 99); // turn 99's into implicit 0's
    Matrix<uint8_t> mask4x3(matMask4x3, 99);

    std::vector<std::vector<double>> matC4x3 = {{9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 0}};
    std::vector<std::vector<double>> matC3x2 = {{9, 9},
                                                {9, 9},
                                                {9, 0}};

    // I,J - AllIndices
    {
        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), NoAccumulate(), transpose(A),
                AllIndices(), AllIndices(), REPLACE);

        std::vector<std::vector<double>> ansMat = {{8, 0, 4},
                                                   {1, 5, 0},
                                                   {6, 0, 0},
                                                   {0, 0, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), NoAccumulate(), transpose(A),
                AllIndices(), AllIndices(), MERGE);

        std::vector<std::vector<double>> ansMat = {{8, 0, 4},
                                                   {1, 5, 9},
                                                   {6, 9, 9},
                                                   {9, 9, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(matC3x2, 0);
        extract(C, complement(mask3x2), NoAccumulate(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{8, 4},
                                                  {1, 0},
                                                  {0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(matC3x2, 0);
        extract(C, complement(mask3x2), NoAccumulate(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{8, 4},
                                                  {1, 9},
                                                  {9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(matC3x2, 0);
        extract(C, complement(mask3x2), NoAccumulate(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{0, 0},
                                                  {4, 0},
                                                  {0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(matC3x2, 0);
        extract(C, complement(mask3x2), NoAccumulate(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{0, 0},
                                                  {4, 9},
                                                  {9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), NoAccumulate(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{8, 8, 4},
                                                  {1, 1, 0},
                                                  {1, 0, 0},
                                                  {0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), NoAccumulate(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{8, 8, 4},
                                                  {1, 1, 9},
                                                  {1, 9, 9},
                                                  {9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), NoAccumulate(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{0, 0, 0},
                                                  {4, 8, 0},
                                                  {0, 0, 0},
                                                  {0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), NoAccumulate(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{0, 0, 0},
                                                  {4, 8, 9},
                                                  {0, 9, 9},
                                                  {9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_scmp_accum_notrans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);

    std::vector<std::vector<uint8_t>> matMask2x3 = {{0,99, 1},    // stored 0
                                                    {0, 1, 1}};
    std::vector<std::vector<uint8_t>> matMask3x4 = {{0,99,99, 1}, // stored 0
                                                    {0,99, 1, 1},
                                                    {0, 1, 1, 1}};
    Matrix<uint8_t> mask2x3(matMask2x3, 99); // turn 99's into implicit 0's
    Matrix<uint8_t> mask3x4(matMask3x4, 99);

    std::vector<std::vector<double>> matC3x4 = {{9, 9, 9, 9},
                                                {9, 9, 9, 9},
                                                {9, 9, 9, 0}};
    std::vector<std::vector<double>> matC2x3 = {{9, 9, 9},
                                                {9, 9, 0}};

    // I,J - AllIndices
    {
        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), Plus<double>(), A, AllIndices(), AllIndices(), REPLACE);

        std::vector<std::vector<double>> ansMat = {{17, 10, 15, 0},
                                                   { 9, 14,  0, 0},
                                                   {13,  0,  0, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), Plus<double>(), A, AllIndices(), AllIndices(), MERGE);

        std::vector<std::vector<double>> ansMat = {{17, 10, 15, 9},
                                                   { 9, 14,  9, 9},
                                                   {13,  9,  9, 0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(matC2x3, 0);
        extract(C, complement(mask2x3), Plus<double>(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{17, 10, 0},
                                                  {13,  0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,2});
        IndexArrayType arrayJ({0,1,3});

        Matrix<double> C(matC2x3, 0);
        extract(C, complement(mask2x3), Plus<double>(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{17, 10, 9},
                                                  {13,  9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(matC2x3, 0);
        extract(C, complement(mask2x3), Plus<double>(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{9,13, 0},
                                                  {9, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({2,0});
        IndexArrayType arrayJ({3,0,1});

        Matrix<double> C(matC2x3, 0);
        extract(C, complement(mask2x3), Plus<double>(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{9,13, 9},
                                                  {9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), Plus<double>(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{17, 10, 10, 0},
                                                  {17, 10,  0, 0},
                                                  {13,  0,  0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,0,2});
        IndexArrayType arrayJ({0,1,1,3});

        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), Plus<double>(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{17, 10, 10, 9},
                                                  {17, 10,  9, 9},
                                                  {13,  9,  9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), Plus<double>(), A, arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{9,13, 9, 0},
                                                  {9,17, 0, 0},
                                                  {9, 0, 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({2,0,2});
        IndexArrayType arrayJ({3,0,1,0});

        Matrix<double> C(matC3x4, 0);
        extract(C, complement(mask3x4), Plus<double>(), A, arrayI, arrayJ, MERGE);

        std::vector<std::vector<double>> ansMat ={{9,13, 9, 9},
                                                  {9,17, 9, 9},
                                                  {9, 9, 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(extract_stdmat_test_scmp_accum_trans)
{
    std::vector<std::vector<double>> matA = {{8, 1, 6, 0},
                                             {0, 5, 7, 9},
                                             {4, 0, 2, 0}};
    Matrix<double> A(matA, 0);
    std::vector<std::vector<uint8_t>> matMask3x2 = {{ 0, 0},    // stored 0
                                                    {99, 1},    // stored 0
                                                    { 1, 1}};
    std::vector<std::vector<uint8_t>> matMask4x3 = {{ 0, 0, 0}, // stored 0
                                                    {99,99, 1}, // stored 0
                                                    {99, 1, 1},
                                                    { 1, 1, 1}};
    Matrix<uint8_t> mask3x2(matMask3x2, 99); // turn 99's into implicit 0's
    Matrix<uint8_t> mask4x3(matMask4x3, 99);

    std::vector<std::vector<double>> matC4x3 = {{9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 9},
                                                {9, 9, 0}};
    std::vector<std::vector<double>> matC3x2 = {{9, 9},
                                                {9, 9},
                                                {9, 0}};

    // I,J - AllIndices
    {
        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), Plus<double>(), transpose(A),
                AllIndices(), AllIndices(), REPLACE);

        std::vector<std::vector<double>> ansMat = {{17,  9, 13},
                                                   {10, 14,  0},
                                                   {15,  0,  0},
                                                   { 0,  0,  0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), Plus<double>(), transpose(A),
                AllIndices(), AllIndices(), MERGE);

        std::vector<std::vector<double>> ansMat = {{17,  9, 13},
                                                   {10, 14,  9},
                                                   {15,  9,  9},
                                                   { 9,  9,  0}};
        Matrix<double> answer(ansMat, 0);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, ordered
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(matC3x2, 0);
        extract(C, complement(mask3x2), Plus<double>(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{17, 13},
                                                  {10,  0},
                                                  { 0,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,1,3});
        IndexArrayType arrayJ({0,2});

        Matrix<double> C(matC3x2, 0);
        extract(C, complement(mask3x2), Plus<double>(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{17, 13},
                                                  {10,  9},
                                                  { 9,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - no dup, permuted
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(matC3x2, 0);
        extract(C, complement(mask3x2), Plus<double>(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{ 9, 9},
                                                  {13, 0},
                                                  { 0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({3,0,1});
        IndexArrayType arrayJ({2,0});

        Matrix<double> C(matC3x2, 0);
        extract(C, complement(mask3x2), Plus<double>(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{ 9, 9},
                                                  {13, 9},
                                                  { 9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, ordered
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), Plus<double>(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{17, 17, 13},
                                                  {10, 10,  0},
                                                  {10,  0,  0},
                                                  { 0,  0,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({0,1,1,3});
        IndexArrayType arrayJ({0,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), Plus<double>(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{17, 17, 13},
                                                  {10, 10,  9},
                                                  {10,  9,  9},
                                                  { 9,  9,  0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }

    // I,J - dup, permuted
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), Plus<double>(), transpose(A), arrayI, arrayJ, REPLACE);

        std::vector<std::vector<double>> ansMat ={{ 9,  9, 9},
                                                  {13, 17, 0},
                                                  { 9,  0, 0},
                                                  { 0,  0, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
    {
        IndexArrayType arrayI({3,0,1,0});
        IndexArrayType arrayJ({2,0,2});

        Matrix<double> C(matC4x3, 0);
        extract(C, complement(mask4x3), Plus<double>(), transpose(A), arrayI, arrayJ,MERGE);

        std::vector<std::vector<double>> ansMat ={{ 9,  9, 9},
                                                  {13, 17, 9},
                                                  { 9,  9, 9},
                                                  { 9,  9, 0}};
        Matrix<double> answer(ansMat, 0.);
        BOOST_CHECK_EQUAL(C, answer);
    }
}

BOOST_AUTO_TEST_SUITE_END()


//  (C) Copyright Gennadiy Rozental 2015.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.
//
/// @file
/// @brief tests collection comparison implementation
// ***************************************************************************

// Boost.Test
#define BOOST_TEST_MODULE Test collection`s comparisons
#include <boost/test/unit_test.hpp>
namespace tt = boost::test_tools;
namespace ut = boost::unit_test;

BOOST_TEST_SPECIALIZED_COLLECTION_COMPARE(std::vector<int>)

#define VALIDATE_OP( op )                           \
{                                                   \
    BOOST_TEST_INFO( "validating operator " #op );  \
    bool expected = (c1 op c2);                     \
    auto const& res = (tt::assertion::seed()->* c1 op c2).evaluate();      \
    BOOST_TEST( expected == !!res );                \
}                                                   \
/**/

template<typename Col>
void
validate_comparisons(Col const& c1, Col const& c2 )
{
    VALIDATE_OP( == )
    VALIDATE_OP( != )
    VALIDATE_OP( < )
    VALIDATE_OP( > )
    VALIDATE_OP( <= )
    VALIDATE_OP( >= )
}

BOOST_AUTO_TEST_CASE( test_against_overloaded_comp_op )
{
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{1, 3, 2};
    std::vector<int> c{1, 2, 3};
    std::vector<int> d{1, 2, 3, 4};

    BOOST_TEST_CONTEXT( "validating comparisons of a and b" )
        validate_comparisons(a, b);
    BOOST_TEST_CONTEXT( "validating comparisons of a and c" )
        validate_comparisons(a, c);
    BOOST_TEST_CONTEXT( "validating comparisons of a and d" )
        validate_comparisons(a, d);
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_per_element_eq, * ut::expected_failures(2) )
{
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{1, 3, 2};

    BOOST_TEST( a == b, tt::per_element() );
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_per_element_ne, * ut::expected_failures(1) )
{
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{1, 3, 2};

    BOOST_TEST( a != b, tt::per_element() );
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_per_element_lt, * ut::expected_failures(2) )
{
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{1, 3, 2};

    BOOST_TEST( a < b, tt::per_element() );
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_per_element_ge, * ut::expected_failures(1) )
{
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{1, 3, 2};

    BOOST_TEST( b >= a, tt::per_element() );
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_lexicographic_lt )
{
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{1, 3, 2};

    BOOST_TEST( a < b, tt::lexicographic() );
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_lexicographic_le )
{
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{1, 3, 2};

    BOOST_TEST( a <= b, tt::lexicographic() );
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_lexicographic_gt )
{
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{1, 3, 2};

    BOOST_TEST( b > a, tt::lexicographic() );
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_lexicographic_ge )
{
    std::vector<int> a{1, 2, 3};
    std::vector<int> b{1, 3, 2};

    BOOST_TEST( b >= a, tt::lexicographic() );
}

//____________________________________________________________________________//

BOOST_AUTO_TEST_CASE( test_collection_of_collection_comp )
{
    BOOST_TEST( std::string("abc") == std::string("abc") );
}

//____________________________________________________________________________//

// this one does not have const_iterator nor a size, but should be forward iterable
// and possible to use in the collection comparison
struct fwd_iterable_custom {
  typedef std::vector<int>::const_iterator custom_iterator; // named "exotic" on purpose

  custom_iterator begin() const { return values.begin(); }
  custom_iterator end() const { return values.end(); }

#if !defined(BOOST_MSVC) || (BOOST_MSVC_FULL_VER > 180040629)
#define MY_TEST_HAS_INIT_LIST
  // this does not work on VC++ 2013 update 5
  fwd_iterable_custom(std::initializer_list<int> ilist) : values{ilist}
  {}
#else
  fwd_iterable_custom(int v1, int v2, int v3) {
    values.push_back(v1);
    values.push_back(v2);
    values.push_back(v3);
  }
#endif
private:
  std::vector<int> values;
};

BOOST_AUTO_TEST_CASE( test_collection_requirement_type )
{
#ifdef MY_TEST_HAS_INIT_LIST
    fwd_iterable_custom a{3,4,5};
    fwd_iterable_custom b{3,4,6};
    fwd_iterable_custom c{3,4,5};
#else
    fwd_iterable_custom a(3,4,5);
    fwd_iterable_custom b(3,4,6);
    fwd_iterable_custom c(3,4,5);
#endif

    BOOST_TEST( a == a, tt::per_element() );
    //BOOST_TEST( a != b, tt::per_element() );
    BOOST_TEST( a == c, tt::per_element() );

    BOOST_TEST( a < b, tt::lexicographic() );
    BOOST_TEST( a <= c, tt::lexicographic() );
    BOOST_TEST( b > c, tt::lexicographic() );

    BOOST_TEST( a <= b, tt::per_element() );
    BOOST_TEST( a <= c, tt::per_element() );
}

// EOF


/*-----------------------------------------------------------------------------+
Copyright (c) 2008-2009: Joachim Faulhaber
+------------------------------------------------------------------------------+
   Distributed under the Boost Software License, Version 1.0.
      (See accompanying file LICENCE.txt or copy at
           http://www.boost.org/LICENSE_1_0.txt)
+-----------------------------------------------------------------------------*/
#define BOOST_TEST_MODULE icl::interval_set_mixed unit test
#include <libs/icl/test/disable_test_warnings.hpp>
#include <string>
#include <boost/mpl/list.hpp>
#include "../unit_test_unwarned.hpp"

// interval instance types
#include "../test_type_lists.hpp"
#include "../test_value_maker.hpp"

#include <boost/icl/interval_set.hpp>
#include <boost/icl/separate_interval_set.hpp>
#include <boost/icl/split_interval_set.hpp>

using namespace std;
using namespace boost;
using namespace unit_test;
using namespace boost::icl;

#include "../test_interval_set_mixed.hpp"

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_ctor_4_ordered_types)
{            interval_set_mixed_ctor_4_ordered_types<int>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_equal_4_ordered_types)
{            interval_set_mixed_equal_4_ordered_types<std::string>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_assign_4_ordered_types)
{            interval_set_mixed_assign_4_ordered_types<float>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_ctor_4_bicremental_types)
{            interval_set_mixed_ctor_4_bicremental_types<bicremental_type_1>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_assign_4_bicremental_types)
{            interval_set_mixed_assign_4_bicremental_types<bicremental_type_2>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_equal_4_bicremental_types)
{            interval_set_mixed_equal_4_bicremental_types<bicremental_type_3>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_contains_4_bicremental_types)
{            interval_set_mixed_contains_4_bicremental_types<bicremental_type_4>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_add_4_bicremental_types)
{            interval_set_mixed_add_4_bicremental_types<bicremental_type_5>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_subtract_4_bicremental_types)
{            interval_set_mixed_subtract_4_bicremental_types<bicremental_type_6>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_erase_4_bicremental_types)
{            interval_set_mixed_erase_4_bicremental_types<bicremental_type_7>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_basic_intersect_4_bicremental_types)
{            interval_set_mixed_basic_intersect_4_bicremental_types<bicremental_type_8>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_disjoint_4_bicremental_types)
{            interval_set_mixed_disjoint_4_bicremental_types<bicremental_type_1>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_infix_plus_overload_4_bicremental_types)
{            interval_set_mixed_infix_plus_overload_4_bicremental_types<bicremental_type_2>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_infix_pipe_overload_4_bicremental_types)
{            interval_set_mixed_infix_pipe_overload_4_bicremental_types<bicremental_type_3>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_infix_minus_overload_4_bicremental_types)
{            interval_set_mixed_infix_minus_overload_4_bicremental_types<bicremental_type_4>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_infix_et_overload_4_bicremental_types)
{            interval_set_mixed_infix_et_overload_4_bicremental_types<bicremental_type_5>(); }

BOOST_AUTO_TEST_CASE
(fastest_icl_interval_set_mixed_infix_caret_overload_4_bicremental_types)
{            interval_set_mixed_infix_caret_overload_4_bicremental_types<bicremental_type_6>(); }


/*! \file 1d_full_layout.cpp
    \brief Example of 1 D plot of 3 different STL container types using several layout features.
    \details Creates file 1d_full_layout.svg
    \author Jacob Voytko
*/

// Copyright Jacob Voytko 2007
// Copyright Paul a. Bristow 2020

// Use, modification and distribution are subject to the
// Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/svg_plot/svg_1d_plot.hpp>
// for using namespace boost::svg; // Convenient to access colors and many other svg_plot items.
#include <boost/svg_plot/svg_color.hpp>
// for boost::svg::svg_plot::svg_color

#include <boost/svg_plot/show_1d_settings.hpp>

#include <array> //   using std::array;
#include <vector> // using std::vector;
#include <deque> // using std::deque;
#include <cmath> //  using std::sqrt;

// Some random functions to create the data to plot.
double f(double x)
{
  return std::sqrt(x);
}

double g(double x)
{
  return -2 + x*x;
}

double h(double x)
{
  return -1 + 2*x;
}

int main()
{
  using namespace boost::svg; // Convenient to access colors and many other svg_plot items.
  using boost::svg::svg_color; // Explicitly access SVG colors.
  using boost::svg::svg_1d_plot; // Explicitly access 1D SVG plot.

  try
  {
    // Three containers of different types (just for show):
    std::vector<double> data1;
    std::deque<double> data2;
    std::array<double, 10> data3;

    // Fill the three containers with some randomish data :
    int j = 0;
    for (double i = 0; i < 9.5; i += 1.)
    {
      data1.push_back(f(i));
      data2.push_front(g(i));
      data3[j++] = h(i);
    }

    svg_1d_plot my_plot;

    // Size/scale settings for the plot.
    my_plot.size(500, 200)
      .x_range(-3, 10);

    // Text settings (note chaining 2nd and 3rd settings).
    my_plot.title("Animal Lives")
      .title_font_size(29)
      .x_label_on(true) // Do want to show X-axis label text.
      .x_axis_label_color(white)
      .x_label("life-time (months)");

    // Commands.
    my_plot.legend_on(true) // Do want a legend box - see legend settings below.
      .plot_window_on(true) // Do want the plot in its own window.
      .x_major_labels_side(true);

    // Color settings.
    my_plot.background_color(gray) //
      // or .background_color(svg_color(47, 79, 79)) //  darkslategray in svg_color.hpp
      .legend_background_color(azure)
      .legend_border_color(gold)
      .plot_background_color(lightgoldenrodyellow)
      .title_color(white);

    // Axis settings.
    my_plot.x_major_interval(2)
      .x_major_tick_length(14)
      .x_major_tick_width(1)
      .x_minor_tick_length(7)
      .x_minor_tick_width(1)
      .x_num_minor_ticks(3);

    // Legend settings.
    my_plot.legend_title("Animal")
      .legend_title_font_size(15)
      ;

    // Add the data to the plot:
    my_plot.plot(data1, "Lions").stroke_color(blue);
    my_plot.plot(data2, "Tigers").stroke_color(orange);
    my_plot.plot(data3, "Bears").stroke_color(red);

    // Write the final plot in SVG format.
    my_plot.write("./1d_full_layout.svg");

    using boost::svg::show_1d_plot_settings;
    show_1d_plot_settings(my_plot);
  }
  catch (std::exception& ex)
  { // Report about any exceptions.
    std::cout << "std::exception thown " << ex.what() << std::endl;
  }
  return 0;
} // int main()

// 1d_full_layout.cpp


//------------------------------------------------------------------------------
// \file DataStructures_tests.cpp
//------------------------------------------------------------------------------
#include <array>
#include <boost/test/unit_test.hpp>
#include <deque>
#include <forward_list>
#include <list>
#include <memory>
#include <stack>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

BOOST_AUTO_TEST_SUITE(DataStructures)
BOOST_AUTO_TEST_SUITE(DataStructures_tests)

BOOST_AUTO_TEST_SUITE(StdForwardList_tests)

// cf. https://www.geeksforgeeks.org/forward-list-c-set-1-introduction-important-functions/
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(DemonstrateStdForwardListAsLinkedList)
{
  {
    // Declaring forward list
    std::forward_list<int> flist1;

    // Declaring another forward list
    std::forward_list<int> flist2;

    // assigns values to container
    flist1.assign({1, 2, 3});

    // Assigning repeating values using assing()
    // 5 elements with value 10
    flist2.assign(5, 10);

    // Displaying forward lists

    int index {1};
    for (int& a : flist1)
    {
      BOOST_TEST(a == index);
      index++;
    }

    for (int& b : flist2)
    {
      BOOST_TEST(b == 10);
    }
  }
  {
    std::forward_list flist {10, 20, 30, 40, 50};

    // push_front pushes to last in at "[0] of flist[0]", the head

    // Inserting value using push front()
    // Inserts 60 at front
    flist.push_front(60); // 60, 10, 20, 30, 40, 50

    std::size_t index {0};
    std::vector<int> check_by_vec {60, 10, 20, 30, 40, 50};
    for (int& c : flist)
    {
      BOOST_TEST(check_by_vec[index] == c);
      index++;
      //std::cout << c << ' ';
    }

    // Inserting value using emplace_front()
    // Inserts 70 at front
    flist.emplace_front(70);

    check_by_vec.assign({70, 60, 10, 20, 30, 40, 50});
    index = 0;
    for (int& c : flist)
    {
      BOOST_TEST(check_by_vec[index] = c);
      index++;
      //std::cout << c << ' ';
    }

    // Deleting first value using pop_front()
    // Pops 70
    flist.pop_front();

    check_by_vec.assign({60, 10, 20, 30, 40, 50});
    index = 0;
    for (int& c : flist)
    {
      BOOST_TEST(check_by_vec[index] = c);
      index++;
      //std::cout << c << ' ';
    }
  }

  {
    // Initializing forward list
    std::forward_list<int> flist {10, 20, 30};

    // Declaring a forward list iterator
    std::forward_list<int>::iterator ptr;

    // Inserting value using insert_after()
    // starts insertion from second position
    // Iterator to inserted element. typically, but in this case
    // iterator to last element inserted

    // insert *after* means insert after
    ptr = flist.insert_after(flist.begin(), {1, 2, 3});

    std::vector<int> check_by_vec {10, 1, 2, 3, 20, 30};

    std::size_t index {0};
    for (int& c : flist)
    {
      BOOST_TEST(check_by_vec[index] == c);
      index++;
      //std::cout << c << ' ';
    }
    //std::cout << "\n";

    // Inerting value using emplace_after()
    // inserts 2 after ptr
    ptr = flist.emplace_after(ptr, 2);

    check_by_vec.assign({10, 1, 2, 3, 2, 20, 30});

    index = 0;
    for (int& c : flist)
    {
      BOOST_TEST(check_by_vec[index] == c);
      index++;
      //std::cout << c << ' ';
    }

  }
}

BOOST_AUTO_TEST_SUITE_END() // StdForwardList_tests

BOOST_AUTO_TEST_SUITE(StdStack_tests)

// cf. https://en.cppreference.com/w/cpp/header/stack
// cf. https://www.geeksforgeeks.org/stack-in-cpp-stl/
// Stacks are LIFO (Last In First Out)

template <typename T>
std::vector<T> copy_stack(const std::stack<T>& s)
{
  // https://en.cppreference.com/w/cpp/container/stack/operator%3D
  std::stack<T> copy_of_s = s;

  std::vector<T> result;

  while (!copy_of_s.empty())
  {
    result.emplace_back(copy_of_s.top());
    copy_of_s.pop();
  }

  return result;
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(DemonstrateStdStack)
{
  {
    // LIFO
    std::stack<int> s;
    s.push(10);
    s.push(30);
    s.push(20);
    s.push(5);
    s.push(1);

    std::vector<int> result {copy_stack(s)};

    BOOST_TEST((result == std::vector<int> {1, 5, 20, 30, 10}));

    BOOST_TEST(s.size() == 5);
    BOOST_TEST(s.top() == 1);
    s.pop();
    result = copy_stack(s);
    BOOST_TEST((result == std::vector<int> {5, 20, 30, 10}));
  }
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(TopReferencesOnEmptyStack)
{
  std::stack<int> s;
  const auto& top_reference_on_empty = s.top();

  // Undefined behavior?
  //BOOST_TEST((&top_reference_on_empty > 0));
}

BOOST_AUTO_TEST_SUITE_END() // StdStack_tests

BOOST_AUTO_TEST_SUITE(StdDeque_tests)

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOST_AUTO_TEST_CASE(DemonstrateStdDeque)
{
  {
    // Create a deque containing integers
    std::deque<int> d {7, 5, 16, 8};

    // Add an integer to the beginning and end of the deque
    d.push_front(13);
    d.push_back(25);

    const std::vector<int> target {13, 7, 5, 16, 8, 25};

    std::size_t index {0};
    for (int n : d)
    {
      BOOST_TEST(n == target[index]);
      index++;
    }
  }
  // FIFO
  {
    std::deque<int> d;
    d.push_back(25);
    d.push_back(8);
    d.push_back(16);
    d.push_back(5);
    d.push_back(7);
    d.push_back(13);

    BOOST_TEST(d.back() == 13);

    // if push_back is the enqueue (add to queue)
    // pop_front is the dequeue (first in, first out)
    d.pop_front();

    BOOST_TEST(d.front() == 8);
  }

  {
    std::deque<int> d;
    d.push_front(25);
    d.push_front(8);
    d.push_front(16);
    d.push_front(5);
    d.push_front(7);
    d.push_front(13);

    BOOST_TEST(d.back() == 25);

    // if push_front is the enqueue (add to queue)
    // pop_back is the dequeue (first in, first out)
    d.pop_back();

    BOOST_TEST(d.back() == 8);
  }

}

BOOST_AUTO_TEST_SUITE_END() // StdDeque_tests

BOOST_AUTO_TEST_SUITE_END() // DataStructures_tests
BOOST_AUTO_TEST_SUITE_END() // DataStructures


// **********************************************************************
// This file is part of the LehrFEM++ finite element library developed
// from 2018 at the Seminar of Applied Mathematics of ETH Zurich for
// teaching purposes.
// This header must not be removed.
// **********************************************************************
/**
 * @file
 * @brief Driver function for simple LehrFEM++ demo
 * @author Ralf Hiptmair
 * @date   January 2019
 * @copyright MIT License
 */

#include <boost/program_options.hpp>

#include "lecturedemoassemble.h"
#include "lecturedemodof.h"
#include "lecturedemomesh.h"
#include "lecturedemomeshfunction.h"
#include "lecturedemoquad.h"
#include "lecturedemorefine.h"
#include "lecturedemotwonorm.h"

int main(int argc, char **argv) {
  // We rely on Boost's program_option library for parsing command line
  // arguments
  namespace po = boost::program_options;
  // We specify what to do for the two allowed options -h and -d, which are
  // linked to the keys 'help' and 'demo_number'. We tell the computer that -d
  // expects an integer argument and that the default value is 0
  po::options_description desc("Allowed options");
  // clang-format off
  desc.add_options()
  ("help,h", "This message")
  ("demo_number,d", po::value<int>()->default_value(0), "Selector for demo code");
  // clang-format on
  po::variables_map vm;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  if (vm.count("help") > 0) {
    std::cout << desc << std::endl;
    std::cout << "No arg: run all demos" << std::endl;
    std::cout << "N = 1: demo of LehrFEM++ mesh capabilities" << std::endl;
    std::cout << "N = 2: demo of LehrFEM++ DofHandler capabilities"
              << std::endl;
    std::cout << "N = 3: demo of LehrFEM++ assembly of LSE" << std::endl;
    std::cout << "N = 4: demo of numerical quadrature in LehrFEM++"
              << std::endl;
    std::cout << "N = 5: demo of solving a Dirichlet BVP" << std::endl;
    std::cout << "N = 6: demo of mesh refinement" << std::endl;
    std::cout << "N = 7: Various of ways of computing an L2-norm" << std::endl;
    std::cout << "N = 8: Demo for MeshFunction" << std::endl;
  } else {
    int selector = vm["demo_number"].as<int>();
    if ((selector == 1) || (selector == 0)) {
      lecturedemo::lecturedemomesh();
    }
    if ((selector == 2) || (selector == 0)) {
      lecturedemo::lecturedemodof();
    }
    if ((selector == 3) || (selector == 0)) {
      lecturedemo::lecturedemoassemble();
    }
    if ((selector == 4) || (selector == 0)) {
      lecturedemo::lecturedemoquad();
    }
    if ((selector == 5) || (selector == 0)) {
      lecturedemo::lecturedemoDirichlet();
    }
    if ((selector == 6) || (selector == 0)) {
      lecturedemo::lecturedemorefine();
    }
    if ((selector == 7) || (selector == 0)) {
      lecturedemo::lecturedemotwonorm();
    }
    if ((selector == 8) || (selector == 0)) {
      lecturedemo::lecturedemomeshfunction();
    }
  }
  return 0L;
}