/*
 * 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 2

#include <graphblas/graphblas.hpp>

#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE kronecker_ABT_test_suite

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

using namespace grb;

BOOST_AUTO_TEST_SUITE(BOOST_TEST_MODULE)

//****************************************************************************

namespace
{
    static std::vector<std::vector<double> > A_sparse_3x3 =
    {{1,  0,  0},
     {0,  2,  0},
     {3,  0,  4}};

    static std::vector<std::vector<double> > AT_sparse_3x3 =
    {{1,  0,  3},
     {0,  2,  0},
     {0,  0,  4}};

    static std::vector<std::vector<double> > AA_sparse_9x9 =
    {{1,  0,  0,   0,  0,  0,   0,  0,  0},
     {0,  2,  0,   0,  0,  0,   0,  0,  0},
     {3,  0,  4,   0,  0,  0,   0,  0,  0},
     {0,  0,  0,   2,  0,  0,   0,  0,  0},
     {0,  0,  0,   0,  4,  0,   0,  0,  0},
     {0,  0,  0,   6,  0,  8,   0,  0,  0},
     {3,  0,  0,   0,  0,  0,   4,  0,  0},
     {0,  6,  0,   0,  0,  0,   0,  8,  0},
     {9,  0, 12,   0,  0,  0,  12,  0, 16}
    };

    static std::vector<std::vector<double> > AAT_sparse_9x9 =
    {{1,  0,  3,   0,  0,  0,   0,  0,  0},
     {0,  2,  0,   0,  0,  0,   0,  0,  0},
     {0,  0,  4,   0,  0,  0,   0,  0,  0},
     {0,  0,  0,   2,  0,  6,   0,  0,  0},
     {0,  0,  0,   0,  4,  0,   0,  0,  0},
     {0,  0,  0,   0,  0,  8,   0,  0,  0},
     {3,  0,  9,   0,  0,  0,   4,  0, 12},
     {0,  6,  0,   0,  0,  0,   0,  8,  0},
     {0,  0, 12,   0,  0,  0,   0,  0, 16}
    };

    static std::vector<std::vector<double> > Ar_sparse_3x3 =
    {{1,  0,  0},
     {0,  2,  0},
     {0,  0,  0}};

    static std::vector<std::vector<double> > Ac_sparse_3x3 =
    {{0,  0,  0},
     {0,  2,  0},
     {0,  0,  4}};

    static std::vector<std::vector<double> > B_sparse_3x4 =
    {{1,  1,  0,  0},
     {0,  2,  2,  0},
     {3,  0,  0,  3}};

    static std::vector<std::vector<double> > B_sparse_4x3 =
    {{1,  0,  3},
     {1,  2,  0},
     {0,  2,  0},
     {0,  0,  3}};

    static std::vector<std::vector<double> > Br_sparse_3x4 =
    {{0,  0,  0,  0},
     {0,  2,  2,  0},
     {3,  0,  0,  3}};

    static std::vector<std::vector<double> > Br_sparse_4x3 =
    {{0,  0,  3},
     {0,  2,  0},
     {0,  2,  0},
     {0,  0,  3}};

    static std::vector<std::vector<double> > Bc_sparse_3x4 =
    {{1,  0,  0,  0},
     {0,  0,  2,  0},
     {3,  0,  0,  3}};

    static std::vector<std::vector<double> > Bc_sparse_4x3 =
    {{1,  0,  3},
     {0,  0,  0},
     {0,  2,  0},
     {0,  0,  3}};

    static std::vector<std::vector<double> > BT_sparse_3x4 =
    {{1,  0,  3},
     {1,  2,  0},
     {0,  2,  0},
     {0,  0,  3}};

    static std::vector<std::vector<double> > Answer_rr_sparse_9x12 =
    {{0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 2, 2, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {3, 0, 0, 3,  0, 0, 0, 0,  0, 0, 0, 0},

     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 4, 4, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  6, 0, 0, 6,  0, 0, 0, 0},

     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0}
    };

    static std::vector<std::vector<double> > Answer_sparse_9x12 =
    {{1, 1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 2, 2, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {3, 0, 0, 3,  0, 0, 0, 0,  0, 0, 0, 0},

     {0, 0, 0, 0,  2, 2, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 4, 4, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  6, 0, 0, 6,  0, 0, 0, 0},

     {3, 3, 0, 0,  0, 0, 0, 0,  4, 4, 0, 0},
     {0, 6, 6, 0,  0, 0, 0, 0,  0, 8, 8, 0},
     {9, 0, 0, 9,  0, 0, 0, 0, 12, 0, 0,12}
    };

    static std::vector<std::vector<double> > Answer_sparse_9x12_Lower =
    {{1, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 2, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {3, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},

     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  6, 0, 0, 0,  0, 0, 0, 0},

     {3, 3, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 6, 6, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {9, 0, 0, 9,  0, 0, 0, 0, 12, 0, 0, 0}
    };

    static std::vector<std::vector<double> > Answer_sparse_9x12_Lower_Ones =
    {{1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1},
     {0, 2, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1},
     {3, 0, 0, 1,  1, 1, 1, 1,  1, 1, 1, 1},

     {0, 0, 0, 0,  1, 1, 1, 1,  1, 1, 1, 1},
     {0, 0, 0, 0,  0, 1, 1, 1,  1, 1, 1, 1},
     {0, 0, 0, 0,  6, 0, 1, 1,  1, 1, 1, 1},

     {3, 3, 0, 0,  0, 0, 0, 1,  1, 1, 1, 1},
     {0, 6, 6, 0,  0, 0, 0, 0,  1, 1, 1, 1},
     {9, 0, 0, 9,  0, 0, 0, 0, 12, 1, 1, 1}
    };

    static std::vector<std::vector<double> > Answer_sparse_9x12_NotLower
    {{0, 1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 2, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 3,  0, 0, 0, 0,  0, 0, 0, 0},

     {0, 0, 0, 0,  2, 2, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 4, 4, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 6,  0, 0, 0, 0},

     {0, 0, 0, 0,  0, 0, 0, 0,  4, 4, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 8, 8, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0,12}
    };

    static std::vector<std::vector<double> > Answer_sparse_9x12_NotLower_Ones
    {{1, 1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {1, 1, 2, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {1, 1, 1, 3,  0, 0, 0, 0,  0, 0, 0, 0},

     {1, 1, 1, 1,  2, 2, 0, 0,  0, 0, 0, 0},
     {1, 1, 1, 1,  1, 4, 4, 0,  0, 0, 0, 0},
     {1, 1, 1, 1,  1, 1, 0, 6,  0, 0, 0, 0},

     {1, 1, 1, 1,  1, 1, 1, 0,  4, 4, 0, 0},
     {1, 1, 1, 1,  1, 1, 1, 1,  0, 8, 8, 0},
     {1, 1, 1, 1,  1, 1, 1, 1,  1, 0, 0,12}
    };

    static std::vector<std::vector<double> > Answer_rc_sparse_9x12 =
    {{1, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 2, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {3, 0, 0, 3,  0, 0, 0, 0,  0, 0, 0, 0},

     {0, 0, 0, 0,  2, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 4, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  6, 0, 0, 6,  0, 0, 0, 0},

     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0}
    };

    static std::vector<std::vector<double> > Answer_cr_sparse_9x12 =
    {{0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},

     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 4, 4, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  6, 0, 0, 6,  0, 0, 0, 0},

     {0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 8, 8, 0},
     {0, 0, 0, 0,  0, 0, 0, 0, 12, 0, 0,12}
    };

    static std::vector<std::vector<double> > Ones_4x4 =
    {{1, 1, 1, 1},
     {1, 1, 1, 1},
     {1, 1, 1, 1},
     {1, 1, 1, 1}};

    static std::vector<std::vector<double> > Ones_3x4 =
    {{1, 1, 1, 1},
     {1, 1, 1, 1},
     {1, 1, 1, 1}};

    static std::vector<std::vector<double> > Ones_3x3 =
    {{1, 1, 1},
     {1, 1, 1},
     {1, 1, 1}};

    static std::vector<std::vector<double> > Ones_4x3 =
    {{1, 1, 1},
     {1, 1, 1},
     {1, 1, 1},
     {1, 1, 1}};

    static std::vector<std::vector<double> > Ones_9x9 =
    {{1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1}};

    static std::vector<std::vector<double> > Ones_9x12 =
    {{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
     {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};

    static std::vector<std::vector<double> > Identity_3x3 =
    {{1, 0, 0},
     {0, 1, 0},
     {0, 0, 1}};

    static std::vector<std::vector<double> > Lower_3x3 =
    {{1, 0, 0},
     {1, 1, 0},
     {1, 1, 1}};

    static std::vector<std::vector<double> > Lower_3x4 =
    {{1, 0, 0, 0},
     {1, 1, 0, 0},
     {1, 1, 1, 0}};

    static std::vector<std::vector<double> > Lower_4x4 =
    {{1, 0, 0, 0},
     {1, 1, 0, 0},
     {1, 1, 1, 0},
     {1, 1, 1, 1}};

    static std::vector<std::vector<double> > NotLower_3x3 =
    {{0, 1, 1},
     {0, 0, 1},
     {0, 0, 0}};

    static std::vector<std::vector<double> > NotLower_3x4 =
    {{0, 1, 1, 1},
     {0, 0, 1, 1},
     {0, 0, 0, 1}};

    static std::vector<std::vector<double> > NotLower_4x4 =
    {{0, 1, 1, 1},
     {0, 0, 1, 1},
     {0, 0, 0, 1},
     {0, 0, 0, 0}};

    static std::vector<std::vector<double> > LowerMask_3x4 =
    {{1, 0,    0,   0},
     {1, 0.5,  0,   0},
     {1, -1.0, 1.5, 0}};

    static std::vector<std::vector<bool> > LowerBool_3x4 =
    {{true, false, false, false},
     {true, true,  false, false},
     {true, true,  true,  false}};

    static std::vector<std::vector<bool> > LowerBool_3x3 =
    {{true, false, false},
     {true, true,  false},
     {true, true,  true}};

    static std::vector<std::vector<bool> > NotLowerBool_3x3 =
    {{false,  true, true},
     {false, false, true},
     {false, false, false}};

    static std::vector<std::vector<double> > Lower_9x9 =
    {{1, 0, 0, 0,  0, 0, 0, 0,  0},
     {1, 1, 0, 0,  0, 0, 0, 0,  0},
     {1, 1, 1, 0,  0, 0, 0, 0,  0},
     {1, 1, 1, 1,  0, 0, 0, 0,  0},
     {1, 1, 1, 1,  1, 0, 0, 0,  0},
     {1, 1, 1, 1,  1, 1, 0, 0,  0},
     {1, 1, 1, 1,  1, 1, 1, 0,  0},
     {1, 1, 1, 1,  1, 1, 1, 1,  0},
     {1, 1, 1, 1,  1, 1, 1, 1,  1}};

    static std::vector<std::vector<double> > NotLower_9x9 =
    {{0, 1, 1, 1,  1, 1, 1, 1,  1},
     {0, 0, 1, 1,  1, 1, 1, 1,  1},
     {0, 0, 0, 1,  1, 1, 1, 1,  1},
     {0, 0, 0, 0,  1, 1, 1, 1,  1},
     {0, 0, 0, 0,  0, 1, 1, 1,  1},
     {0, 0, 0, 0,  0, 0, 1, 1,  1},
     {0, 0, 0, 0,  0, 0, 0, 1,  1},
     {0, 0, 0, 0,  0, 0, 0, 0,  1},
     {0, 0, 0, 0,  0, 0, 0, 0,  0}};

    static std::vector<std::vector<double> > Lower_9x12 =
    {{1, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {1, 1, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {1, 1, 1, 0,  0, 0, 0, 0,  0, 0, 0, 0},
     {1, 1, 1, 1,  0, 0, 0, 0,  0, 0, 0, 0},
     {1, 1, 1, 1,  1, 0, 0, 0,  0, 0, 0, 0},
     {1, 1, 1, 1,  1, 1, 0, 0,  0, 0, 0, 0},
     {1, 1, 1, 1,  1, 1, 1, 0,  0, 0, 0, 0},
     {1, 1, 1, 1,  1, 1, 1, 1,  0, 0, 0, 0},
     {1, 1, 1, 1,  1, 1, 1, 1,  1, 0, 0, 0}};

    static std::vector<std::vector<double> > NotLower_9x12 =
    {{0, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1},
     {0, 0, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1},
     {0, 0, 0, 1,  1, 1, 1, 1,  1, 1, 1, 1},
     {0, 0, 0, 0,  1, 1, 1, 1,  1, 1, 1, 1},
     {0, 0, 0, 0,  0, 1, 1, 1,  1, 1, 1, 1},
     {0, 0, 0, 0,  0, 0, 1, 1,  1, 1, 1, 1},
     {0, 0, 0, 0,  0, 0, 0, 1,  1, 1, 1, 1},
     {0, 0, 0, 0,  0, 0, 0, 0,  1, 1, 1, 1},
     {0, 0, 0, 0,  0, 0, 0, 0,  0, 1, 1, 1}};

    static std::vector<std::vector<double> > M_9x9 =
        {{1, 0, 0, 1, 0, 0, 1, 0, 0},
         {1, 1, 0, 1, 1, 0, 1, 1, 0},
         {1, 1, 1, 1, 1, 1, 1, 1, 1},
         {1, 0, 0, 1, 0, 0, 1, 0, 0},
         {1, 1, 0, 1, 1, 0, 1, 1, 0},
         {1, 1, 1, 1, 1, 1, 1, 1, 1},
         {1, 0, 0, 1, 0, 0, 1, 0, 0},
         {1, 1, 0, 1, 1, 0, 1, 1, 0},
         {1, 1, 1, 1, 1, 1, 1, 1, 1}};

    static std::vector<std::vector<double> > M_9x12 =
        {{1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0},
         {1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0},
         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
         {1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0},
         {1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0},
         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
         {1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0},
         {1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0},
         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};

    static std::vector<std::vector<double> > M_10x12 =
        {{1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0},
         {1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0},
         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
         {1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0},
         {1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0},
         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
         {1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0},
         {1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0},
         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
         {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};
}

//****************************************************************************
// API error tests
//****************************************************************************

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_ABT_bad_dimensions)
{
    Matrix<double> A(A_sparse_3x3, 0.); // 3x3
    Matrix<double> B(B_sparse_4x3, 0.); // 4x3
    Matrix<double> ones9x12(Ones_9x12, 0.);

    Matrix<double> M9x9(M_9x9, 0.);
    Matrix<double> M9x12(M_9x12, 0.);
    Matrix<double> M10x12(M_10x12, 0.);

    Matrix<double> result9x12(9, 12);
    Matrix<double> result9x9(9, 9);
    Matrix<double> result12x12(12, 12);

    // NoMask_NoAccum_AB

    // dim(C) != dim(A)*dim(B)
    BOOST_CHECK_THROW(
        (kronecker(result9x9, NoMask(), NoAccumulate(),
                   Times<double>(), A, transpose(B))),
        DimensionException);

    BOOST_CHECK_THROW(
        (kronecker(result12x12, NoMask(), NoAccumulate(),
                   Times<double>(), A, transpose(B))),
        DimensionException);

    // NoMask_Accum_AB

    // dim(C) != dim(A)*dim(B)
    BOOST_CHECK_THROW(
        (kronecker(result9x9, NoMask(), Plus<double>(),
                   Times<double>(), A, transpose(B))),
        DimensionException);

    BOOST_CHECK_THROW(
        (kronecker(result12x12, NoMask(), Plus<double>(),
                   Times<double>(), A, transpose(B))),
        DimensionException);

    // Mask_NoAccum

    // incompatible mask matrix dimensions
    // nrows(C) != nrows(M)
    BOOST_CHECK_THROW(
        (kronecker(result9x12, M10x12, NoAccumulate(),
                   Times<double>(), A, transpose(B))),
        DimensionException);

    // ncols(C) != ncols(M)
    BOOST_CHECK_THROW(
        (kronecker(result9x12, M9x9, NoAccumulate(),
                   Times<double>(), A, transpose(B))),
        DimensionException);

    BOOST_CHECK_THROW(
        (kronecker(ones9x12, M9x12, NoAccumulate(),
                   Times<double>(), A, transpose(A),
                   REPLACE)),
        DimensionException);

    // Mask_Accum (replace and merge)

    // incompatible mask matrix dimensions
    BOOST_CHECK_THROW(
        (kronecker(ones9x12, M10x12, Second<double>(),
                   Times<double>(), A, transpose(B), REPLACE)),
        DimensionException);

    // incompatible mask matrix dimensions
    BOOST_CHECK_THROW(
        (kronecker(ones9x12, M9x9, Second<double>(),
                   Times<double>(), A, transpose(B), MERGE)),
        DimensionException);

    // CompMask_NoAccum (replace and merge)

    // incompatible mask matrix dimensions
    BOOST_CHECK_THROW(
        (kronecker(ones9x12, complement(M10x12), NoAccumulate(),
                   Times<double>(), A, transpose(B), REPLACE)),
        DimensionException);

    // incompatible mask matrix dimensions
    BOOST_CHECK_THROW(
        (kronecker(ones9x12, complement(M9x9), NoAccumulate(),
                   Times<double>(), A, transpose(B), MERGE)),
        DimensionException);

    // CompMask_Accum (replace and merge)

    // incompatible mask matrix dimensions
    BOOST_CHECK_THROW(
        (kronecker(ones9x12, complement(M10x12), Plus<double>(),
                   Times<double>(), A, transpose(B), REPLACE)),
        DimensionException);

    // incompatible mask matrix dimensions
    BOOST_CHECK_THROW(
        (kronecker(ones9x12, complement(M9x9), Plus<double>(),
                   Times<double>(), A, transpose(B), MERGE)),
        DimensionException);
}

//****************************************************************************
// NoMask_NoAccum
//****************************************************************************

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_NoAccum_ABT)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> B(B_sparse_4x3, 0.);

    Matrix<double> answer(Answer_sparse_9x12, 0.);

    kronecker(C, NoMask(), NoAccumulate(),
              Times<double>(), A, transpose(B));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_NoAccum_ABT_empty)
{
    Matrix<double> Zero(3, 3);
    Matrix<double> Ones(Ones_3x3, 0.);
    Matrix<double> C(Ones_9x9, 0.);
    Matrix<double> mD(Ones_9x9, 0.);
    Matrix<double> Zero9x9(9, 9);

    kronecker(C, NoMask(), NoAccumulate(), Times<double>(), Zero, transpose(Ones));
    BOOST_CHECK_EQUAL(C, Zero9x9);

    kronecker(mD, NoMask(), NoAccumulate(), Times<double>(), Ones, transpose(Zero));
    BOOST_CHECK_EQUAL(mD, Zero9x9);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_NoAccum_ABT_dense)
{
    Matrix<double> Ones3x3(Ones_3x3, 0.);
    Matrix<double> Ones4x3(Ones_4x3, 0.);
    Matrix<double> Ones9x12(Ones_9x12, 0.);
    Matrix<double> result(9, 12);

    kronecker(result, NoMask(), NoAccumulate(),
              Times<double>(), Ones3x3, transpose(Ones4x3));

    BOOST_CHECK_EQUAL(result, Ones9x12);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_NoAccum_ABT_empty_rows)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);

    Matrix<double> answer(Answer_rr_sparse_9x12, 0.);

    kronecker(C, NoMask(), NoAccumulate(),
              Times<double>(), A, transpose(B));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_NoAccum_ABT_emptyRowA_emptyColB)
{
    Matrix<double> result(9, 12);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Bc_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rc_sparse_9x12, 0.);

    kronecker(result, NoMask(), NoAccumulate(),
              Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(result, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_NoAccum_ABT_emptyColA_emptyRowB)
{
    Matrix<double> result(9, 12);
    Matrix<double> A(Ac_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_cr_sparse_9x12, 0.);

    kronecker(result, NoMask(), NoAccumulate(),
              Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(result, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_NoAccum_ABT_ABdup)
{
    // Build some matrices.
    Matrix<double, DirectedMatrixTag> mat(A_sparse_3x3, 0.);
    Matrix<double, DirectedMatrixTag> m3(9, 9);
    Matrix<double, DirectedMatrixTag> answer(AAT_sparse_9x9, 0.);

    kronecker(m3, NoMask(), NoAccumulate(),
              Times<double>(), mat, transpose(mat));

    BOOST_CHECK_EQUAL(m3, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_NoAccum_ABT_ACdup)
{
    Matrix<double> C(A_sparse_3x3, 0.);
    Matrix<double> B(1, 1);
    B.setElement(0, 0, 1.0);

    Matrix<double> answer(A_sparse_3x3, 0.);

    kronecker(C, NoMask(), NoAccumulate(),
              Times<double>(), C, transpose(B));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_NoAccum_ABT_BCdup)
{
    Matrix<double> A(1, 1);
    Matrix<double> C(A_sparse_3x3, 0.);
    A.setElement(0, 0, 1.0);

    Matrix<double> answer(AT_sparse_3x3, 0.);

    kronecker(C, NoMask(), NoAccumulate(),
              Times<double>(), A, transpose(C));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
// NoMask_Accum
//****************************************************************************

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_Accum_ABT)
{
    Matrix<double> C(Ones_9x12, 0.);
    Matrix<double> A(A_sparse_3x3, 0.); // 3x3
    Matrix<double> B(B_sparse_4x3, 0.); // 3x4

    Matrix<double> answer(Answer_sparse_9x12, 0.);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), answer, C);

    kronecker(C, NoMask(), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_Accum_ABT_empty)
{
    Matrix<double> Zero(3, 3);
    Matrix<double> Ones(Ones_3x3, 0.);
    Matrix<double> C(Ones_9x9, 0.);
    Matrix<double> mD(Ones_9x9, 0.);
    Matrix<double> answer(Ones_9x9, 0.);

    kronecker(C, NoMask(), Plus<double>(), Times<double>(), Zero, transpose(Ones));
    BOOST_CHECK_EQUAL(C, answer);

    kronecker(mD, NoMask(), Plus<double>(), Times<double>(), Ones, transpose(Zero));
    BOOST_CHECK_EQUAL(mD, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_Accum_ABT_dense)
{
    Matrix<double> Ones3x3(Ones_3x3, 0.);
    Matrix<double> Ones4x3(Ones_4x3, 0.);
    Matrix<double> Ones9x12(Ones_9x12, 0.);
    Matrix<double> result(Ones_9x12, 0.);
    Matrix<double> answer(9, 12);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones9x12, Ones9x12);

    kronecker(result, NoMask(), Plus<double>(),
              Times<double>(), Ones3x3, transpose(Ones4x3));

    BOOST_CHECK_EQUAL(result, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_Accum_ABT_empty_rows)
{
    Matrix<double> C(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);

    Matrix<double> answer(Answer_rr_sparse_9x12, 0.);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), C, answer);

    kronecker(C, NoMask(), Plus<double>(), Times<double>(), A, transpose(B));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_Accum_ABT_emptyRowA_emptyColB)
{
    Matrix<double> result(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Bc_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rc_sparse_9x12, 0.);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), answer, result);

    kronecker(result, NoMask(), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(result, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_Accum_ABT_emptyColA_emptyRowB)
{
    Matrix<double> result(Ones_9x12, 0.);
    Matrix<double> A(Ac_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_cr_sparse_9x12, 0.);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), answer, result);

    kronecker(result, NoMask(), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(result, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_Accum_ABT_ABdup)
{
    // Build some matrices.
    Matrix<double, DirectedMatrixTag> mat(A_sparse_3x3, 0.);
    Matrix<double, DirectedMatrixTag> m3(Ones_9x9, 0.);
    Matrix<double, DirectedMatrixTag> answer(AAT_sparse_9x9, 0.);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), answer, m3);

    kronecker(m3, NoMask(), Plus<double>(), Times<double>(), mat, transpose(mat));

    BOOST_CHECK_EQUAL(m3, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_Accum_ABT_ACdup)
{
    Matrix<double> C(A_sparse_3x3, 0.);
    Matrix<double> B(1, 1);
    B.setElement(0, 0, 1.0);
    Matrix<double> answer(A_sparse_3x3, 0.);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), answer, answer);

    kronecker(C, NoMask(), Plus<double>(), Times<double>(), C, transpose(B));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_NoMask_Accum_ABT_BCdup)
{
    Matrix<double> A(1, 1);
    Matrix<double> C(A_sparse_3x3, 0.);
    A.setElement(0, 0, 1.0);
    Matrix<double> answer(A_sparse_3x3, 0.);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             answer, transpose(answer));

    kronecker(C, NoMask(), Plus<double>(), Times<double>(), A, transpose(C));

    BOOST_CHECK_EQUAL(C, answer);
}

// ****************************************************************************
// Mask_NoAccum
// ****************************************************************************

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABT)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(A_sparse_3x3, 0.); // 1 0 0 / 0 2 0 / 3 0 4
    Matrix<double> B(B_sparse_4x3, 0.); // 1 1 0 0 / 0 2 2 0 / 3 0 0 3

    Matrix<double> Identity(Identity_3x3, 0.0);

    Matrix<double> Empty(9, 12);
    Matrix<double> Ones(Ones_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    Matrix<double> Answer_9x12(Answer_sparse_9x12, 0.);
    Matrix<double> Answer_9x12_Lower(Answer_sparse_9x12_Lower, 0.);
    Matrix<double> Answer_9x12_NotLower(Answer_sparse_9x12_NotLower, 0.);
    Matrix<double> Answer_9x12_Lower_Ones(Answer_sparse_9x12_Lower_Ones, 0.);
    Matrix<double> Answer_9x12_NotLower_Ones(Answer_sparse_9x12_NotLower_Ones, 0.);

    // Merge
    // Mempty vs Mfull vs Mlower

    C = Ones;
    kronecker(C, Empty, NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, Ones,  NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Answer_9x12);

    C = Ones;
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Answer_9x12_Lower_Ones);

    C = Ones;
    kronecker(C, MNotLower, NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Answer_9x12_NotLower_Ones);

    // Replace
    // Mempty vs Mfull vs Mlower

    C = Ones;
    kronecker(C, Empty, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    kronecker(C, Ones,  NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Answer_9x12);

    C = Ones;
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Answer_9x12_Lower);

    C = Ones;
    kronecker(C, MNotLower, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Answer_9x12_NotLower);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABTM_empty)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(A_sparse_3x3, 0.); // 1 0 0 / 0 2 0 / 3 0 4
    Matrix<double> B(B_sparse_4x3, 0.); // 1 1 0 0 / 0 2 2 0 / 3 0 0 3

    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> Empty(9, 12);
    Matrix<double> Empty3x3(3, 3);
    Matrix<double> Empty4x3(4, 3);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Merge
    C = Ones;
    kronecker(C, Empty, NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, MLower, NoAccumulate(), Times<double>(), Empty3x3, transpose(B));
    BOOST_CHECK_EQUAL(C, MNotLower);

    C = Ones;
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(Empty4x3));
    BOOST_CHECK_EQUAL(C, MNotLower);

    // Replace
    C = Ones;
    kronecker(C, Empty, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    kronecker(C, MLower, NoAccumulate(), Times<double>(), Empty3x3, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(Empty4x3), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABTM_dense)
{
    Matrix<double> A(Ones_3x3, 0.);
    Matrix<double> B(Ones_4x3, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> C(9, 12);

    C = Ones;
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, MLower);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABT_empty_rows)
{
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rr_sparse_9x12, 0.);
    Matrix<double> C(9, 12);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);

}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABT_emptyRowA_emptyColB)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Bc_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rc_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // REPLACE
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABT_emptyColA_emptyRowB)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ac_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_cr_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // REPLACE
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABT_emptyRowM)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> B(B_sparse_4x3, 0.);
    Matrix<double> answer(Answer_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Replace
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABT_ABdup)
{
    Matrix<double> C(9,9);
    Matrix<double> Ones(Ones_9x9, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> answer(AAT_sparse_9x9, 0.);

    Matrix<double> MLower(Lower_9x9, 0.);
    Matrix<double> MNotLower(NotLower_9x9, 0.);

    // Replace
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(A), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(A));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABT_ACdup)
{
    Matrix<double> Ones(Ones_3x4, 0.);
    Matrix<double> mat(B_sparse_3x4, 0.);
    Matrix<double> C(3, 4);
    Matrix<double> B(1, 1);    B.setElement(0, 0, 1.0);
    Matrix<double> answer(mat);

    Matrix<double> MLower(Lower_3x4, 0.);
    Matrix<double> MNotLower(NotLower_3x4, 0.);

    // Build some matrices.

    // Replace
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, MLower, NoAccumulate(), Times<double>(), C, transpose(B), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = mat;
    kronecker(C, MLower, NoAccumulate(), Times<double>(), C, transpose(B));

    BOOST_CHECK_EQUAL(C, mat);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABT_BCdup)
{
    // Build some matrices.
    Matrix<double> mat(A_sparse_3x3, 0.);
    Matrix<double> C(3,3);
    Matrix<double> A(1, 1);    A.setElement(0, 0, 1.0);
    Matrix<double> answer(mat);

    Matrix<double> MLower(Lower_3x3, 0.);
    Matrix<double> MNotLower(NotLower_3x3, 0.);

    // Replace
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(),
              MLower, transpose(answer));
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(C), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(),
              transpose(MLower), transpose(answer));
    kronecker(C, MLower, NoAccumulate(), Times<double>(), A, transpose(C));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_NoAccum_ABT_MCdup)
{
    // Build some matrices.
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> B(B_sparse_4x3, 0.);
    Matrix<double> answer(Answer_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Replace
    C = MLower;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, C, NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = MLower;
    kronecker(C, C, NoAccumulate(), Times<double>(), A, transpose(B));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
// Mask_Accum
//****************************************************************************

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABT)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(A_sparse_3x3, 0.); // 1 0 0 / 0 2 0 / 3 0 4
    Matrix<double> B(B_sparse_4x3, 0.); // 1 1 0 0 / 0 2 2 0 / 3 0 0 3

    Matrix<double> Identity(Identity_3x3, 0.0);

    Matrix<double> answer(9, 12);
    Matrix<double> Empty(9, 12);
    Matrix<double> Ones(Ones_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    Matrix<double> Answer_9x12(Answer_sparse_9x12, 0.);
    Matrix<double> Answer_9x12_Lower(Answer_sparse_9x12_Lower, 0.);
    Matrix<double> Answer_9x12_NotLower(Answer_sparse_9x12_NotLower, 0.);
    Matrix<double> Answer_9x12_Lower_Ones(Answer_sparse_9x12_Lower_Ones, 0.);
    Matrix<double> Answer_9x12_NotLower_Ones(Answer_sparse_9x12_NotLower_Ones, 0.);

    // Merge
    // Mempty vs Mfull vs Mlower

    C = Ones;
    kronecker(C, Empty, Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, Answer_9x12);
    kronecker(C, Ones,  Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer); //Answer_9x12);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, Answer_9x12_Lower);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, Answer_9x12_NotLower);
    kronecker(C, MNotLower, Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);

    // Replace
    // Mempty vs Mfull vs Mlower

    C = Ones;
    kronecker(C, Empty, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, Answer_9x12);
    kronecker(C, Ones,  Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             MLower, Answer_9x12_Lower);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             MNotLower, Answer_9x12_NotLower);
    kronecker(C, MNotLower, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABTM_empty)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(A_sparse_3x3, 0.); // 1 0 0 / 0 2 0 / 3 0 4
    Matrix<double> B(B_sparse_4x3, 0.); // 1 1 0 0 / 0 2 2 0 / 3 0 0 3

    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> Empty(9, 12);
    Matrix<double> Empty3x3(3, 3);
    Matrix<double> Empty4x3(4, 3);

    Matrix<double> MLower(Lower_9x12, 0.);

    // Merge
    C = Ones;
    kronecker(C, Empty, Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, MLower, Plus<double>(), Times<double>(), Empty3x3, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(Empty4x3));
    BOOST_CHECK_EQUAL(C, Ones);

    // Replace
    C = Ones;
    kronecker(C, Empty, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    kronecker(C, MLower, Plus<double>(), Times<double>(), Empty3x3, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, MLower);

    C = Ones;
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(Empty4x3), REPLACE);
    BOOST_CHECK_EQUAL(C, MLower);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABTM_dense)
{
    Matrix<double> A(Ones_3x3, 0.);
    Matrix<double> B(Ones_4x3, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> C(9, 12);
    Matrix<double> answer(9, 12);

    C = Ones;
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B));
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, MLower);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             MLower, MLower);
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABT_empty_rows)
{
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rr_sparse_9x12, 0.);
    Matrix<double> C(9, 12);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABT_emptyRowA_emptyColB)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Bc_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rc_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // REPLACE
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABT_emptyColA_emptyRowB)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ac_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_cr_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // REPLACE
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABT_emptyRowM)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> B(B_sparse_4x3, 0.);
    Matrix<double> answer(Answer_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Replace
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABT_ABdup)
{
    Matrix<double> C(9,9);
    Matrix<double> Ones(Ones_9x9, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> answer(AAT_sparse_9x9, 0.);

    Matrix<double> MLower(Lower_9x9, 0.);
    Matrix<double> MNotLower(NotLower_9x9, 0.);

    // Replace
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(A), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(A));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABT_ACdup)
{
    // Build some matrices.
    Matrix<double> Ones(Ones_3x4, 0.);
    Matrix<double> mat(B_sparse_3x4, 0.);
    Matrix<double> C(3, 4);
    Matrix<double> B(1, 1);    B.setElement(0, 0, 1.0);
    Matrix<double> answer(mat);

    Matrix<double> MLower(Lower_3x4, 0.);
    Matrix<double> MNotLower(NotLower_3x4, 0.);

    // Replace
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), answer, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), C, transpose(B), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, mat);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), mat, answer);
    kronecker(C, MLower, Plus<double>(), Times<double>(), C, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABT_BCdup)
{
    // Build some matrices.
    Matrix<double> mat(A_sparse_3x3, 0.);
    Matrix<double> C(3,3);
    Matrix<double> A(1, 1);    A.setElement(0, 0, 1.0);
    Matrix<double> answer(mat);

    Matrix<double> MLower(Lower_3x3, 0.);
    Matrix<double> MNotLower(NotLower_3x3, 0.);

    // Replace
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower,transpose(mat));
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), answer, mat);
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(C), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = mat;
    kronecker(C, MLower, Plus<double>(), Times<double>(), A, transpose(C));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_Mask_Accum_ABT_MCdup)
{
    // Build some matrices.
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> B(B_sparse_4x3, 0.);
    Matrix<double> answer(Answer_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Replace
    C = MLower;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, C, Plus<double>(), Times<double>(), A, transpose(B), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = MLower;
    kronecker(C, C, Plus<double>(), Times<double>(), A, transpose(B));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
// CompMask_NoAccum
//****************************************************************************

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABT)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(A_sparse_3x3, 0.); // 1 0 0 / 0 2 0 / 3 0 4
    Matrix<double> B(B_sparse_4x3, 0.); // 1 1 0 0 / 0 2 2 0 / 3 0 0 3

    Matrix<double> Identity(Identity_3x3, 0.0);

    Matrix<double> Empty(9, 12);
    Matrix<double> Ones(Ones_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    Matrix<double> Answer_9x12(Answer_sparse_9x12, 0.);
    Matrix<double> Answer_9x12_Lower(Answer_sparse_9x12_Lower, 0.);
    Matrix<double> Answer_9x12_NotLower(Answer_sparse_9x12_NotLower, 0.);
    Matrix<double> Answer_9x12_Lower_Ones(Answer_sparse_9x12_Lower_Ones, 0.);
    Matrix<double> Answer_9x12_NotLower_Ones(Answer_sparse_9x12_NotLower_Ones, 0.);

    // Merge
    // Mempty vs Mfull vs Mlower

    C = Ones;
    kronecker(C, complement(Ones), NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, complement(Empty),  NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Answer_9x12);

    C = Ones;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Answer_9x12_Lower_Ones);

    C = Ones;
    kronecker(C, complement(MLower), NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Answer_9x12_NotLower_Ones);

    // Replace
    // Mempty vs Mfull vs Mlower

    C = Ones;
    kronecker(C, complement(Ones), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    kronecker(C, complement(Empty),  NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Answer_9x12);

    C = Ones;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Answer_9x12_Lower);

    C = Ones;
    kronecker(C, complement(MLower), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Answer_9x12_NotLower);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABTM_empty)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(A_sparse_3x3, 0.); // 1 0 0 / 0 2 0 / 3 0 4
    Matrix<double> B(B_sparse_4x3, 0.); // 1 1 0 0 / 0 2 2 0 / 3 0 0 3

    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> Empty(9, 12);
    Matrix<double> Empty3x3(3, 3);
    Matrix<double> Empty4x3(4, 3);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Merge
    C = Ones;
    kronecker(C, complement(Ones), NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), Empty3x3, transpose(B));
    BOOST_CHECK_EQUAL(C, MNotLower);

    C = Ones;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(Empty4x3));
    BOOST_CHECK_EQUAL(C, MNotLower);

    // Replace
    C = Ones;
    kronecker(C, complement(Ones), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), Empty3x3, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(Empty4x3), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABTM_dense)
{
    Matrix<double> A(Ones_3x3, 0.);
    Matrix<double> B(Ones_4x3, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> C(9, 12);

    C = Ones;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, MLower);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABT_empty_rows)
{
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rr_sparse_9x12, 0.);
    Matrix<double> C(9, 12);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);

}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABT_emptyRowA_emptyColB)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Bc_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rc_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // REPLACE
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABT_emptyColA_emptyRowB)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ac_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_cr_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // REPLACE
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABT_emptyRowM)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> B(B_sparse_4x3, 0.);
    Matrix<double> answer(Answer_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Replace
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABT_ABdup)
{
    Matrix<double> C(9,9);
    Matrix<double> Ones(Ones_9x9, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> answer(AAT_sparse_9x9, 0.);

    Matrix<double> MLower(Lower_9x9, 0.);
    Matrix<double> MNotLower(NotLower_9x9, 0.);

    // Replace
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(A), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(A));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABT_ACdup)
{
    Matrix<double> Ones(Ones_3x4, 0.);
    Matrix<double> mat(B_sparse_3x4, 0.);
    Matrix<double> C(3, 4);
    Matrix<double> B(1, 1);    B.setElement(0, 0, 1.0);
    Matrix<double> answer(mat);

    Matrix<double> MLower(Lower_3x4, 0.);
    Matrix<double> MNotLower(NotLower_3x4, 0.);

    // Build some matrices.

    // Replace
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), C, transpose(B), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = mat;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), C, transpose(B));

    BOOST_CHECK_EQUAL(C, mat);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABT_BCdup)
{
    // Build some matrices.
    Matrix<double> mat(A_sparse_3x3, 0.);
    Matrix<double> C(3,3);
    Matrix<double> A(1, 1);    A.setElement(0, 0, 1.0);
    Matrix<double> answer(AT_sparse_3x3, 0.);

    Matrix<double> MLower(Lower_3x3, 0.);
    Matrix<double> MNotLower(NotLower_3x3, 0.);

    // Replace
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(C), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = mat;
    kronecker(C, complement(MNotLower), NoAccumulate(), Times<double>(), A, transpose(C));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_NoAccum_ABT_MCdup)
{
    // Build some matrices.
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> B(B_sparse_4x3, 0.);
    Matrix<double> answer(Answer_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Replace
    C = MLower;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MNotLower, answer);
    kronecker(C, complement(C), NoAccumulate(), Times<double>(), A, transpose(B), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = MLower;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, complement(C), NoAccumulate(), Times<double>(), A, transpose(B));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
// CompMask_Accum
//****************************************************************************

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABT)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(A_sparse_3x3, 0.); // 1 0 0 / 0 2 0 / 3 0 4
    Matrix<double> B(B_sparse_4x3, 0.); // 1 1 0 0 / 0 2 2 0 / 3 0 0 3

    Matrix<double> Identity(Identity_3x3, 0.0);

    Matrix<double> answer(9, 12);
    Matrix<double> Empty(9, 12);
    Matrix<double> Ones(Ones_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    Matrix<double> Answer_9x12(Answer_sparse_9x12, 0.);
    Matrix<double> Answer_9x12_Lower(Answer_sparse_9x12_Lower, 0.);
    Matrix<double> Answer_9x12_NotLower(Answer_sparse_9x12_NotLower, 0.);
    Matrix<double> Answer_9x12_Lower_Ones(Answer_sparse_9x12_Lower_Ones, 0.);
    Matrix<double> Answer_9x12_NotLower_Ones(Answer_sparse_9x12_NotLower_Ones, 0.);

    // Merge
    // Mempty vs Mfull vs Mlower

    C = Ones;
    kronecker(C, complement(Ones), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, Answer_9x12);
    kronecker(C, complement(Empty),  Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer); //Answer_9x12);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, Answer_9x12_Lower);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, Answer_9x12_NotLower);
    kronecker(C, complement(MLower), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);

    // Replace
    // Mempty vs Mfull vs Mlower

    C = Ones;
    kronecker(C, complement(Ones), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, Answer_9x12);
    kronecker(C, complement(Empty),  Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             MLower, Answer_9x12_Lower);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             MNotLower, Answer_9x12_NotLower);
    kronecker(C, complement(MLower), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABTM_empty)
{
    Matrix<double> C(9, 12);
    Matrix<double> A(A_sparse_3x3, 0.); // 1 0 0 / 0 2 0 / 3 0 4
    Matrix<double> B(B_sparse_4x3, 0.); // 1 1 0 0 / 0 2 2 0 / 3 0 0 3

    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> Empty(9, 12);
    Matrix<double> Empty3x3(3, 3);
    Matrix<double> Empty4x3(4, 3);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Merge
    C = Ones;
    kronecker(C, complement(Ones), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), Empty3x3, transpose(B));
    BOOST_CHECK_EQUAL(C, Ones);

    C = Ones;
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(Empty4x3));
    BOOST_CHECK_EQUAL(C, Ones);

    // Replace
    C = Ones;
    kronecker(C, complement(Ones), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, Empty);

    C = Ones;
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), Empty3x3, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, MLower);

    C = Ones;
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(Empty4x3), REPLACE);
    BOOST_CHECK_EQUAL(C, MLower);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABTM_dense)
{
    Matrix<double> A(Ones_3x3, 0.);
    Matrix<double> B(Ones_4x3, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> C(9, 12);
    Matrix<double> answer(9, 12);

    C = Ones;
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B));
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             Ones, MLower);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(),
             MLower, MLower);
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABT_empty_rows)
{
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rr_sparse_9x12, 0.);
    Matrix<double> C(9, 12);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABT_emptyRowA_emptyColB)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ar_sparse_3x3, 0.);
    Matrix<double> B(Bc_sparse_4x3, 0.);
    Matrix<double> answer(Answer_rc_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // REPLACE
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABT_emptyColA_emptyRowB)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(Ac_sparse_3x3, 0.);
    Matrix<double> B(Br_sparse_4x3, 0.);
    Matrix<double> answer(Answer_cr_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // REPLACE
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABTM_emptyRowM)
{
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> B(B_sparse_4x3, 0.);
    Matrix<double> answer(Answer_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Replace
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);
    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABT_ABdup)
{
    Matrix<double> C(9,9);
    Matrix<double> Ones(Ones_9x9, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> answer(AAT_sparse_9x9, 0.);

    Matrix<double> MLower(Lower_9x9, 0.);
    Matrix<double> MNotLower(NotLower_9x9, 0.);

    // Replace
    C = Ones;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(A), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = Ones;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MNotLower, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(A));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABT_ACdup)
{
    // Build some matrices.
    Matrix<double> Ones(Ones_3x4, 0.);
    Matrix<double> mat(B_sparse_3x4, 0.);
    Matrix<double> C(3, 4);
    Matrix<double> B(1, 1);    B.setElement(0, 0, 1.0);
    Matrix<double> answer(mat);

    Matrix<double> MLower(Lower_3x4, 0.);
    Matrix<double> MNotLower(NotLower_3x4, 0.);

    // Replace
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, answer);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), answer, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), C, transpose(B), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, mat);
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), mat, answer);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), C, transpose(B));
    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABT_BCdup)
{
    // Build some matrices.
    Matrix<double> mat(A_sparse_3x3, 0.);
    Matrix<double> C(3,3);
    Matrix<double> A(1, 1);    A.setElement(0, 0, 1.0);
    Matrix<double> answer(mat);

    Matrix<double> MLower(Lower_3x3, 0.);
    Matrix<double> MNotLower(NotLower_3x3, 0.);

    // Replace
    C = mat;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MLower, transpose(mat));
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), answer, mat);
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(C), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = mat;
    kronecker(C, complement(MNotLower), Plus<double>(), Times<double>(), A, transpose(C));

    BOOST_CHECK_EQUAL(C, answer);
}

//****************************************************************************
BOOST_AUTO_TEST_CASE(test_kronecker_CompMask_Accum_ABT_MCdup)
{
    // Build some matrices.
    Matrix<double> C(9,12);
    Matrix<double> Ones(Ones_9x12, 0.);
    Matrix<double> A(A_sparse_3x3, 0.);
    Matrix<double> B(B_sparse_4x3, 0.);
    Matrix<double> answer(Answer_sparse_9x12, 0.);

    Matrix<double> MLower(Lower_9x12, 0.);
    Matrix<double> MNotLower(NotLower_9x12, 0.);

    // Replace
    C = MLower;
    eWiseMult(answer, NoMask(), NoAccumulate(), Times<double>(), MNotLower, answer);
    kronecker(C, complement(C), Plus<double>(), Times<double>(), A, transpose(B), REPLACE);

    BOOST_CHECK_EQUAL(C, answer);

    // Merge
    C = MLower;
    eWiseAdd(answer, NoMask(), NoAccumulate(), Plus<double>(), MLower, answer);
    kronecker(C, complement(C), Plus<double>(), Times<double>(), A, transpose(B));

    BOOST_CHECK_EQUAL(C, answer);
}

BOOST_AUTO_TEST_SUITE_END()


#include "ros/ros.h"
#include "nav_msgs/Odometry.h"
#include "geometry_msgs/Twist.h"
#include "sensor_msgs/LaserScan.h"
#include <cstdlib> // Needed for rand()
#include <ctime> // Needed to seed random number generator with a time value
#include <boost/thread/mutex.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <tf/transform_listener.h>
#include <math.h>

using namespace boost::posix_time;

class GridMapper {
public:
  // Construct a new occupancy grid mapper  object and hook up
  // this ROS node to the simulated robot's pose, velocity control,
  // and laser topics
  GridMapper(ros::NodeHandle& nh) :
      canvas(200,200,CV_8UC1) {
    // Initialize random time generator
    srand(time(NULL));

    // Advertise a new publisher for the current simulated robot's
    // velocity command topic (the second argument indicates that
    // if multiple command messages are in the queue to be sent,
    // only the last command will be sent)
    commandPub = nh.advertise<geometry_msgs::Twist>("/mobile_base/commands/velocity", 1);

    // Subscribe to the current simulated robot's laser scan topic and
    // tell ROS to call this->laserCallback() whenever a new message
    // is published on that topic
    laserSub = nh.subscribe("/scan", 1, \
      &GridMapper::laserCallback, this);

    // Subscribe to the current simulated robot' ground truth pose topic
    // and tell ROS to call this->poseCallback(...) whenever a new
    // message is published on that topic
    poseSub = nh.subscribe("/odom", 1, \
      &GridMapper::poseCallback, this);

    // Timer that allows use to save snapshot every 30 seconds.
    timer = nh.createTimer(ros::Duration(30.0),
        &GridMapper::timerCallback,this);

    // Create resizeable named window
  //  cv::namedWindow("Occupancy Grid Canvas", \
   //   CV_WINDOW_NORMAL | CV_WINDOW_KEEPRATIO | CV_GUI_EXPANDED);

  };

  void timerCallback(const ros::TimerEvent&)
  {
    saveSnapshot();
    ROS_INFO_STREAM("Snapshot of Occupancy Grid map saved!");

  }

  // Save a snapshot of the occupancy grid canvas
  // NOTE: image is saved to same folder where code was executed
  void saveSnapshot() {
    std::string filename = "grid_" + to_iso_string(second_clock::local_time()) + ".jpg";
    canvasMutex.lock();
    cv::imwrite(filename, canvas);
    canvasMutex.unlock();
  };

  // Update grayscale intensity on canvas pixel (x, y) (in robot coordinate frame)
  void plot(int x, int y, char value) {
    canvasMutex.lock();
    x+=canvas.rows/2;
    y+=canvas.cols/2;
    //added condition where you cannot plot over the robot's path
    if (x >= 0 && x < canvas.rows && y >= 0 && y < canvas.cols
        && canvas.at<char>(canvas.cols - y, x) != CELL_ROBOT) {
      canvas.at<char>(canvas.cols - y, x) = value;
    }
    canvasMutex.unlock();
  };

  // Update grayscale intensity on canvas pixel (x, y) (in image coordinate frame)
  void plotImg(int x, int y, char value) {
    canvasMutex.lock();
    if (x >= 0 && x < canvas.cols && y >= 0 && y < canvas.rows) {
      //canvas.at<char>(y, x) = value;
      canvas.at<char>(y, x) = value;
    }
    canvasMutex.unlock();
  };

  // Send a velocity command
  void move(double linearVelMPS, double angularVelRadPS) {
    geometry_msgs::Twist msg; // The default constructor will set all commands to 0
    msg.linear.x = linearVelMPS;
    msg.angular.z = angularVelRadPS;
    commandPub.publish(msg);
  };

  /**
   *Uses the Bresenham line algorithm in order to update the Occupancy Grid,
   *given coordinates of the two end points of a line.
   */
  void bresenhamLine(int x1, int y1, int x2, int y2) {
    int w = x2 - x1;
    int h = y2 - y1;
    int dx1 = 0, dy1 = 0, dx2 = 0, dy2 = 0;
    //boolean conditions to make sure algorithm works for all octants
    if (w<0)
      dx1=-1;
    else if (w>0)
      dx1 = 1;
    if (h<0)
      dy1 = -1;
    else if (h>0)
      dy1 = 1;
    if (w<0)
      dx2 = -1;
    else if (w>0)
      dx2 = 1;
    int longest = abs(w);
    int shortest = abs(h);
    if (!(longest>shortest))
    {
       longest = abs(h);
       shortest = abs(w);
       if (h<0) dy2 = -1;
       else if (h>0) dy2 = 1;
       dx2 = 0;
    }
    int numerator = longest / 2;
    int y = y1;

    for(int i = 0; i <= longest; i++)
    {
       plot(x1,y1,CELL_FREE);
       numerator+=shortest;
       if (!(numerator<longest))
       {
          numerator -= longest;
          x1 += dx1;
          y1 +=dy1;
       }
       else {
          x1 += dx2;
          y1 += dy2;
       }
    }
    plot(x2,y2, CELL_OCCUPIED);

  }

  // Process incoming laser scan message
  void laserCallback(const sensor_msgs::LaserScan::ConstPtr& msg) {
    // TODO: parse laser data and update occupancy grid canvas
    //       (use CELL_OCCUPIED, CELL_UNKNOWN, CELL_FREE, and CELL_ROBOT values)
    // (see http://www.ros.org/doc/api/sensor_msgs/html/msg/LaserScan.html)
    int minIndex = 0;
    int maxIndex = msg->ranges.size()-1;

    for (int i = minIndex; i <= maxIndex; i++)
    {
      if (!isnan(msg->ranges[i]))
      {
        double d = msg->ranges[i];
        double angle = msg->angle_min + i * msg->angle_increment;

        int x2,y2;
        //calculate position of end point of laser data with known pose of robot
        x2 = x + d * cos(heading + angle + NINETYDEGREES);
        y2 = y + d * sin(heading + angle + NINETYDEGREES);

        bresenhamLine(x,y,x2,y2);
      }
    }
    //plots the robot path
    plot(x,y,CELL_ROBOT);

  };

  // Process incoming ground truth robot pose message
  void poseCallback(const nav_msgs::Odometry::ConstPtr& msg) {
    double roll, pitch;
    x = msg->pose.pose.position.x;
    y = msg->pose.pose.position.y;
    heading=tf::getYaw(msg->pose.pose.orientation);
  };

  // Main FSM loop for ensuring that ROS messages are
  // processed in a timely manner, and also for sending
  // velocity controls to the simulated robot based on the FSM state
  void spin() {
    int key = 0;

    // Initialize all pixel values in canvas to CELL_UNKNOWN
    canvasMutex.lock();
    canvas = cv::Scalar(CELL_UNKNOWN);
    canvasMutex.unlock();

    while (ros::ok()) { // Keep spinning loop until user presses Ctrl+C

      // NOTE: DO NOT REMOVE CODE BELOW THIS LINE
      //cv::imshow("Occupancy Grid Canvas", canvas);
      ros::spinOnce(); // Need to call this function often to allow ROS to process incoming messages
      key = cv::waitKey(1000/SPIN_RATE_HZ); // Obtain keypress from user; wait at most N milliseconds
      if (key == 'x' || key == 'X') {
        break;
      } else if (key == ' ') {
        saveSnapshot();
      }
    }

    ros::shutdown(); // Ensure that this ROS node shuts down properly
  };

  // Tunable motion controller parameters
  const static double FORWARD_SPEED_MPS = 2.0;
  const static double ROTATE_SPEED_RADPS = M_PI/2;

  const static int SPIN_RATE_HZ = 30;

  const static char CELL_OCCUPIED = 0;
  const static char CELL_UNKNOWN = 86;
  const static char CELL_FREE = 172;
  const static char CELL_ROBOT = 255;
  const static double NINETYDEGREES = M_PI/2;
  const static double ANGULAR_SPEED = 1.0;
  const static double GRID_SIZE = 0.1;
  const static double angleNormalizer = .5;

protected:
  ros::Publisher commandPub; // Publisher to the current robot's velocity command topic
  ros::Subscriber laserSub; // Subscriber to the current robot's laser scan topic
  ros::Subscriber poseSub; // Subscriber to the current robot's ground truth pose topic

  ros::Timer timer; //allows us to schedule the snapshot function every 30 seconds

  double x; // in simulated Stage units, + = East/right
  double y; // in simulated Stage units, + = North/up
  double heading; // in radians, 0 = East (+x dir.), pi/2 = North (+y dir.)

  cv::Mat canvas; // Occupancy grid canvas
  boost::mutex canvasMutex; // Mutex for occupancy grid canvas object
};

int main(int argc, char **argv) {

  ros::init(argc, argv, "grid_mapper"); // Initiate ROS node
  ros::NodeHandle n; // Create default handle
  GridMapper robbie(n); // Create new grid mapper object
  robbie.spin(); // Execute FSM loop

  return EXIT_SUCCESS;
};


#include <ros/ros.h>
#include "std_msgs/UInt32.h"
#include "std_msgs/Int32.h"
#include "std_msgs/Float64.h"
#include <boost/function.hpp>
#include "tread_distance_publisher.h"
#include <cmath>

TreadDistance::TreadDistance(const int ticksPerRevolution, const int maxTicks, const double wheelRadius, const int prevTickCount) :
    distanceTraveled{ 0 }, prevTickCount{ prevTickCount }, ticksPerRevolution{ ticksPerRevolution }, maxTicks{ maxTicks }, wheelCircumference{ wheelRadius * 2 * M_PI} {}

void TreadDistance::updateFromNewCount(const int newCount) {
    auto ticksMoved = calcTickDiff(newCount);
    distanceTraveled = (wheelCircumference * ticksMoved) / ticksPerRevolution;
    prevTickCount = newCount;
}

int TreadDistance::calcTickDiff(const int newCount) {
    //TODO: handle rollover from going past maxTicks

    // otherwise its a simple difference
    return newCount - prevTickCount;
}

int main(int argc, char** argv) {
    ros::init(argc, argv, "tread_distance_publisher");
    ros::NodeHandle n; //NodeHandle is the main access point to communications with the ROS system.

    bool requiredParamsFound = true;
    double wheelRadius, ticksPerRevolution, maxTicks;
    double rate; //rate: how quickly to publish hz.
    requiredParamsFound = requiredParamsFound && ros::param::get("~wheelRadius", wheelRadius);
    requiredParamsFound = requiredParamsFound && ros::param::get("~ticksPerRevolution", ticksPerRevolution);
    requiredParamsFound = requiredParamsFound && ros::param::get("~maxTicks", maxTicks);
    //if (!requiredParamsFound) {//throw an error or something...}

    ros::Publisher leftTreadPublisher = n.advertise<std_msgs::Float64>("/left_tread_distance", 15);
    ros::Publisher rightTreadPublisher = n.advertise<std_msgs::Float64>("/right_tread_distance", 15);
    TreadDistance leftTread(ticksPerRevolution, maxTicks, wheelRadius), rightTread(ticksPerRevolution, maxTicks, wheelRadius);
    boost::function<void(const std_msgs::Int32&)> leftTreadCallback = [&leftTread, &leftTreadPublisher](const std_msgs::Int32& msg) {
        std_msgs::Float64 new_msg;
        leftTread.updateFromNewCount(msg.data);
        new_msg.data = leftTread.distanceTraveled;
        leftTreadPublisher.publish(new_msg);

    };
    boost::function<void(const std_msgs::Int32&)> rightTreadCallback = [&rightTread, &rightTreadPublisher](const std_msgs::Int32& msg) {
        std_msgs::Float64 new_msg;
        rightTread.updateFromNewCount(msg.data);
        new_msg.data = rightTread.distanceTraveled;
        rightTreadPublisher.publish(new_msg);

    };
    auto leftTreadCountSub = n.subscribe<std_msgs::Int32>("/left_tread_count", 10, leftTreadCallback);
    auto rightTreadCountSub = n.subscribe<std_msgs::Int32>("/right_tread_count", 10, rightTreadCallback);

    ros::param::param<double>("~rate", rate, 10.0);
    ros::Rate loop_rate(rate);
    while (ros::ok()) {
        ros::spinOnce();
        loop_rate.sleep();

    }
    return 0;
}


#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_generator.hpp>
#include <stan/math/opencl/matrix_cl.hpp>
#include <stan/math/opencl/copy.hpp>
#include <test/unit/util.hpp>
#include <Eigen/Dense>
#include <gtest/gtest.h>

TEST(KernelGenerator, row_index_errors_test) {
  EXPECT_THROW(stan::math::row_index().eval(), std::invalid_argument);
  EXPECT_NO_THROW(stan::math::row_index(2, 3).eval());
}

TEST(KernelGenerator, col_index_errors_test) {
  EXPECT_THROW(stan::math::col_index().eval(), std::invalid_argument);
  EXPECT_NO_THROW(stan::math::col_index(2, 3).eval());
}

TEST(KernelGenerator, row_index_test) {
  stan::math::matrix_cl<int> m_cl = stan::math::row_index(3, 5);

  Eigen::MatrixXi correct(3, 5);
  correct << 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2;
  Eigen::MatrixXi res1 = stan::math::from_matrix_cl(m_cl);
  EXPECT_MATRIX_EQ(res1, correct);

  m_cl = m_cl + stan::math::row_index();

  Eigen::MatrixXi res2 = stan::math::from_matrix_cl(m_cl);
  EXPECT_MATRIX_EQ(res2, correct * 2);
}

TEST(KernelGenerator, col_index_test) {
  stan::math::matrix_cl<int> m_cl = stan::math::col_index(3, 5);

  Eigen::MatrixXi correct(3, 5);
  correct << 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4;
  Eigen::MatrixXi res1 = stan::math::from_matrix_cl(m_cl);
  EXPECT_MATRIX_EQ(res1, correct);

  m_cl = m_cl + stan::math::col_index();

  Eigen::MatrixXi res2 = stan::math::from_matrix_cl(m_cl);
  EXPECT_MATRIX_EQ(res2, correct * 2);
}

#endif


/*
 * StateEnumerator.hpp
 *
 *  Created on: Aug 26, 2009
 *      Author: stewie
 */

#ifndef STATEENUMERATOR_HPP_
#define STATEENUMERATOR_HPP_

#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>

#include "../core/network_state.hpp"
#include "../util/state_util.hpp"

namespace bn {

namespace gen {

namespace detail {

class StateIterator : public boost::iterator_facade<StateIterator, const State,
		boost::forward_traversal_tag> {
private:
	friend class boost::iterator_core_access;

	/**
	 * Length of the state vector to produce.
	 */
	State::size_type n;
	/**
	 * End iterators have this flag set to @e true.
	 */
	bool overflow;
	/**
	 * Value to return upon dereferentiation.
	 */
	value_type res;

	reference dereference() const {
		return res;
	}

	bool equal(const StateIterator& other) const {
		return overflow == other.overflow && res == other.res;
	}

	void increment() {
		overflow = util::next(res);
	}

public:

	StateIterator() {
	}

	StateIterator(const State::size_type n, const bool overflow) :
		n(n), overflow(overflow), res(n) {
	}
};

}

/**
 * @ingroup generators
 *
 * This generator enumerates the whole set of boolean states of a given length
 * @e n in the natural order. This range therefore lazily represents all of the
 * possible \f$2^n\f$ states  starting from \f$\underbrace{00 \ldots 0}_n\f$.
 *
 * This class models the ForwardRange concept.
 *
 * @see RandomStateGen for another way to generate states.
 */
class StateEnumerator : public boost::iterator_range<detail::StateIterator> {
private:
	typedef boost::iterator_range<detail::StateIterator> base;

public:

	StateEnumerator() {
	}

	/**
	 * Initializes a generator that produces vectors of length @a n.
	 *
	 * @param n state vector length
	 */
	explicit StateEnumerator(const State::size_type n) :
		base(iterator(n, false), iterator(n, true)) {
	}
};

inline StateEnumerator enumerate_states(const State::size_type n) {
	return StateEnumerator(n);
}

}

}

#endif /* STATEENUMERATOR_HPP_ */