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

  lsjt_operator.cpp

  Mark A. Caprio, Patrick J. Fasano
  University of Notre Dame.

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

#include "lsjt_operator.h"

#include <cmath>
#include <cassert>
#include <cstdlib>
#include <iomanip>
#include <fstream>
#include <tuple>
#include <utility>
#include <vector>

#include "basis.h"
#include "jt_operator.h"
#include "lsjt_scheme.h"
#include "many_body.h"
#include "operator.h"
#include "mcutils/parsing.h"
#include "mcutils/io.h"
#include <Eigen/Core>

namespace basis {

  ////////////////////////////////////////////////////////////////
  // relative two-body operator
  ////////////////////////////////////////////////////////////////

  void ReadRelativeOperatorParametersLSJT(
      std::istream& is,
      basis::RelativeOperatorParametersLSJT& parameters,
      mcutils::IOMode io_mode
    )
  {

    std::string line;

    if (io_mode == mcutils::IOMode::kText)
    {
      // line 1: version -- but first gobble any comment lines
      while (std::getline(is,line), line[0]=='#') {};
      int version;
      std::stringstream(line) >> version;
      assert(version==1);

      // line 2: operator tensor properties
      std::getline(is,line);
      int symmetry_phase_mode_int;
      std::stringstream(line)
        >> parameters.J0 >> parameters.g0
        >> parameters.T0_min >> parameters.T0_max
        >> symmetry_phase_mode_int;
      assert (symmetry_phase_mode_int==0);
      parameters.symmetry_phase_mode = basis::SymmetryPhaseMode(symmetry_phase_mode_int);

      // line 3: relative basis truncation
      std::getline(is,line);
      std::stringstream(line) >> parameters.Nmax >> parameters.Jmax;
    }
    else // if (io_mode == mcutils::IOMode::kBinary)
    {
      // field 1: version
      mcutils::VerifyBinary<int32_t>(is, 1, "Invalid version", "version");

      // fields 2-6: operator tensor properties
      mcutils::ReadBinary<int32_t>(is, parameters.J0);
      mcutils::ReadBinary<int32_t>(is, parameters.g0);
      mcutils::ReadBinary<int32_t>(is, parameters.T0_min);
      mcutils::ReadBinary<int32_t>(is, parameters.T0_max);
      mcutils::VerifyBinary<int32_t>(is, 0, "Unsupported symmetry phase mode", "symmetry phase mode");
      parameters.symmetry_phase_mode = basis::SymmetryPhaseMode::kHermitian;

      // fields 7-8: relative basis truncation
      mcutils::ReadBinary<int32_t>(is, parameters.Nmax);
      mcutils::ReadBinary<int32_t>(is, parameters.Jmax);
    }
  }

  void WriteRelativeOperatorParametersLSJT(
      std::ostream& os,
      const basis::RelativeOperatorParametersLSJT& parameters,
      mcutils::IOMode io_mode
    )
  {
    int version = 1;
    if (io_mode == mcutils::IOMode::kText)
    {
      os
        << "# RELATIVE LSJT" << std::endl
        << "#   version" << std::endl
        << "#   J0 g0 T0_min T0_max symmetry_phase_mode  [P0=(-)^g0]" << std::endl
        << "#   Nmax Jmax" << std::endl
        << "#   T0   N' L' S' J' T'   N L S J T   JT-RME" << std::endl
        << " " << version << std::endl
        << " " << parameters.J0 << " " << parameters.g0
        << " " << parameters.T0_min << " " << parameters.T0_max
        << " " << int(parameters.symmetry_phase_mode) << std::endl
        << " " << parameters.Nmax << " " << parameters.Jmax << std::endl;
    }
    else  // if (io_mode == mcutils::IOMode::kBinary)
    {
      // field 1: version
      mcutils::WriteBinary<int32_t>(os, version);

      // fields 2-6: operator tensor properties
      mcutils::WriteBinary<int32_t>(os, parameters.J0);
      mcutils::WriteBinary<int32_t>(os, parameters.g0);
      mcutils::WriteBinary<int32_t>(os, parameters.T0_min);
      mcutils::WriteBinary<int32_t>(os, parameters.T0_max);
      assert(parameters.symmetry_phase_mode==basis::SymmetryPhaseMode::kHermitian);
      mcutils::WriteBinary<int32_t>(os, static_cast<int32_t>(parameters.symmetry_phase_mode));

      // fields 7-8: relative basis truncation
      mcutils::WriteBinary<int32_t>(os, parameters.Nmax);
      mcutils::WriteBinary<int32_t>(os, parameters.Jmax);
    }
  }

  void ReadRelativeOperatorComponentLSJT(
      std::istream& is,
      int T0,
      const RelativeSectorsLSJT& sectors,
      OperatorBlocks<double>& matrices,
      mcutils::IOMode io_mode
    )
  {

    // iterate over sectors
    for (std::size_t sector_index = 0; sector_index < sectors.size(); ++sector_index)
      {

        // extract sector
        const typename RelativeSectorsLSJT::SectorType& sector = sectors.GetSector(sector_index);
        const typename RelativeSectorsLSJT::SubspaceType& bra_subspace = sector.bra_subspace();
        const typename RelativeSectorsLSJT::SubspaceType& ket_subspace = sector.ket_subspace();

        // construct zero matrix for sector
        Eigen::MatrixXd sector_matrix = Eigen::MatrixXd::Zero(bra_subspace.size(),ket_subspace.size());

        std::vector<double> buffer;
        if (io_mode == mcutils::IOMode::kBinary)
          {
                  // calculate number of matrix elements in sector
            std::size_t sector_entries = 0;
            if (sector.IsDiagonal())
              // diagonal sector
              {
                std::size_t dimension = ket_subspace.size();
                sector_entries = dimension*(dimension+1)/2;
              }
            else  // if (sector.IsUpperTriangle())
              // upper triangle sector (but not diagonal)
              {
                std::size_t bra_dimension = bra_subspace.size();
                std::size_t ket_dimension = ket_subspace.size();
                sector_entries = bra_dimension*ket_dimension;
              }

            // read entire sector to temporary buffer
            buffer.resize(sector_entries, 0.);
            mcutils::ReadBinary<double>(is, buffer.data(), sector_entries);

          }

        std::size_t i = 0;
        // retrieve matrix elements
        for (std::size_t bra_index=0; bra_index<bra_subspace.size(); ++bra_index)
          for (std::size_t ket_index=0; ket_index<ket_subspace.size(); ++ket_index)
            {

              // diagonal sector: restrict to upper triangle
              if (sector.IsDiagonal())
                if (!(bra_index<=ket_index))
                  continue;

              double matrix_element;
              if (io_mode == mcutils::IOMode::kText)
              {
                // define states
                const basis::RelativeStateLSJT bra(bra_subspace,bra_index);
                const basis::RelativeStateLSJT ket(ket_subspace,ket_index);

                // read numbers from input line
                int
                  input_T0,
                  bra_N,
                  bra_L,
                  bra_S,
                  bra_J,
                  bra_T,
                  ket_N,
                  ket_L,
                  ket_S,
                  ket_J,
                  ket_T;
                is
                  >> input_T0
                  >> bra_N
                  >> bra_L
                  >> bra_S
                  >> bra_J
                  >> bra_T
                  >> ket_N
                  >> ket_L
                  >> ket_S
                  >> ket_J
                  >> ket_T
                  >> matrix_element;

                // validate labels
                bool expected_labels = true
                  && (input_T0==T0)
                  && (bra_N==bra.N())
                  && (bra_L==bra.L())
                  && (bra_S==bra.S())
                  && (bra_J==bra.J())
                  && (bra_T==bra.T())
                  && (ket_N==ket.N())
                  && (ket_L==ket.L())
                  && (ket_S==ket.S())
                  && (ket_J==ket.J())
                  && (ket_T==ket.T());
                assert(expected_labels);
              }
              else
              {
                matrix_element = buffer[i++];
              }

              // save matrix element
              sector_matrix(bra_index,ket_index) = matrix_element;
            }

        // store matrix for sector
        matrices.push_back(sector_matrix);

      }
  }

  void WriteRelativeOperatorComponentLSJT(
      std::ostream& os,
      int T0,
      const RelativeSectorsLSJT& sectors,
      const OperatorBlocks<double>& matrices,
      mcutils::IOMode io_mode
    )
  {

    // iterate over sectors
    for (std::size_t sector_index = 0; sector_index < sectors.size(); ++sector_index)
      {

        // extract sector
        const typename RelativeSectorsLSJT::SectorType& sector = sectors.GetSector(sector_index);
        const typename RelativeSectorsLSJT::SubspaceType& bra_subspace = sector.bra_subspace();
        const typename RelativeSectorsLSJT::SubspaceType& ket_subspace = sector.ket_subspace();

        // verify that sector is canonical
        //
        // This is a check that the caller's sector construction
        // followed the specification that only "upper triangle"
        // sectors are stored.
        assert(sector.bra_subspace_index()<=sector.ket_subspace_index());

        // temporary buffer for binary write
        std::vector<double> buffer;
        std::size_t sector_entries = 0;
        if (io_mode == mcutils::IOMode::kBinary)
          {
            // calculate number of matrix elements in sector
            if (sector.IsDiagonal())
              // diagonal sector
              {
                const std::size_t& dimension = ket_subspace.size();
                sector_entries = dimension*(dimension+1)/2;
              }
            else  // if (sector.IsUpperTriangle())
              // upper triangle sector (but not diagonal)
              {
                const std::size_t& bra_dimension = bra_subspace.size();
                const std::size_t& ket_dimension = ket_subspace.size();
                sector_entries = bra_dimension*ket_dimension;
              }

            // allocate buffer
            buffer.resize(sector_entries, 0.);
          }

        // iterate over matrix elements
        std::size_t i = 0;
        for (std::size_t bra_index=0; bra_index<bra_subspace.size(); ++bra_index)
          for (std::size_t ket_index=0; ket_index<ket_subspace.size(); ++ket_index)
            {

              // diagonal sector: restrict to upper triangle
              if (sector.IsDiagonal())
                if (!(bra_index<=ket_index))
                  continue;

              // define states
              const basis::RelativeStateLSJT bra(bra_subspace,bra_index);
              const basis::RelativeStateLSJT ket(ket_subspace,ket_index);

              // extract matrix element factor
              const double matrix_element = matrices[sector_index](bra_index,ket_index);

              if (io_mode == mcutils::IOMode::kText)
                {
                  // generate output line
                  const int width = 3;
                  const int precision = 14;  // less than 16 to provide some roundoff and avoid ugliness on doubles
                  os << std::setprecision(precision);
                  os
                    << " " << std::setw(width) << T0
                    << " " << "  "
                    << " " << std::setw(width) << bra.N()
                    << " " << std::setw(width) << bra.L()
                    << " " << std::setw(width) << bra.S()
                    << " " << std::setw(width) << bra.J()
                    << " " << std::setw(width) << bra.T()
                    << " " << "  "
                    << " " << std::setw(width) << ket.N()
                    << " " << std::setw(width) << ket.L()
                    << " " << std::setw(width) << ket.S()
                    << " " << std::setw(width) << ket.J()
                    << " " << std::setw(width) << ket.T()
                    << " " << "  "
                    << " " << std::showpoint << std::scientific << matrix_element
                    << std::endl;
                }
              else  // if (io_mode == mcutils::IOMode::kText)
                {
                  buffer[i++] = matrix_element;
                }
            }

        // write temporary buffer to file
        if (io_mode == mcutils::IOMode::kBinary)
          mcutils::WriteBinary<double>(os,buffer.data(),sector_entries);
      }
  }

  void ReadRelativeOperatorLSJT(
      const std::string& relative_filename,
      basis::RelativeSpaceLSJT& relative_space,
      basis::RelativeOperatorParametersLSJT& operator_parameters,
      std::array<basis::RelativeSectorsLSJT,3>& relative_component_sectors,
      std::array<basis::OperatorBlocks<double>,3>& relative_component_matrices,
      bool verbose
    )
  // FUTURE: change to line-based input with std::getline and parsing checks for easier debugging
  // FUTURE: check file status on open
  {

    // deduce I/O mode
    mcutils::IOMode io_mode = mcutils::DeducedIOMode(relative_filename);

    // open stream for reading
    if (verbose)
      {
        std::cout
          << "Reading relative operator file..." << std::endl
          << "  Filename: " << relative_filename << std::endl
          << "  Mode: " << ((io_mode == mcutils::IOMode::kText) ? "text" : "binary")
          << std::endl;
      }
    std::ios_base::openmode mode_argument = std::ios_base::in;
    if (io_mode==mcutils::IOMode::kBinary)
      mode_argument |= std::ios_base::binary;
    std::ifstream is(relative_filename.c_str(), mode_argument);

    // read header parameters
    basis::ReadRelativeOperatorParametersLSJT(is,operator_parameters,io_mode);
    if (verbose)
      {
        std::cout
          << "  Operator properties:"
          << " J0 " << operator_parameters.J0
          << " g0 " << operator_parameters.g0
          << " T0_min " << operator_parameters.T0_min
          << " T0_max " << operator_parameters.T0_max
          << " symmetry " << int(operator_parameters.symmetry_phase_mode)
          << std::endl
          << "  Truncation:"
          << " Nmax " << operator_parameters.Nmax
          << " Jmax " << operator_parameters.Jmax
          << std::endl;
      }

    // set up relative space
    relative_space = basis::RelativeSpaceLSJT(
        operator_parameters.Nmax,operator_parameters.Jmax
      );

    // populate sectors and matrices
    for (int T0=operator_parameters.T0_min; T0<=operator_parameters.T0_max; ++T0)
      // for each isospin component
      {
        // enumerate sectors
        relative_component_sectors[T0]
          = basis::RelativeSectorsLSJT(relative_space,operator_parameters.J0,T0,operator_parameters.g0);

        // read matrices
        basis::ReadRelativeOperatorComponentLSJT(
            is,
            T0,
            relative_component_sectors[T0],relative_component_matrices[T0],
            io_mode
          );
      }

    // write diagnostics
    if (verbose)
      {
        std::cout << "  Matrix elements:";
        for (int T0=operator_parameters.T0_min; T0<=operator_parameters.T0_max; ++T0)
          std::cout << " " << basis::UpperTriangularEntries(relative_component_sectors[T0]);
        std::cout << std::endl;
        std::cout << "  Allocated:";
        for (int T0=operator_parameters.T0_min; T0<=operator_parameters.T0_max; ++T0)
          std::cout << " " << basis::AllocatedEntries(relative_component_matrices[T0]);
        std::cout << std::endl;
      }
  }

  void WriteRelativeOperatorLSJT(
      const std::string& relative_filename,
      const basis::RelativeSpaceLSJT& relative_space,
      const basis::OperatorLabelsJT& operator_labels,
      const std::array<basis::RelativeSectorsLSJT,3>& relative_component_sectors,
      const std::array<basis::OperatorBlocks<double>,3>& relative_component_matrices,
      bool verbose
    )
  // FUTURE: check file status on open
  {

    // deduce I/O mode
    mcutils::IOMode io_mode = mcutils::DeducedIOMode(relative_filename);

    // open stream for writing
    if (verbose)
      {
        std::cout
          << "Writing relative operator file..." << std::endl
          << "  Filename: " << relative_filename << std::endl;
      }
    std::ios_base::openmode mode_argument = std::ios_base::out;
    if (io_mode==mcutils::IOMode::kBinary)
      mode_argument |= std::ios_base::binary;
    std::ofstream os(relative_filename.c_str(), mode_argument);

    // set up full operator file parameters
    int Nmax = relative_space.Nmax();
    int Jmax = relative_space.Jmax();
    RelativeOperatorParametersLSJT operator_parameters(operator_labels,Nmax,Jmax);
    if (verbose)
      {
        std::cout
          << "  Operator properties:"
          << " J0 " << operator_parameters.J0
          << " g0 " << operator_parameters.g0
          << " T0_min " << operator_parameters.T0_min
          << " T0_max " << operator_parameters.T0_max
          << " symmetry " << int(operator_parameters.symmetry_phase_mode)
          << std::endl
          << "  Truncation:"
          << " Nmax " << operator_parameters.Nmax
          << " Jmax " << operator_parameters.Jmax
          << std::endl;
      }

    // write header parameters
    basis::WriteRelativeOperatorParametersLSJT(os,operator_parameters,io_mode);

    // write matrices
    for (int T0=operator_parameters.T0_min; T0<=operator_parameters.T0_max; ++T0)
      {
        basis::WriteRelativeOperatorComponentLSJT(
            os,
            T0,
            relative_component_sectors[T0],relative_component_matrices[T0],
            io_mode
          );
      }

  }

  ////////////////////////////////////////////////////////////////
  // relative LSJT operator construction
  ////////////////////////////////////////////////////////////////

  void ConstructZeroOperatorRelativeLSJT(
      const basis::OperatorLabelsJT& operator_labels,
      const basis::RelativeSpaceLSJT& relative_space,
      std::array<basis::RelativeSectorsLSJT,3>& relative_component_sectors,
      std::array<basis::OperatorBlocks<double>,3>& relative_component_matrices
    )
  {

    for (int T0=operator_labels.T0_min; T0<=operator_labels.T0_max; ++T0)
      // for each isospin component
      {

        // enumerate sectors
        relative_component_sectors[T0]
          = basis::RelativeSectorsLSJT(relative_space,operator_labels.J0,T0,operator_labels.g0);

        // populate matrices
        relative_component_matrices[T0].resize(relative_component_sectors[T0].size());
        basis::SetOperatorToZero(relative_component_sectors[T0],relative_component_matrices[T0]);
      }
  }

  void ConstructIdentityOperatorRelativeLSJT(
      const basis::OperatorLabelsJT& operator_labels,
      const basis::RelativeSpaceLSJT& relative_space,
      std::array<basis::RelativeSectorsLSJT,3>& relative_component_sectors,
      std::array<basis::OperatorBlocks<double>,3>& relative_component_matrices
    )
  {

    // validate operator labels
    assert(operator_labels.J0==0);
    assert(operator_labels.g0==0);
    assert(operator_labels.symmetry_phase_mode==basis::SymmetryPhaseMode::kHermitian);

    for (int T0=operator_labels.T0_min; T0<=operator_labels.T0_max; ++T0)
      // for each isospin component
      {

        // enumerate sectors
        relative_component_sectors[T0]
          = basis::RelativeSectorsLSJT(relative_space,operator_labels.J0,T0,operator_labels.g0);

        // populate matrices
        relative_component_matrices[T0].resize(relative_component_sectors[T0].size());
        if (T0==0)
          // identity matrices in
          basis::SetOperatorToIdentity(relative_component_sectors[T0],relative_component_matrices[T0]);
        else
          basis::SetOperatorToZero(relative_component_sectors[T0],relative_component_matrices[T0]);
      }
  }

  ////////////////////////////////////////////////////////////////
  // relative-cm LSJT operator manipulation
  ////////////////////////////////////////////////////////////////

  inline
  void RecastLabelsRelativeCMLSJTToRelativeCMLSJTN(
      const RelativeCMSubspaceLSJTLabels& relative_cm_lsjt_subspace_labels,
      const RelativeCMStateLSJTLabels& relative_cm_lsjt_state_labels,
      RelativeCMSubspaceLSJTNLabels& relative_cm_lsjtn_subspace_labels,
      RelativeCMStateLSJTNLabels& relative_cm_lsjtn_state_labels
    )
  // Recast labels for (subspace,state) from RelativeCMLSJT scheme to
  // RelativeCMLSJTN scheme.
  //
  // This direction of conversion (from target labels to source
  // labels) is as needed for looking up the source matrix element.
  //
  // This switches N from being the most-significant state label (that
  // is, least-rapidly-varying in the lexicographic ordering scheme),
  // to being the least-significant subspace label (that is,
  // most-rapidly-varying):
  //
  //   (L,S,J,T,g) : ([N],N1,l1,N2,l2) -> (L,S,J,T,g,N) : (N1,l1,N2,l2)
  //
  // As a state label, N is actually an implicit label, not stored,
  // but effectively the first label for ordering purposes.  Its value
  // may be recovered as N1+N2.
  //
  // Arguments:
  //   relative_cm_lsjt_subspace_labels (...) : source subspace labels
  //   relative_cm_lsjt_state_labels (...) : source state labels
  //   relative_cm_lsjtn_subspace_labels (...,output) : target subspace labels
  //   relative_cm_lsjtn_state_labels (...,output) : target state labels
  {
    // extract labels
    int L, S, J, T, g;
    std::tie(L,S,J,T,g) = relative_cm_lsjt_subspace_labels;
    int Nr, lr, Nc, lc;
    std::tie(Nr,lr,Nc,lc) = relative_cm_lsjt_state_labels;

    // repackage labels
    int N = Nr+Nc;
    relative_cm_lsjtn_subspace_labels = RelativeCMSubspaceLSJTNLabels(L,S,J,T,g,N);
    relative_cm_lsjtn_state_labels = relative_cm_lsjt_state_labels;
  }

  inline
  void RecastLabelsRelativeCMLSJTNToRelativeCMLSJT(
      const RelativeCMSubspaceLSJTNLabels& relative_cm_lsjtn_subspace_labels,
      const RelativeCMStateLSJTNLabels& relative_cm_lsjtn_state_labels,
      RelativeCMSubspaceLSJTLabels& relative_cm_lsjt_subspace_labels,
      RelativeCMStateLSJTLabels& relative_cm_lsjt_state_labels
    )
  // Recast labels for (subspace,state) from RelativeCMLSJTN scheme to
  // RelativeCMLSJT scheme.
  //
  // This direction of conversion (from target labels to source
  // labels) is as needed for looking up the source matrix element.
  //
  // This switches N from being the least-significant state label (that
  // is, most-rapidly-varying in the lexicographic ordering scheme),
  // to being the most-significant subspace label (that is,
  // least-rapidly-varying):
  //
  //   (L,S,J,T,g,N) : (N1,l1,N2,l2) -> (L,S,J,T,g) : ([N],N1,l1,N2,l2)
  //
  // As a state label, N is actually an implicit label, not stored,
  // but effectively the last label for ordering purposes.  Its value
  // may be recovered as Nr+Nc.
  //
  // Arguments:
  //   relative_cm_lsjtn_subspace_labels (...,output) : source subspace labels
  //   relative_cm_lsjtn_state_labels (...,output) : source state labels
  //   relative_cm_lsjt_subspace_labels (...) : target subspace labels
  //   relative_cm_lsjt_state_labels (...) : target state labels
  {
    // extract labels
    int L, S, J, T, g, N;
    std::tie(L,S,J,T,g,N) = relative_cm_lsjtn_subspace_labels;
    int Nr, lr, Nc, lc;
    std::tie(Nr,lr,Nc,lc) = relative_cm_lsjtn_state_labels;

    // repackage labels
    relative_cm_lsjt_subspace_labels = RelativeCMSubspaceLSJTLabels(L,S,J,T,g);
    relative_cm_lsjt_state_labels = relative_cm_lsjtn_state_labels;
  }

  void GatherOperatorRelativeCMLSJTNToRelativeCMLSJT(
      const basis::OperatorLabelsJT& operator_labels,
      const basis::RelativeCMSpaceLSJTN& relative_cm_lsjtn_space,
      const std::array<basis::RelativeCMSectorsLSJTN,3>& relative_cm_lsjtn_component_sectors,
      const std::array<basis::OperatorBlocks<double>,3>& relative_cm_lsjtn_component_matrices,
      const basis::RelativeCMSpaceLSJT& relative_cm_lsjt_space,
      std::array<basis::RelativeCMSectorsLSJT,3>& relative_cm_lsjt_component_sectors,
      std::array<basis::OperatorBlocks<double>,3>& relative_cm_lsjt_component_matrices
    )
  {
    for (int T0=operator_labels.T0_min; T0<=operator_labels.T0_max; ++T0)
      // for each isospin component
      {

        // enumerate target sectors
        relative_cm_lsjt_component_sectors[T0]
          = basis::RelativeCMSectorsLSJT(relative_cm_lsjt_space,operator_labels.J0,T0,operator_labels.g0);

        // populate matrices
        relative_cm_lsjt_component_matrices[T0].resize(relative_cm_lsjt_component_sectors[T0].size());
        for (std::size_t sector_index=0; sector_index<relative_cm_lsjt_component_sectors[T0].size(); ++sector_index)
          {
            // retrieve target sector
            const basis::RelativeCMSectorsLSJT::SectorType& relative_cm_lsjt_sector
              = relative_cm_lsjt_component_sectors[T0].GetSector(sector_index);

            // initialize matrix
            Eigen::MatrixXd& relative_cm_lsjt_matrix = relative_cm_lsjt_component_matrices[T0][sector_index];
            relative_cm_lsjt_matrix = Eigen::MatrixXd::Zero(
                relative_cm_lsjt_sector.bra_subspace().size(),
                relative_cm_lsjt_sector.ket_subspace().size()
              );

            // populate matrix elements
            for (std::size_t bra_index = 0; bra_index < relative_cm_lsjt_sector.bra_subspace().size(); ++bra_index)
              for (std::size_t ket_index = 0; ket_index < relative_cm_lsjt_sector.ket_subspace().size(); ++ket_index)
                // for each target matrix element
                {

                  // ensure canonical matrix element if diagonal sector
                  if (relative_cm_lsjt_sector.IsDiagonal())
                    if (!(bra_index<=ket_index))
                      continue;

                  // retrieve target states
                  basis::RelativeCMStateLSJT relative_cm_lsjt_bra(relative_cm_lsjt_sector.bra_subspace(),bra_index);
                  basis::RelativeCMStateLSJT relative_cm_lsjt_ket(relative_cm_lsjt_sector.ket_subspace(),ket_index);

                  // extract source bra labels
                  RelativeCMSubspaceLSJTLabels relative_cm_lsjt_subspace_labels_bra
                    = relative_cm_lsjt_bra.subspace().labels();
                  RelativeCMStateLSJTLabels relative_cm_lsjt_state_labels_bra
                    = relative_cm_lsjt_bra.labels();
                  RelativeCMSubspaceLSJTNLabels relative_cm_lsjtn_subspace_labels_bra;
                  RelativeCMStateLSJTNLabels relative_cm_lsjtn_state_labels_bra;
                  RecastLabelsRelativeCMLSJTToRelativeCMLSJTN(
                      relative_cm_lsjt_subspace_labels_bra,
                      relative_cm_lsjt_state_labels_bra,
                      relative_cm_lsjtn_subspace_labels_bra,
                      relative_cm_lsjtn_state_labels_bra
                    );

                  // extract source bra indices
                  std::size_t relative_cm_lsjtn_subspace_index_bra
                    = relative_cm_lsjtn_space.LookUpSubspaceIndex(
                        relative_cm_lsjtn_subspace_labels_bra
                      );
                  std::size_t relative_cm_lsjtn_state_index_bra
                    = relative_cm_lsjtn_space.GetSubspace(relative_cm_lsjtn_subspace_index_bra).LookUpStateIndex(
                        relative_cm_lsjtn_state_labels_bra
                      );

                  // extract source ket labels
                  RelativeCMSubspaceLSJTLabels relative_cm_lsjt_subspace_labels_ket
                    = relative_cm_lsjt_ket.subspace().labels();
                  RelativeCMStateLSJTLabels relative_cm_lsjt_state_labels_ket
                    = relative_cm_lsjt_ket.labels();
                  RelativeCMSubspaceLSJTNLabels relative_cm_lsjtn_subspace_labels_ket;
                  RelativeCMStateLSJTNLabels relative_cm_lsjtn_state_labels_ket;
                  RecastLabelsRelativeCMLSJTToRelativeCMLSJTN(
                      relative_cm_lsjt_subspace_labels_ket,
                      relative_cm_lsjt_state_labels_ket,
                      relative_cm_lsjtn_subspace_labels_ket,
                      relative_cm_lsjtn_state_labels_ket
                    );

                  // extract source ket indices
                  std::size_t relative_cm_lsjtn_subspace_index_ket
                    = relative_cm_lsjtn_space.LookUpSubspaceIndex(
                        relative_cm_lsjtn_subspace_labels_ket
                      );
                  std::size_t relative_cm_lsjtn_state_index_ket
                    = relative_cm_lsjtn_space.GetSubspace(relative_cm_lsjtn_subspace_index_ket).LookUpStateIndex(
                        relative_cm_lsjtn_state_labels_ket
                      );

                  // // debugging
                  // const RelativeCMSubspaceLSJTN& relative_cm_lsjtn_subspace_bra
                  //   = relative_cm_lsjtn_space.GetSubspace(relative_cm_lsjtn_subspace_index_bra);
                  // const RelativeCMSubspaceLSJTN& relative_cm_lsjtn_subspace_ket
                  //   = relative_cm_lsjtn_space.GetSubspace(relative_cm_lsjtn_subspace_index_ket);
                  // std::cout << " pre-lookup "
                  //           << " " << relative_cm_lsjtn_subspace_index_bra
                  //           << " " << relative_cm_lsjtn_subspace_index_ket
                  //           << " " << ";"
                  //           << " " << relative_cm_lsjtn_state_index_bra
                  //           << " " << relative_cm_lsjtn_state_index_ket
                  //           << " " << ";"
                  //           << " " << relative_cm_lsjtn_subspace_bra.LabelStr()
                  //           << " " << relative_cm_lsjtn_subspace_ket.LabelStr()
                  //           << " " << relative_cm_lsjtn_subspace_bra.size()
                  //           << " " << relative_cm_lsjtn_subspace_ket.size()
                  //           << std::endl;

                  // Note on canonicalization of indices for lookup
                  // (or lack thereof)
                  //
                  // Matrix elements which are canonical by LSJT
                  // sector, and ordered by N within a LSJT sector,
                  // should also be canonical by (LSJT;N) sector.
                  // That is, the upper triangle of a matrix with
                  // basis states ordered by
                  //
                  //   (L,S,J,T,g) : ([N],N1,l1,N2,l2)
                  //
                  // or
                  //
                  //   (L,S,J,T,g,N) : (N1,l1,N2,l2)
                  //
                  // should be identical.
                  //
                  // So canonicalization would only be necessary if we
                  // were to fill in a *lower* triangle matrix element
                  // of a diagonal target sector.  Then we would have
                  // to ensure that we look up a canonical (upper
                  // triangular) LSJTN sector.

                  // look up matrix element
                  std::size_t relative_cm_lsjtn_sector_index
                    = relative_cm_lsjtn_component_sectors[T0].LookUpSectorIndex(
                        relative_cm_lsjtn_subspace_index_bra,
                        relative_cm_lsjtn_subspace_index_ket
                      );

                  const Eigen::MatrixXd& relative_cm_lsjtn_matrix
                    = relative_cm_lsjtn_component_matrices[T0][relative_cm_lsjtn_sector_index];
                  double relative_cm_lsjtn_matrix_element = relative_cm_lsjtn_matrix(
                      relative_cm_lsjtn_state_index_bra,relative_cm_lsjtn_state_index_ket
                    );

                  relative_cm_lsjt_matrix(bra_index,ket_index) = relative_cm_lsjtn_matrix_element;

                }
          }
      }

  }

  void ScatterOperatorRelativeCMLSJTToRelativeCMLSJTN(
      const basis::OperatorLabelsJT& operator_labels,
      const basis::RelativeCMSpaceLSJT& relative_cm_lsjt_space,
      const std::array<basis::RelativeCMSectorsLSJT,3>& relative_cm_lsjt_component_sectors,
      const std::array<basis::OperatorBlocks<double>,3>& relative_cm_lsjt_component_matrices,
      const basis::RelativeCMSpaceLSJTN& relative_cm_lsjtn_space,
      std::array<basis::RelativeCMSectorsLSJTN,3>& relative_cm_lsjtn_component_sectors,
      std::array<basis::OperatorBlocks<double>,3>& relative_cm_lsjtn_component_matrices
    )
  {
    for (int T0=operator_labels.T0_min; T0<=operator_labels.T0_max; ++T0)
      // for each isospin component
      {

        // enumerate target sectors
        relative_cm_lsjtn_component_sectors[T0]
          = basis::RelativeCMSectorsLSJTN(relative_cm_lsjtn_space,operator_labels.J0,T0,operator_labels.g0);

        // populate matrices
        relative_cm_lsjtn_component_matrices[T0].resize(relative_cm_lsjtn_component_sectors[T0].size());
        for (std::size_t sector_index=0; sector_index<relative_cm_lsjtn_component_sectors[T0].size(); ++sector_index)
          {
            // retrieve target sector
            const basis::RelativeCMSectorsLSJTN::SectorType& relative_cm_lsjtn_sector
              = relative_cm_lsjtn_component_sectors[T0].GetSector(sector_index);

            // initialize matrix
            Eigen::MatrixXd& relative_cm_lsjtn_matrix = relative_cm_lsjtn_component_matrices[T0][sector_index];
            relative_cm_lsjtn_matrix = Eigen::MatrixXd::Zero(
                relative_cm_lsjtn_sector.bra_subspace().size(),
                relative_cm_lsjtn_sector.ket_subspace().size()
              );

            // populate matrix elements
            for (std::size_t bra_index = 0; bra_index < relative_cm_lsjtn_sector.bra_subspace().size(); ++bra_index)
              {
                // retreive target bra state
                basis::RelativeCMStateLSJTN relative_cm_lsjtn_bra(relative_cm_lsjtn_sector.bra_subspace(),bra_index);
                // extract source bra labels
                RelativeCMSubspaceLSJTNLabels relative_cm_lsjtn_subspace_labels_bra
                  = relative_cm_lsjtn_bra.subspace().labels();
                RelativeCMStateLSJTNLabels relative_cm_lsjtn_state_labels_bra
                  = relative_cm_lsjtn_bra.labels();
                RelativeCMSubspaceLSJTLabels relative_cm_lsjt_subspace_labels_bra;
                RelativeCMStateLSJTLabels relative_cm_lsjt_state_labels_bra;
                RecastLabelsRelativeCMLSJTNToRelativeCMLSJT(
                    relative_cm_lsjtn_subspace_labels_bra,
                    relative_cm_lsjtn_state_labels_bra,
                    relative_cm_lsjt_subspace_labels_bra,
                    relative_cm_lsjt_state_labels_bra
                  );

                // extract source bra indices
                std::size_t relative_cm_lsjt_subspace_index_bra
                  = relative_cm_lsjt_space.LookUpSubspaceIndex(
                      relative_cm_lsjt_subspace_labels_bra
                    );
                std::size_t relative_cm_lsjt_state_index_bra
                  = relative_cm_lsjt_space.GetSubspace(relative_cm_lsjt_subspace_index_bra).LookUpStateIndex(
                      relative_cm_lsjt_state_labels_bra
                    );

                for (std::size_t ket_index = 0; ket_index < relative_cm_lsjtn_sector.ket_subspace().size(); ++ket_index)
                  // for each target matrix element
                  {

                    // ensure canonical matrix element if diagonal sector
                    if (relative_cm_lsjtn_sector.IsDiagonal())
                      if (!(bra_index<=ket_index))
                        continue;

                    // retrieve target ket state
                    basis::RelativeCMStateLSJTN relative_cm_lsjtn_ket(relative_cm_lsjtn_sector.ket_subspace(),ket_index);

                    // extract source ket labels
                    RelativeCMSubspaceLSJTNLabels relative_cm_lsjtn_subspace_labels_ket
                      = relative_cm_lsjtn_ket.subspace().labels();
                    RelativeCMStateLSJTNLabels relative_cm_lsjtn_state_labels_ket
                      = relative_cm_lsjtn_ket.labels();
                    RelativeCMSubspaceLSJTLabels relative_cm_lsjt_subspace_labels_ket;
                    RelativeCMStateLSJTLabels relative_cm_lsjt_state_labels_ket;
                    RecastLabelsRelativeCMLSJTNToRelativeCMLSJT(
                        relative_cm_lsjtn_subspace_labels_ket,
                        relative_cm_lsjtn_state_labels_ket,
                        relative_cm_lsjt_subspace_labels_ket,
                        relative_cm_lsjt_state_labels_ket
                      );

                    // extract source ket indices
                    std::size_t relative_cm_lsjt_subspace_index_ket
                      = relative_cm_lsjt_space.LookUpSubspaceIndex(
                          relative_cm_lsjt_subspace_labels_ket
                        );
                    std::size_t relative_cm_lsjt_state_index_ket
                      = relative_cm_lsjt_space.GetSubspace(relative_cm_lsjt_subspace_index_ket).LookUpStateIndex(
                          relative_cm_lsjt_state_labels_ket
                        );

                    // // debugging
                    // const RelativeCMSubspaceLSJT& relative_cm_lsjt_subspace_bra
                    //   = relative_cm_lsjt_space.GetSubspace(relative_cm_lsjt_subspace_index_bra);
                    // const RelativeCMSubspaceLSJT& relative_cm_lsjt_subspace_ket
                    //   = relative_cm_lsjt_space.GetSubspace(relative_cm_lsjt_subspace_index_ket);
                    // std::cout << " pre-lookup "
                    //           << " " << relative_cm_lsjt_subspace_index_bra
                    //           << " " << relative_cm_lsjt_subspace_index_ket
                    //           << " " << ";"
                    //           << " " << relative_cm_lsjt_state_index_bra
                    //           << " " << relative_cm_lsjt_state_index_ket
                    //           << " " << ";"
                    //           << " " << relative_cm_lsjt_subspace_bra.LabelStr()
                    //           << " " << relative_cm_lsjt_subspace_ket.LabelStr()
                    //           << " " << relative_cm_lsjt_subspace_bra.size()
                    //           << " " << relative_cm_lsjt_subspace_ket.size()
                    //           << std::endl;

                    // Note on canonicalization of indices for lookup
                    // (or lack thereof)
                    //
                    // Matrix elements which are canonical by LSJT
                    // sector, and ordered by N within a LSJT sector,
                    // should also be canonical by (LSJT;N) sector.
                    // That is, the upper triangle of a matrix with
                    // basis states ordered by
                    //
                    //   (L,S,J,T,g) : ([N],N1,l1,N2,l2)
                    //
                    // or
                    //
                    //   (L,S,J,T,g,N) : (N1,l1,N2,l2)
                    //
                    // should be identical.
                    //
                    // So canonicalization would only be necessary if we
                    // were to fill in a *lower* triangle matrix element
                    // of a diagonal target sector.  Then we would have
                    // to ensure that we look up a canonical (upper
                    // triangular) LSJT sector.

                    // look up matrix element
                    std::size_t relative_cm_lsjt_sector_index
                      = relative_cm_lsjt_component_sectors[T0].LookUpSectorIndex(
                          relative_cm_lsjt_subspace_index_bra,
                          relative_cm_lsjt_subspace_index_ket
                        );

                    const Eigen::MatrixXd& relative_cm_lsjt_matrix
                      = relative_cm_lsjt_component_matrices[T0][relative_cm_lsjt_sector_index];
                    double relative_cm_lsjt_matrix_element = relative_cm_lsjt_matrix(
                        relative_cm_lsjt_state_index_bra,relative_cm_lsjt_state_index_ket
                      );

                    relative_cm_lsjtn_matrix(bra_index,ket_index) = relative_cm_lsjt_matrix_element;

                  }
              }
          }
      }

  }

  ////////////////////////////////////////////////////////////////
  // two-body LSJT operator manipulation
  ////////////////////////////////////////////////////////////////

  inline
  void RecastLabelsTwoBodyLSJTToTwoBodyLSJTN(
      const TwoBodySubspaceLSJTLabels& two_body_lsjt_subspace_labels,
      const TwoBodyStateLSJTLabels& two_body_lsjt_state_labels,
      TwoBodySubspaceLSJTNLabels& two_body_lsjtn_subspace_labels,
      TwoBodyStateLSJTNLabels& two_body_lsjtn_state_labels
    )
  // Recast labels for (subspace,state) from TwoBodyLSJT scheme to
  // TwoBodyLSJTN scheme.
  //
  // This direction of conversion (from target labels to source
  // labels) is as needed for looking up the source matrix element.
  //
  // This switches N from being the most-significant state label (that
  // is, least-rapidly-varying in the lexicographic ordering scheme),
  // to being the least-significant subspace label (that is,
  // most-rapidly-varying):
  //
  //   (L,S,J,T,g) : ([N],N1,l1,N2,l2) -> (L,S,J,T,g,N) : (N1,l1,N2,l2)
  //
  // As a state label, N is actually an implicit label, not stored,
  // but effectively the first label for ordering purposes.  Its value
  // may be recovered as N1+N2.
  //
  // Arguments:
  //   two_body_lsjt_subspace_labels (...) : source subspace labels
  //   two_body_lsjt_state_labels (...) : source state labels
  //   two_body_lsjtn_subspace_labels (...,output) : target subspace labels
  //   two_body_lsjtn_state_labels (...,output) : target state labels
  {
    // extract labels
    int L, S, J, T, g;
    std::tie(L,S,J,T,g) = two_body_lsjt_subspace_labels;
    int N1, l1, N2, l2;
    std::tie(N1,l1,N2,l2) = two_body_lsjt_state_labels;

    // repackage labels
    int N = N1+N2;
    two_body_lsjtn_subspace_labels = TwoBodySubspaceLSJTNLabels(L,S,J,T,g,N);
    two_body_lsjtn_state_labels = two_body_lsjt_state_labels;
  }

  void GatherOperatorTwoBodyLSJTNToTwoBodyLSJT(
      const basis::OperatorLabelsJT& operator_labels,
      const basis::TwoBodySpaceLSJTN& two_body_lsjtn_space,
      const std::array<basis::TwoBodySectorsLSJTN,3>& two_body_lsjtn_component_sectors,
      const std::array<basis::OperatorBlocks<double>,3>& two_body_lsjtn_component_matrices,
      const basis::TwoBodySpaceLSJT& two_body_lsjt_space,
      std::array<basis::TwoBodySectorsLSJT,3>& two_body_lsjt_component_sectors,
      std::array<basis::OperatorBlocks<double>,3>& two_body_lsjt_component_matrices
    )
  {
    for (int T0=operator_labels.T0_min; T0<=operator_labels.T0_max; ++T0)
      // for each isospin component
      {

        // enumerate target sectors
        two_body_lsjt_component_sectors[T0]
          = basis::TwoBodySectorsLSJT(two_body_lsjt_space,operator_labels.J0,T0,operator_labels.g0);

        // populate matrices
        two_body_lsjt_component_matrices[T0].resize(two_body_lsjt_component_sectors[T0].size());
        for (std::size_t sector_index=0; sector_index<two_body_lsjt_component_sectors[T0].size(); ++sector_index)
          {
            // retrieve target sector
            const basis::TwoBodySectorsLSJT::SectorType& two_body_lsjt_sector
              = two_body_lsjt_component_sectors[T0].GetSector(sector_index);

            // initialize matrix
            Eigen::MatrixXd& two_body_lsjt_matrix = two_body_lsjt_component_matrices[T0][sector_index];
            two_body_lsjt_matrix = Eigen::MatrixXd::Zero(
                two_body_lsjt_sector.bra_subspace().size(),
                two_body_lsjt_sector.ket_subspace().size()
              );

            // populate matrix elements
            for (std::size_t bra_index = 0; bra_index < two_body_lsjt_sector.bra_subspace().size(); ++bra_index)
              for (std::size_t ket_index = 0; ket_index < two_body_lsjt_sector.ket_subspace().size(); ++ket_index)
                // for each target matrix element
                {

                  // ensure canonical matrix element if diagonal sector
                  if (two_body_lsjt_sector.IsDiagonal())
                    if (!(bra_index<=ket_index))
                      continue;

                  // retrieve target states
                  basis::TwoBodyStateLSJT two_body_lsjt_bra(two_body_lsjt_sector.bra_subspace(),bra_index);
                  basis::TwoBodyStateLSJT two_body_lsjt_ket(two_body_lsjt_sector.ket_subspace(),ket_index);

                  // extract source bra labels
                  TwoBodySubspaceLSJTLabels two_body_lsjt_subspace_labels_bra
                    = two_body_lsjt_bra.subspace().labels();
                  TwoBodyStateLSJTLabels two_body_lsjt_state_labels_bra
                    = two_body_lsjt_bra.labels();
                  TwoBodySubspaceLSJTNLabels two_body_lsjtn_subspace_labels_bra;
                  TwoBodyStateLSJTNLabels two_body_lsjtn_state_labels_bra;
                  RecastLabelsTwoBodyLSJTToTwoBodyLSJTN(
                      two_body_lsjt_subspace_labels_bra,
                      two_body_lsjt_state_labels_bra,
                      two_body_lsjtn_subspace_labels_bra,
                      two_body_lsjtn_state_labels_bra
                    );

                  // extract source bra indices
                  std::size_t two_body_lsjtn_subspace_index_bra
                    = two_body_lsjtn_space.LookUpSubspaceIndex(
                        two_body_lsjtn_subspace_labels_bra
                      );
                  std::size_t two_body_lsjtn_state_index_bra
                    = two_body_lsjtn_space.GetSubspace(two_body_lsjtn_subspace_index_bra).LookUpStateIndex(
                        two_body_lsjtn_state_labels_bra
                      );

                  // extract source ket labels
                  TwoBodySubspaceLSJTLabels two_body_lsjt_subspace_labels_ket
                    = two_body_lsjt_ket.subspace().labels();
                  TwoBodyStateLSJTLabels two_body_lsjt_state_labels_ket
                    = two_body_lsjt_ket.labels();
                  TwoBodySubspaceLSJTNLabels two_body_lsjtn_subspace_labels_ket;
                  TwoBodyStateLSJTNLabels two_body_lsjtn_state_labels_ket;
                  RecastLabelsTwoBodyLSJTToTwoBodyLSJTN(
                      two_body_lsjt_subspace_labels_ket,
                      two_body_lsjt_state_labels_ket,
                      two_body_lsjtn_subspace_labels_ket,
                      two_body_lsjtn_state_labels_ket
                    );

                  // extract source ket indices
                  std::size_t two_body_lsjtn_subspace_index_ket
                    = two_body_lsjtn_space.LookUpSubspaceIndex(
                        two_body_lsjtn_subspace_labels_ket
                      );
                  std::size_t two_body_lsjtn_state_index_ket
                    = two_body_lsjtn_space.GetSubspace(two_body_lsjtn_subspace_index_ket).LookUpStateIndex(
                        two_body_lsjtn_state_labels_ket
                      );

                  // Note on canonicalization of indices for lookup
                  // (or lack thereof)
                  //
                  // Matrix elements which are canonical by LSJT
                  // sector, and ordered by N within a LSJT sector,
                  // should also be canonical by (LSJT;N) sector.
                  // That is, the upper triangle of a matrix with
                  // basis states ordered by
                  //
                  //   (L,S,J,T,g) : ([N],N1,l1,N2,l2)
                  //
                  // or
                  //
                  //   (L,S,J,T,g,N) : (N1,l1,N2,l2)
                  //
                  // should be identical.
                  //
                  // So canonicalization would only be necessary if we
                  // were to fill in a *lower* triangle matrix element
                  // of a diagonal target sector.  Then we would have
                  // to ensure that we look up a canonical (upper
                  // triangular) LSJTN sector.

                  // look up matrix element
                  std::size_t two_body_lsjtn_sector_index
                    = two_body_lsjtn_component_sectors[T0].LookUpSectorIndex(
                        two_body_lsjtn_subspace_index_bra,
                        two_body_lsjtn_subspace_index_ket
                      );

                  const Eigen::MatrixXd& two_body_lsjtn_matrix
                    = two_body_lsjtn_component_matrices[T0][two_body_lsjtn_sector_index];
                  double two_body_lsjtn_matrix_element = two_body_lsjtn_matrix(
                      two_body_lsjtn_state_index_bra,two_body_lsjtn_state_index_ket
                    );

                  two_body_lsjt_matrix(bra_index,ket_index) = two_body_lsjtn_matrix_element;

                }
          }
      }

  }

  ////////////////////////////////////////////////////////////////
  // relative-cm LSJT operator I/O
  ////////////////////////////////////////////////////////////////

  void ReadRelativeCMOperatorParametersLSJT(
      std::istream& is,
      basis::RelativeCMOperatorParametersLSJT& parameters,
      mcutils::IOMode io_mode
    )
  {

    std::string line;

    if (io_mode == mcutils::IOMode::kText)
    {
      // line 1: version -- but first gobble any comment lines
      while (std::getline(is,line), line[0]=='#') {};
      int version;
      std::stringstream(line) >> version;
      assert(version==1);

      // line 2: operator tensor properties
      std::getline(is,line);
      int symmetry_phase_mode_int;
      std::stringstream(line)
        >> parameters.J0 >> parameters.g0
        >> parameters.T0_min >> parameters.T0_max
        >> symmetry_phase_mode_int;
      assert (symmetry_phase_mode_int==0);
      parameters.symmetry_phase_mode = basis::SymmetryPhaseMode(symmetry_phase_mode_int);

      // line 3: relative basis truncation
      std::getline(is,line);
      std::stringstream(line) >> parameters.Nmax;
    }
    else // if (io_mode == mcutils::IOMode::kBinary)
    {
      // field 1: version
      mcutils::VerifyBinary<int32_t>(is, 1, "Invalid version", "version");

      // fields 2-6: operator tensor properties
      int symmetry_phase_mode_int;
      mcutils::ReadBinary<int32_t>(is, parameters.J0);
      mcutils::ReadBinary<int32_t>(is, parameters.g0);
      mcutils::ReadBinary<int32_t>(is, parameters.T0_min);
      mcutils::ReadBinary<int32_t>(is, parameters.T0_max);
      mcutils::ReadBinary<int32_t>(is, symmetry_phase_mode_int);
      assert(symmetry_phase_mode_int==0);
      parameters.symmetry_phase_mode = basis::SymmetryPhaseMode(symmetry_phase_mode_int);

      // field 7: relative basis truncation
      mcutils::ReadBinary<int32_t>(is, parameters.Nmax);
    }
  }

  void WriteRelativeCMOperatorParametersLSJT(
      std::ostream& os,
      const basis::RelativeCMOperatorParametersLSJT& parameters,
      mcutils::IOMode io_mode
    )
  {
    int version = 1;
    if (io_mode == mcutils::IOMode::kText)
    {
      os
        << "# RELATIVE-CM LSJT" << std::endl
        << "#   version" << std::endl
        << "#   J0 g0 T0_min T0_max symmetry_phase_mode  [P0=(-)^g0]" << std::endl
        << "#   Nmax" << std::endl
        << "#   T0   Nr' lr' Nc' lc' L' S' J' T' g'   Nr lr Nc lc L S J T g   JT-RME" << std::endl
        << " " << version << std::endl
        << " " << parameters.J0 << " " << parameters.g0
        << " " << parameters.T0_min << " " << parameters.T0_max
        << " " << int(parameters.symmetry_phase_mode) << std::endl
        << " " << parameters.Nmax << std::endl;
    }
    else  // if (io_mode == mcutils::IOMode::kBinary)
    {
      // field 1: version
      mcutils::WriteBinary<int32_t>(os, version);

      // fields 2-6: operator tensor properties
      mcutils::WriteBinary<int32_t>(os, parameters.J0);
      mcutils::WriteBinary<int32_t>(os, parameters.g0);
      mcutils::WriteBinary<int32_t>(os, parameters.T0_min);
      mcutils::WriteBinary<int32_t>(os, parameters.T0_max);
      assert(parameters.symmetry_phase_mode==basis::SymmetryPhaseMode::kHermitian);
      mcutils::WriteBinary<int32_t>(os, static_cast<int32_t>(parameters.symmetry_phase_mode));

      // fields 7: relative basis truncation
      mcutils::WriteBinary<int32_t>(os, parameters.Nmax);
    }
  }

  void ReadRelativeCMOperatorComponentLSJT(
      std::istream& is,
      int T0,
      const basis::RelativeCMSectorsLSJT& sectors,
      basis::OperatorBlocks<double>& matrices,
      mcutils::IOMode io_mode
    )
  {
    std::string line;
    int line_count = 0;

    // iterate over sectors
    for (std::size_t sector_index = 0; sector_index < sectors.size(); ++sector_index)
      {
        // extract sector
        const auto& sector = sectors.GetSector(sector_index);
        const auto& bra_subspace = sector.bra_subspace();
        const auto& ket_subspace = sector.ket_subspace();

        // verify that sector is canonical
        //
        // This is a check that the caller's sector construction
        // followed the specification that only "upper triangle"
        // sectors are stored.
        assert(sector.bra_subspace_index()<=sector.ket_subspace_index());

        // construct zero matrix for sector
        basis::OperatorBlock<double> sector_matrix =
          basis::OperatorBlock<double>::Zero(bra_subspace.size(),ket_subspace.size());

        std::vector<double> buffer;
        if (io_mode == mcutils::IOMode::kBinary)
          {
                  // calculate number of matrix elements in sector
            std::size_t sector_entries = 0;
            if (sector.IsDiagonal())
              // diagonal sector
              {
                std::size_t dimension = ket_subspace.size();
                sector_entries = dimension*(dimension+1)/2;
              }
            else  // if (sector.IsUpperTriangle())
              // upper triangle sector (but not diagonal)
              {
                std::size_t bra_dimension = bra_subspace.size();
                std::size_t ket_dimension = ket_subspace.size();
                sector_entries = bra_dimension*ket_dimension;
              }

            // read entire sector to temporary buffer
            buffer.resize(sector_entries, 0.);
            mcutils::ReadBinary<double>(is, buffer.data(), sector_entries);

          }

        std::size_t i = 0;
        // iterate over matrix elements
        for (std::size_t bra_index=0; bra_index<bra_subspace.size(); ++bra_index)
          for (std::size_t ket_index=0; ket_index<ket_subspace.size(); ++ket_index)
            {

              // diagonal sector: restrict to upper triangle
              if (sector.IsDiagonal())
                if (!(bra_index<=ket_index))
                  continue;

              double matrix_element;
              if (io_mode == mcutils::IOMode::kText)
              {
                // define states
                const basis::RelativeCMStateLSJT bra(bra_subspace,bra_index);
                const basis::RelativeCMStateLSJT ket(ket_subspace,ket_index);

                // define input variables
                int input_T0;
                int bra_Nr, bra_lr, bra_Nc, bra_lc, bra_L, bra_S, bra_J, bra_T, bra_g;
                int ket_Nr, ket_lr, ket_Nc, ket_lc, ket_L, ket_S, ket_J, ket_T, ket_g;

                // read input line
                mcutils::GetLine(is, line, line_count);
                std::istringstream line_stream(line);
                line_stream
                  >> input_T0
                  >> bra_Nr >> bra_lr >> bra_Nc >> bra_lc
                  >> bra_L  >> bra_S  >> bra_J  >> bra_T  >> bra_g
                  >> ket_Nr >> ket_lr >> ket_Nc >> ket_lc
                  >> ket_L  >> ket_S  >> ket_J  >> ket_T  >> ket_g
                  >> matrix_element;
                mcutils::ParsingCheck(line_stream, line_count, line);

                // validate labels
                bool expected_labels = true
                  && (input_T0==T0)
                  && (bra_Nr==bra.Nr())
                  && (bra_lr==bra.lr())
                  && (bra_Nc==bra.Nc())
                  && (bra_lc==bra.lc())
                  && (bra_L==bra.L())
                  && (bra_S==bra.S())
                  && (bra_J==bra.J())
                  && (bra_T==bra.T())
                  && (ket_Nr==ket.Nr())
                  && (ket_lr==ket.lr())
                  && (ket_Nc==ket.Nc())
                  && (ket_lc==ket.lc())
                  && (ket_L==ket.L())
                  && (ket_S==ket.S())
                  && (ket_J==ket.J())
                  && (ket_T==ket.T());
                assert(expected_labels);
              }
              else
              {
                matrix_element = buffer[i++];
              }

              // save matrix element
              sector_matrix(bra_index,ket_index) = matrix_element;
            }

          // store matrix for sector
          matrices.push_back(std::move(sector_matrix));
      }
  }

  void WriteRelativeCMOperatorComponentLSJT(
      std::ostream& os,
      int T0,
      const basis::RelativeCMSectorsLSJT& sectors,
      const basis::OperatorBlocks<double>& matrices,
      mcutils::IOMode io_mode
    )
  {

    // iterate over sectors
    for (std::size_t sector_index = 0; sector_index < sectors.size(); ++sector_index)
      {

        // extract sector
        const typename RelativeCMSectorsLSJT::SectorType& sector = sectors.GetSector(sector_index);
        const typename RelativeCMSectorsLSJT::SubspaceType& bra_subspace = sector.bra_subspace();
        const typename RelativeCMSectorsLSJT::SubspaceType& ket_subspace = sector.ket_subspace();

        // verify that sector is canonical
        //
        // This is a check that the caller's sector construction
        // followed the specification that only "upper triangle"
        // sectors are stored.
        assert(sector.bra_subspace_index()<=sector.ket_subspace_index());

        // temporary buffer for binary write
        std::vector<double> buffer;
        std::size_t sector_entries = 0;
        if (io_mode == mcutils::IOMode::kBinary)
          {
            // calculate number of matrix elements in sector
            if (sector.IsDiagonal())
              // diagonal sector
              {
                const std::size_t& dimension = ket_subspace.size();
                sector_entries = dimension*(dimension+1)/2;
              }
            else  // if (sector.IsUpperTriangle())
              // upper triangle sector (but not diagonal)
              {
                const std::size_t& bra_dimension = bra_subspace.size();
                const std::size_t& ket_dimension = ket_subspace.size();
                sector_entries = bra_dimension*ket_dimension;
              }

            // allocate buffer
            buffer.resize(sector_entries, 0.);
          }

        // iterate over matrix elements
        std::size_t i = 0;
        for (std::size_t bra_index=0; bra_index<bra_subspace.size(); ++bra_index)
          for (std::size_t ket_index=0; ket_index<ket_subspace.size(); ++ket_index)
            {

              // diagonal sector: restrict to upper triangle
              if (sector.IsDiagonal())
                if (!(bra_index<=ket_index))
                  continue;

              // define states
              const basis::RelativeCMStateLSJT bra(bra_subspace,bra_index);
              const basis::RelativeCMStateLSJT ket(ket_subspace,ket_index);

              // extract matrix element
              const double matrix_element = matrices[sector_index](bra_index,ket_index);

              if (io_mode == mcutils::IOMode::kText)
                {
                  // generate output line
                  const int width = 3;
                  const int precision = 8;  // for approximately single precision output
                  os << std::setprecision(precision);
                  os
                    << " " << std::setw(width) << T0
                    << " " << "  "
                    << " " << std::setw(width) << bra.Nr()
                    << " " << std::setw(width) << bra.lr()
                    << " " << std::setw(width) << bra.Nc()
                    << " " << std::setw(width) << bra.lc()
                    << " " << std::setw(width) << bra.L()
                    << " " << std::setw(width) << bra.S()
                    << " " << std::setw(width) << bra.J()
                    << " " << std::setw(width) << bra.T()
                    << " " << std::setw(width) << bra.g()
                    << " " << "    "
                    << " " << std::setw(width) << ket.Nr()
                    << " " << std::setw(width) << ket.lr()
                    << " " << std::setw(width) << ket.Nc()
                    << " " << std::setw(width) << ket.lc()
                    << " " << std::setw(width) << ket.L()
                    << " " << std::setw(width) << ket.S()
                    << " " << std::setw(width) << ket.J()
                    << " " << std::setw(width) << ket.T()
                    << " " << std::setw(width) << ket.g()
                    << " " << "    "
                    << " " << std::showpoint << std::scientific << matrix_element
                    << std::endl;
                }
              else  // if (io_mode == mcutils::IOMode::kText)
                {
                  buffer[i++] = matrix_element;
                }

            }

        // write temporary buffer to file
        if (io_mode == mcutils::IOMode::kBinary)
          mcutils::WriteBinary<double>(os,buffer.data(),sector_entries);
      }
  }

  void ReadRelativeCMOperatorLSJT(
      const std::string& filename,
      basis::RelativeCMSpaceLSJT& space,
      basis::RelativeCMOperatorParametersLSJT& operator_parameters,
      std::array<basis::RelativeCMSectorsLSJT,3>& component_sectors,
      std::array<basis::OperatorBlocks<double>,3>& component_matrices,
      bool verbose
    )
  // FUTURE: change to line-based input with std::getline and parsing checks for easier debugging
  // FUTURE: check file status on open
  {

    // deduce I/O mode
    mcutils::IOMode io_mode = mcutils::DeducedIOMode(filename);

    // open stream for reading
    if (verbose)
      {
        std::cout
          << "Reading relative-cm operator file..." << std::endl
          << "  Filename: " << filename << std::endl
          << "  Mode: " << ((io_mode == mcutils::IOMode::kText) ? "text" : "binary")
          << std::endl;
      }
    std::ios_base::openmode mode_argument = std::ios_base::in;
    if (io_mode==mcutils::IOMode::kBinary)
      mode_argument |= std::ios_base::binary;
    std::ifstream is(filename.c_str(), mode_argument);

    // read header parameters
    basis::ReadRelativeCMOperatorParametersLSJT(is, operator_parameters, io_mode);
    if (verbose)
      {
        std::cout
          << "  Operator properties:"
          << " J0 " << operator_parameters.J0
          << " g0 " << operator_parameters.g0
          << " T0_min " << operator_parameters.T0_min
          << " T0_max " << operator_parameters.T0_max
          << " symmetry " << int(operator_parameters.symmetry_phase_mode)
          << std::endl
          << "  Truncation:"
          << " Nmax " << operator_parameters.Nmax
          << std::endl;
      }

    // set up relative space
    space = basis::RelativeCMSpaceLSJT(
        operator_parameters.Nmax
      );

    // populate sectors and matrices
    for (int T0=operator_parameters.T0_min; T0<=operator_parameters.T0_max; ++T0)
      // for each isospin component
      {
        // enumerate sectors
        component_sectors[T0]
          = basis::RelativeCMSectorsLSJT(space,operator_parameters.J0,T0,operator_parameters.g0);

        // read matrices
        basis::ReadRelativeCMOperatorComponentLSJT(
            is,
            T0,
            component_sectors[T0], component_matrices[T0],
            io_mode
          );
      }

    // write diagnostics
    if (verbose)
      {
        std::cout << "  Matrix elements:";
        for (int T0=operator_parameters.T0_min; T0<=operator_parameters.T0_max; ++T0)
          std::cout << " " << basis::UpperTriangularEntries(component_sectors[T0]);
        std::cout << std::endl;
        std::cout << "  Allocated:";
        for (int T0=operator_parameters.T0_min; T0<=operator_parameters.T0_max; ++T0)
          std::cout << " " << basis::AllocatedEntries(component_matrices[T0]);
        std::cout << std::endl;
      }
  }

  void WriteRelativeCMOperatorLSJT(
      const std::string& filename,
      const basis::RelativeCMSpaceLSJT& space,
      const basis::OperatorLabelsJT& operator_labels,
      const std::array<basis::RelativeCMSectorsLSJT,3>& component_sectors,
      const std::array<basis::OperatorBlocks<double>,3>& component_matrices,
      bool verbose
    )
  // FUTURE: check file status on open
  {

    // deduce I/O mode
    mcutils::IOMode io_mode = mcutils::DeducedIOMode(filename);

    // open stream for writing
    if (verbose)
      {
        std::cout
          << "Writing relative-cm operator file..." << std::endl
          << "  Filename: " << filename << std::endl
          << "  Mode: " << ((io_mode == mcutils::IOMode::kText) ? "text" : "binary")
          << std::endl;
      }
    std::ios_base::openmode mode_argument = std::ios_base::out;
    if (io_mode==mcutils::IOMode::kBinary)
      mode_argument |= std::ios_base::binary;
    std::ofstream os(filename.c_str(), mode_argument);

    // set up full operator file parameters
    int Nmax = space.Nmax();
    RelativeCMOperatorParametersLSJT operator_parameters(operator_labels,Nmax);
    if (verbose)
      {
        std::cout
          << "  Operator properties:"
          << " J0 " << operator_parameters.J0
          << " g0 " << operator_parameters.g0
          << " T0_min " << operator_parameters.T0_min
          << " T0_max " << operator_parameters.T0_max
          << " symmetry " << int(operator_parameters.symmetry_phase_mode)
          << std::endl
          << "  Truncation:"
          << " Nmax " << operator_parameters.Nmax
          << std::endl;
      }

    // write header parameters
    basis::WriteRelativeCMOperatorParametersLSJT(os,operator_parameters,io_mode);

    // write matrices
    for (int T0=operator_parameters.T0_min; T0<=operator_parameters.T0_max; ++T0)
      {
        basis::WriteRelativeCMOperatorComponentLSJT(
            os,
            T0,
            component_sectors[T0], component_matrices[T0],
            io_mode
          );
      }

  }

  ////////////////////////////////////////////////////////////////
  // relative LSJT operator construction
  ////////////////////////////////////////////////////////////////

  void ConstructZeroOperatorRelativeCMLSJT(
      const basis::OperatorLabelsJT& operator_labels,
      const basis::RelativeCMSpaceLSJT& relative_cm_space,
      std::array<basis::RelativeCMSectorsLSJT,3>& relative_cm_component_sectors,
      std::array<basis::OperatorBlocks<double>,3>& relative_cm_component_matrices
    )
  {

    for (int T0=operator_labels.T0_min; T0<=operator_labels.T0_max; ++T0)
      // for each isospin component
      {

        // enumerate sectors
        relative_cm_component_sectors[T0]
          = basis::RelativeCMSectorsLSJT(relative_cm_space,operator_labels.J0,T0,operator_labels.g0);

        // populate matrices
        relative_cm_component_matrices[T0].resize(relative_cm_component_sectors[T0].size());
        basis::SetOperatorToZero(relative_cm_component_sectors[T0],relative_cm_component_matrices[T0]);
      }
  }

  void ConstructIdentityOperatorRelativeCMLSJT(
      const basis::OperatorLabelsJT& operator_labels,
      const basis::RelativeCMSpaceLSJT& relative_cm_space,
      std::array<basis::RelativeCMSectorsLSJT,3>& relative_cm_component_sectors,
      std::array<basis::OperatorBlocks<double>,3>& relative_cm_component_matrices
    )
  {

    // validate operator labels
    assert(operator_labels.J0==0);
    assert(operator_labels.g0==0);
    assert(operator_labels.symmetry_phase_mode==basis::SymmetryPhaseMode::kHermitian);

    for (int T0=operator_labels.T0_min; T0<=operator_labels.T0_max; ++T0)
      // for each isospin component
      {

        // enumerate sectors
        relative_cm_component_sectors[T0]
          = basis::RelativeCMSectorsLSJT(relative_cm_space,operator_labels.J0,T0,operator_labels.g0);

        // populate matrices
        relative_cm_component_matrices[T0].resize(relative_cm_component_sectors[T0].size());
        if (T0==0)
          // identity matrices in
          basis::SetOperatorToIdentity(relative_cm_component_sectors[T0],relative_cm_component_matrices[T0]);
        else
          basis::SetOperatorToZero(relative_cm_component_sectors[T0],relative_cm_component_matrices[T0]);
      }
  }

  ////////////////////////////////////////////////////////////////
  // two-body LSJT operator output
  ////////////////////////////////////////////////////////////////

  void WriteTwoBodyOperatorComponentLSJT(
      std::ostream& os,
      int T0,
      const TwoBodySectorsLSJT& sectors,
      const OperatorBlocks<double>& matrices,
      basis::NormalizationConversion conversion_mode
    )
  {

    // iterate over sectors
    for (std::size_t sector_index = 0; sector_index < sectors.size(); ++sector_index)
      {

        // extract sector
        const typename TwoBodySectorsLSJT::SectorType& sector = sectors.GetSector(sector_index);
        const typename TwoBodySectorsLSJT::SubspaceType& bra_subspace = sector.bra_subspace();
        const typename TwoBodySectorsLSJT::SubspaceType& ket_subspace = sector.ket_subspace();

        // verify that sector is canonical
        //
        // This is a check that the caller's sector construction
        // followed the specification that only "upper triangle"
        // sectors are stored.
        assert(sector.bra_subspace_index()<=sector.ket_subspace_index());

        // iterate over matrix elements
        for (std::size_t bra_index=0; bra_index<bra_subspace.size(); ++bra_index)
          for (std::size_t ket_index=0; ket_index<ket_subspace.size(); ++ket_index)
            {

              // diagonal sector: restrict to upper triangle
              if (sector.IsDiagonal())
                if (!(bra_index<=ket_index))
                  continue;

              // define states
              const basis::TwoBodyStateLSJT bra(bra_subspace,bra_index);
              const basis::TwoBodyStateLSJT ket(ket_subspace,ket_index);

              // determine matrix element normalization factor
              double conversion_factor = 1.;
              if (conversion_mode == basis::NormalizationConversion::kASToNAS)
                {
                  if ((bra.N1()==bra.N2())&&(bra.l1()==bra.l2()))
                    conversion_factor *= (1/sqrt(2.));
                  if ((ket.N1()==ket.N2())&&(ket.l1()==ket.l2()))
                    conversion_factor *= (1/sqrt(2.));
                }
              else if (conversion_mode == basis::NormalizationConversion::kNASToAS)
                {
                  if ((bra.N1()==bra.N2())&&(bra.l1()==bra.l2()))
                    conversion_factor *= sqrt(2.);
                  if ((ket.N1()==ket.N2())&&(ket.l1()==ket.l2()))
                    conversion_factor *= sqrt(2.);
                }

              // extract matrix element
              const double matrix_element = conversion_factor*matrices[sector_index](bra_index,ket_index);

              // generate output line
              const int width = 3;
              const int precision = 8;  // for approximately single precision output
              os << std::setprecision(precision);
              os
                << " " << std::setw(width) << T0
                << " " << "  "
                << " " << std::setw(width) << bra.N1()
                << " " << std::setw(width) << bra.l1()
                << " " << std::setw(width) << bra.N2()
                << " " << std::setw(width) << bra.l2()
                << " " << std::setw(width) << bra.L()
                << " " << std::setw(width) << bra.S()
                << " " << std::setw(width) << bra.J()
                << " " << std::setw(width) << bra.T()
                << " " << std::setw(width) << bra.g()
                << " " << "    "
                << " " << std::setw(width) << ket.N1()
                << " " << std::setw(width) << ket.l1()
                << " " << std::setw(width) << ket.N2()
                << " " << std::setw(width) << ket.l2()
                << " " << std::setw(width) << ket.L()
                << " " << std::setw(width) << ket.S()
                << " " << std::setw(width) << ket.J()
                << " " << std::setw(width) << ket.T()
                << " " << std::setw(width) << ket.g()
                << " " << "    "
                << " " << std::showpoint << std::scientific << matrix_element
                << std::endl;

            }

      };
  }

  ////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////
} // namespace


//==============================================================================
//         Copyright 2015 J.T. Lapreste
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#include <boost/simd/bitwise/include/functions/bitset.hpp>
#include <boost/simd/sdk/simd/io.hpp>
#include <boost/dispatch/meta/as_integer.hpp>
#include <nt2/sdk/unit/tests.hpp>
#include <nt2/sdk/unit/module.hpp>
#include <nt2/sdk/unit/tests/type_expr.hpp>

#include <boost/simd/include/constants/one.hpp>
#include <boost/simd/include/constants/mone.hpp>
#include <boost/simd/include/constants/zero.hpp>
#include <boost/simd/include/constants/valmax.hpp>
#include <boost/simd/include/constants/three.hpp>

NT2_TEST_CASE_TPL ( bitset_uit,  BOOST_SIMD_SIMD_UNSIGNED_TYPES)
{

  using boost::simd::bitset;
  using boost::simd::tag::bitset_;
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION                          ext_t;
  typedef native<T,ext_t>                                          vT;
  typedef typename boost::dispatch::meta::as_integer<vT, unsigned>::type viT;
  typedef typename boost::dispatch::meta::call<bitset_(T,viT)>::type r_t;
  typedef vT wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_EQUAL(bitset(boost::simd::splat<vT>(T(2)),1), boost::simd::splat<vT>(T(3)));
  NT2_TEST_EQUAL(bitset(boost::simd::splat<vT>(T(3)),2), boost::simd::splat<vT>(T(3)));
  NT2_TEST_EQUAL(bitset(boost::simd::splat<vT>(T(3)),3), boost::simd::splat<vT>(T(7)));
} // end of test for unsigned_int_

NT2_TEST_CASE_TPL ( bitset_si,  BOOST_SIMD_SIMD_INTEGRAL_SIGNED_TYPES)
{

  using boost::simd::bitset;
  using boost::simd::tag::bitset_;
  using boost::simd::native;
  typedef BOOST_SIMD_DEFAULT_EXTENSION                          ext_t;
  typedef native<T,ext_t>                                          vT;
  typedef typename boost::dispatch::meta::as_integer<vT, unsigned>::type viT;
  typedef typename boost::dispatch::meta::call<bitset_(T,viT)>::type r_t;
  typedef vT wished_r_t;

  // return type conformity test
  NT2_TEST_TYPE_IS( r_t, wished_r_t );

  // specific values tests
  NT2_TEST_EQUAL(bitset(boost::simd::splat<vT>(T(2)) ,1),boost::simd::splat<vT>(T(3)));
  NT2_TEST_EQUAL(bitset(boost::simd::splat<vT>(T(2)) ,2), boost::simd::splat<vT>(T(2)));
  NT2_TEST_EQUAL(bitset(boost::simd::splat<vT>(T(-1)), 1), boost::simd::splat<vT>(T(-1)));
} // end of test for unsigned_int_


#ifndef BFGS_HPP
#define BFGS_HPP

#include <functional>
#include <Eigen/Core>

namespace mathtoolbox
{
    namespace optimization
    {
        void RunBfgs(const Eigen::VectorXd& x_init,
                     const std::function<double(const Eigen::VectorXd&)>& f,
                     const std::function<Eigen::VectorXd(const Eigen::VectorXd&)>& g,
                     const double epsilon,
                     const unsigned int max_num_iterations,
                     Eigen::VectorXd& x_star,
                     unsigned int& num_iterations);
    }
}

#endif // BFGS_HPP


//
//  boost_compile_test.cpp
//
//
//  Created by Lennart Hilbert on 03.08.19.
//

#include <boost/lambda/lambda.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>

int main()
{
    using namespace boost::lambda;
    typedef std::istream_iterator<int> in;

    std::for_each(
                  in(std::cin), in(), std::cout << (_1 * 3) << " " );
}


/*
 [auto_generated]
 boost/numeric/odeint/stepper/rosenbrock4_controller.hpp

 [begin_description]
 Controller for the Rosenbrock4 method.
 [end_description]

 Copyright 2011-2012 Karsten Ahnert
 Copyright 2011-2012 Mario Mulansky
 Copyright 2012 Christoph Koke

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

#ifndef BOOST_NUMERIC_ODEINT_STEPPER_ROSENBROCK4_CONTROLLER_HPP_INCLUDED
#define BOOST_NUMERIC_ODEINT_STEPPER_ROSENBROCK4_CONTROLLER_HPP_INCLUDED

#include <boost/config.hpp>
#include <boost/numeric/odeint/util/bind.hpp>

#include <boost/numeric/odeint/stepper/controlled_step_result.hpp>
#include <boost/numeric/odeint/stepper/stepper_categories.hpp>

#include <boost/numeric/odeint/util/copy.hpp>
#include <boost/numeric/odeint/util/is_resizeable.hpp>

#include <boost/numeric/odeint/stepper/rosenbrock4.hpp>

namespace boost {
namespace numeric {
namespace odeint {

template< class Stepper >
class rosenbrock4_controller
{
private:

public:

    typedef Stepper stepper_type;
    typedef typename stepper_type::value_type value_type;
    typedef typename stepper_type::state_type state_type;
    typedef typename stepper_type::wrapped_state_type wrapped_state_type;
    typedef typename stepper_type::time_type time_type;
    typedef typename stepper_type::deriv_type deriv_type;
    typedef typename stepper_type::wrapped_deriv_type wrapped_deriv_type;
    typedef typename stepper_type::resizer_type resizer_type;
    typedef controlled_stepper_tag stepper_category;

    typedef rosenbrock4_controller< Stepper > controller_type;

    rosenbrock4_controller( value_type atol = 1.0e-6 , value_type rtol = 1.0e-6 , const stepper_type &stepper = stepper_type() )
    : m_stepper( stepper ) , m_atol( atol ) , m_rtol( rtol ) ,
      m_first_step( true ) , m_err_old( 0.0 ) , m_dt_old( 0.0 ) ,
      m_last_rejected( false )
    { }

    value_type error( const state_type &x , const state_type &xold , const state_type &xerr )
    {
        BOOST_USING_STD_MAX();
        using std::abs;

        const size_t n = x.size();
        value_type err = 0.0 , sk = 0.0;
        for( size_t i=0 ; i<n ; ++i )
        {
            sk = m_atol + m_rtol * max BOOST_PREVENT_MACRO_SUBSTITUTION ( abs( xold[i] ) , abs( x[i] ) );
            err += xerr[i] * xerr[i] / sk / sk;
        }
        return sqrt( err / value_type( n ) );
    }

    value_type last_error( void ) const
    {
        return m_err_old;
    }

    template< class System >
    boost::numeric::odeint::controlled_step_result
    try_step( System sys , state_type &x , time_type &t , time_type &dt )
    {
        m_xnew_resizer.adjust_size( x , detail::bind( &controller_type::template resize_m_xnew< state_type > , detail::ref( *this ) , detail::_1 ) );
        boost::numeric::odeint::controlled_step_result res = try_step( sys , x , t , m_xnew.m_v , dt );
        if( res == success )
        {
            boost::numeric::odeint::copy( m_xnew.m_v , x );
        }
        return res;
    }

    template< class System >
    boost::numeric::odeint::controlled_step_result
    try_step( System sys , const state_type &x , time_type &t , state_type &xout , time_type &dt )
    {
        BOOST_USING_STD_MIN();
        BOOST_USING_STD_MAX();
        using std::pow;

        static const value_type safe = 0.9 , fac1 = 5.0 , fac2 = 1.0 / 6.0;

        m_xerr_resizer.adjust_size( x , detail::bind( &controller_type::template resize_m_xerr< state_type > , detail::ref( *this ) , detail::_1 ) );

        m_stepper.do_step( sys , x , t , xout , dt , m_xerr.m_v );
        value_type err = error( xout , x , m_xerr.m_v );

        value_type fac = max BOOST_PREVENT_MACRO_SUBSTITUTION (
            fac2 , min BOOST_PREVENT_MACRO_SUBSTITUTION (
                fac1 ,
                static_cast< value_type >( pow( err , 0.25 ) / safe ) ) );
        value_type dt_new = dt / fac;
        if ( err <= 1.0 )
        {
            if( m_first_step )
            {
                m_first_step = false;
            }
            else
            {
                value_type fac_pred = ( m_dt_old / dt ) * pow( err * err / m_err_old , 0.25 ) / safe;
                fac_pred = max BOOST_PREVENT_MACRO_SUBSTITUTION (
                    fac2 , min BOOST_PREVENT_MACRO_SUBSTITUTION ( fac1 , fac_pred ) );
                fac = max BOOST_PREVENT_MACRO_SUBSTITUTION ( fac , fac_pred );
                dt_new = dt / fac;
            }

            m_dt_old = dt;
            m_err_old = max BOOST_PREVENT_MACRO_SUBSTITUTION ( static_cast< value_type >( 0.01 ) , err );
            if( m_last_rejected )
                dt_new = ( dt >= 0.0 ?
                min BOOST_PREVENT_MACRO_SUBSTITUTION ( dt_new , dt ) :
                max BOOST_PREVENT_MACRO_SUBSTITUTION ( dt_new , dt ) );
            t += dt;
            dt = dt_new;
            m_last_rejected = false;
            return success;
        }
        else
        {
            dt = dt_new;
            m_last_rejected = true;
            return fail;
        }
    }

    template< class StateType >
    void adjust_size( const StateType &x )
    {
        resize_m_xerr( x );
        resize_m_xnew( x );
    }

    stepper_type& stepper( void )
    {
        return m_stepper;
    }

    const stepper_type& stepper( void ) const
    {
        return m_stepper;
    }

private:

    template< class StateIn >
    bool resize_m_xerr( const StateIn &x )
    {
        return adjust_size_by_resizeability( m_xerr , x , typename is_resizeable<state_type>::type() );
    }

    template< class StateIn >
    bool resize_m_xnew( const StateIn &x )
    {
        return adjust_size_by_resizeability( m_xnew , x , typename is_resizeable<state_type>::type() );
    }

    stepper_type m_stepper;
    resizer_type m_xerr_resizer;
    resizer_type m_xnew_resizer;
    wrapped_state_type m_xerr;
    wrapped_state_type m_xnew;
    value_type m_atol , m_rtol;
    bool m_first_step;
    value_type m_err_old , m_dt_old;
    bool m_last_rejected;
};

} // namespace odeint
} // namespace numeric
} // namespace boost

#endif // BOOST_NUMERIC_ODEINT_STEPPER_ROSENBROCK4_CONTROLLER_HPP_INCLUDED


//          Copyright Oliver Kowalke 2014.
// 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)

#ifndef MEMORIA_CONTEXT_DETAIL_INDEX_SEQUENCE_H
#define MEMORIA_CONTEXT_DETAIL_INDEX_SEQUENCE_H

#include <cstddef>

#include <boost/config.hpp>

#include <memoria/context/detail/config.hpp>

namespace memoria {
namespace context {
namespace detail {

#if ! defined(MEMORIA_CONTEXT_NO_CXX14_INTEGER_SEQUENCE)
template< std::size_t ... I >
using index_sequence = std::index_sequence< I ... >;
template< std::size_t I >
using make_index_sequence = std::make_index_sequence< I >;
template< typename ... T >
using index_sequence_for = std::index_sequence_for< T ... >;
#else
//http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence

template< std::size_t ... I >
struct index_sequence {
    using type = index_sequence;
    using value_type = std::size_t;
    static constexpr std::size_t size() {
        return sizeof ... (I);
    }
};

template< typename Seq1, typename Seq2 >
struct concat_sequence;

template< std::size_t ... I1, std::size_t ... I2 >
struct concat_sequence< index_sequence< I1 ... >, index_sequence< I2 ... > > : public index_sequence< I1 ..., (sizeof ... (I1)+I2) ... > {
};

template< std::size_t I >
struct make_index_sequence : public concat_sequence< typename make_index_sequence< I/2 >::type,
                                                     typename make_index_sequence< I-I/2 >::type > {
};

template<>
struct make_index_sequence< 0 > : public index_sequence<> {
};
template<>
struct make_index_sequence< 1 > : public index_sequence< 0 > {
};

template< typename ... T >
using index_sequence_for = make_index_sequence< sizeof ... (T) >;
#endif

}}}

#endif // MEMORIA_CONTEXT_DETAIL_INDEX_SEQUENCE_H


/*
 [auto_generated]
 test/std_array.cpp

 [begin_description]
 Checks if odeint compiles fine with the std::array using the array algebra
 [end_description]

 Copyright 2009-2014 Karsten Ahnert
 Copyright 2009-2014 Mario Mulansky

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

#define BOOST_TEST_MODULE odeint_std_array

#include <array>
#include <boost/numeric/odeint.hpp>
#include <boost/static_assert.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/test/unit_test.hpp>

using namespace boost::unit_test;

typedef std::array<double, 3> state_type;

void rhs(const state_type &x, state_type &dxdt, const double t)
{
}

BOOST_AUTO_TEST_SUITE( unwrap_reference_test )

BOOST_AUTO_TEST_CASE( test_case )
{
    state_type x = {0.0, 0.0, 0.0};

    typedef boost::numeric::odeint::runge_kutta4<state_type> stepper_type;
// check if array algebra is selected, but only if odeint detects c++11
#ifdef BOOST_NUMERIC_ODEINT_CXX11
    BOOST_STATIC_ASSERT(( boost::is_same< stepper_type::algebra_type ,
                          boost::numeric::odeint::array_algebra >::value ));
#endif
    stepper_type stepper1;
    stepper1.do_step(rhs, x, 0.0, 0.1);

    boost::numeric::odeint::runge_kutta4<
        state_type, double, state_type, double,
        boost::numeric::odeint::array_algebra > stepper;
    stepper.do_step(rhs, x, 0.0, 0.1);

}

BOOST_AUTO_TEST_SUITE_END()


// Copyright Aleksey Gurtovoy 2001-2004
//
// 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/mpl for documentation.

// $Id$
// $Date$
// $Revision$

#include <boost/static_assert.hpp>
#include <boost/mpl/filter_view.hpp>
#include <boost/mpl/transform_view.hpp>
#include <boost/mpl/max_element.hpp>
#include <boost/mpl/list.hpp>
#include <boost/mpl/sizeof.hpp>
#include <boost/mpl/deref.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/aux_/test.hpp>

#include <boost/type_traits/is_float.hpp>
#include <boost/type_traits/is_same.hpp>

MPL_TEST_CASE()
{
    typedef mpl::list<int,float,long,float,char[50],long double,char> types;
    typedef mpl::list<float,float,long double> floats;

    MPL_ASSERT(( equal< mpl::filter_view< types,boost::is_float<_> >::type,floats > ));

    typedef mpl::max_element<
          mpl::transform_view<
              mpl::filter_view< types,boost::is_float<_> >
            , mpl::sizeof_<_>
            >
        >::type iter;

    MPL_ASSERT((is_same<mpl::deref<iter::base>::type, long double>));
}


#define ST_STATIC_EXPECT_CONFIG_STATIC_ASSERT 0

#include <static_test/static_expect.hpp>

#include <boost/mpl/lambda.hpp>
#include <utility>

namespace mpl = boost::mpl;
//using namespace mpl::placeholders;
using namespace st::static_matchers;

// http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html
template<class...> struct mp_list;

template <class T> using mp_add_pointer = T *;

template <class... T>
using mp_length = std::integral_constant<std::size_t, sizeof...(T)>;

//STATIC_EXPECT_THAT( (mp_length<int,double>), Eq<3>);

namespace detail {

template<class A, template<class...> class B> struct mp_rename;

template<template<class...> class A, class... T, template<class...> class B>
struct mp_rename<A<T...>, B>
{
    using type = B<T...>;
};
}

template<class A, template<class...> class B>
using mp_rename = typename detail::mp_rename<A, B>::type;

STATIC_EXPECT_THAT(
                   (mp_rename<std::pair<int, float>, std::tuple>),
                   (Is<std::tuple<int, float>>)
                   );

template<class L> struct mp_size_impl;

template<template<class...> class L,  class... T> struct mp_size_impl<L<T...>>
{
    using type = std::integral_constant<std::size_t, sizeof...(T)>;
};

template<class L> using mp_size = typename mp_size_impl<L>::type;

//  +----------+
//  |  Length  |
//  +----------+

template<class... T> using mp_length = std::integral_constant<std::size_t, sizeof...(T)>;

STATIC_EXPECT_THAT((mp_length<int, int, int>), Eq<3> );


// Copyright 2016. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Author: ryan.latture@gmail.com (Ryan Latture)

#include "fe_utils.h"
#include <Eigen/Dense>
#include <limits>
#include <memory>

namespace explicit_fea {

    void update_rotation(const std::vector<Node> &nodes, const BeamElement& elem, LocalMatrix &rotation, LocalMatrix &rotation_transposed) {
        rotation.setZero();
        rotation_transposed.setZero();
        const int nn1 = elem.get_node_numbers()[0];
        const int nn2 = elem.get_node_numbers()[1];

        // calculate unit normal vector along local x-direction
        Eigen::Vector3d nx = nodes[nn2] - nodes[nn1];
        nx.normalize();

        // calculate unit normal vector along y-direction
        const Eigen::Vector3d ny = elem.get_props().normal_vec.normalized();

        // calculate the unit normal vector in local z direction
        Eigen::Vector3d nz = nx.cross(ny);
        nz.normalize();

        // update rotation matrix
        rotation(0, 0) = nx(0);
        rotation(0, 1) = nx(1);
        rotation(0, 2) = nx(2);
        rotation(1, 0) = ny(0);
        rotation(1, 1) = ny(1);
        rotation(1, 2) = ny(2);
        rotation(2, 0) = nz(0);
        rotation(2, 1) = nz(1);
        rotation(2, 2) = nz(2);

        rotation(3, 3) = nx(0);
        rotation(3, 4) = nx(1);
        rotation(3, 5) = nx(2);
        rotation(4, 3) = ny(0);
        rotation(4, 4) = ny(1);
        rotation(4, 5) = ny(2);
        rotation(5, 3) = nz(0);
        rotation(5, 4) = nz(1);
        rotation(5, 5) = nz(2);

        rotation(6, 6) = nx(0);
        rotation(6, 7) = nx(1);
        rotation(6, 8) = nx(2);
        rotation(7, 6) = ny(0);
        rotation(7, 7) = ny(1);
        rotation(7, 8) = ny(2);
        rotation(8, 6) = nz(0);
        rotation(8, 7) = nz(1);
        rotation(8, 8) = nz(2);

        rotation(9, 9) = nx(0);
        rotation(9, 10) = nx(1);
        rotation(9, 11) = nx(2);
        rotation(10, 9) = ny(0);
        rotation(10, 10) = ny(1);
        rotation(10, 11) = ny(2);
        rotation(11, 9) = nz(0);
        rotation(11, 10) = nz(1);
        rotation(11, 11) = nz(2);

        // update transposed rotation matrix
        rotation_transposed(0, 0) = nx(0);
        rotation_transposed(0, 1) = ny(0);
        rotation_transposed(0, 2) = nz(0);
        rotation_transposed(1, 0) = nx(1);
        rotation_transposed(1, 1) = ny(1);
        rotation_transposed(1, 2) = nz(1);
        rotation_transposed(2, 0) = nx(2);
        rotation_transposed(2, 1) = ny(2);
        rotation_transposed(2, 2) = nz(2);

        rotation_transposed(3, 3) = nx(0);
        rotation_transposed(3, 4) = ny(0);
        rotation_transposed(3, 5) = nz(0);
        rotation_transposed(4, 3) = nx(1);
        rotation_transposed(4, 4) = ny(1);
        rotation_transposed(4, 5) = nz(1);
        rotation_transposed(5, 3) = nx(2);
        rotation_transposed(5, 4) = ny(2);
        rotation_transposed(5, 5) = nz(2);

        rotation_transposed(6, 6) = nx(0);
        rotation_transposed(6, 7) = ny(0);
        rotation_transposed(6, 8) = nz(0);
        rotation_transposed(7, 6) = nx(1);
        rotation_transposed(7, 7) = ny(1);
        rotation_transposed(7, 8) = nz(1);
        rotation_transposed(8, 6) = nx(2);
        rotation_transposed(8, 7) = ny(2);
        rotation_transposed(8, 8) = nz(2);

        rotation_transposed(9, 9) = nx(0);
        rotation_transposed(9, 10) = ny(0);
        rotation_transposed(9, 11) = nz(0);
        rotation_transposed(10, 9) = nx(1);
        rotation_transposed(10, 10) = ny(1);
        rotation_transposed(10, 11) = nz(1);
        rotation_transposed(11, 9) = nx(2);
        rotation_transposed(11, 10) = ny(2);
        rotation_transposed(11, 11) = nz(2);
    }

    double estimate_stable_timestep(const std::vector<Node>& nodes, const std::vector<std::unique_ptr<BeamElement>>& elems) {
        double length, timestep, wavespeed;
        double min_timestep = std::numeric_limits<double>::max();
        Eigen::Vector3d dn;
        int nn1, nn2;

        for (size_t i = 0; i < elems.size(); ++i) {
            nn1 = elems[i]->get_node_numbers()[0];
            nn2 = elems[i]->get_node_numbers()[1];

            dn = nodes[nn2] - nodes[nn1];
            length = dn.norm();
            wavespeed = sqrt(elems[i]->get_props().youngs_modulus / elems[i]->get_props().density);
            timestep = length / wavespeed;
            if (timestep < min_timestep) min_timestep = timestep;
        }
        return min_timestep / 10.0;
    }

} // namespace explicit_fea