#include <sstream>
#include <iostream>
#include <vector>
#include <cassert>
#include <cmath>

#include <boost/program_options.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/shared_ptr.hpp>

#include "stringlib.h"
#include "hg_sampler.h"
#include "sentence_metadata.h"
#include "ns.h"
#include "ns_docscorer.h"
#include "verbose.h"
#include "viterbi.h"
#include "hg.h"
#include "prob.h"
#include "kbest.h"
#include "ff_register.h"
#include "decoder.h"
#include "filelib.h"
#include "fdict.h"
#include "weights.h"
#include "sparse_vector.h"
#include "sampler.h"

using namespace std;
namespace po = boost::program_options;

bool invert_score;
boost::shared_ptr<MT19937> rng;

void RandomPermutation(int len, vector<int>* p_ids) {
  vector<int>& ids = *p_ids;
  ids.resize(len);
  for (int i = 0; i < len; ++i) ids[i] = i;
  for (int i = len; i > 0; --i) {
    int j = rng->next() * i;
    if (j == i) i--;
    swap(ids[i-1], ids[j]);
  }
}

bool InitCommandLine(int argc, char** argv, po::variables_map* conf) {
  po::options_description opts("Configuration options");
  opts.add_options()
        ("input_weights,w",po::value<string>(),"Input feature weights file")
        ("source,i",po::value<string>(),"Source file for development set")
        ("passes,p", po::value<int>()->default_value(15), "Number of passes through the training data")
        ("reference,r",po::value<vector<string> >(), "[REQD] Reference translation(s) (tokenized text file)")
        ("mt_metric,m",po::value<string>()->default_value("ibm_bleu"), "Scoring metric (ibm_bleu, nist_bleu, koehn_bleu, ter, combi)")
        ("max_step_size,C", po::value<double>()->default_value(0.01), "regularization strength (C)")
        ("mt_metric_scale,s", po::value<double>()->default_value(1.0), "Amount to scale MT loss function by")
        ("k_best_size,k", po::value<int>()->default_value(250), "Size of hypothesis list to search for oracles")
        ("sample_forest,f", "Instead of a k-best list, sample k hypotheses from the decoder's forest")
        ("sample_forest_unit_weight_vector,x", "Before sampling (must use -f option), rescale the weight vector used so it has unit length; this may improve the quality of the samples")
        ("random_seed,S", po::value<uint32_t>(), "Random seed (if not specified, /dev/random will be used)")
        ("decoder_config,c",po::value<string>(),"Decoder configuration file");
  po::options_description clo("Command line options");
  clo.add_options()
        ("config", po::value<string>(), "Configuration file")
        ("help,h", "Print this help message and exit");
  po::options_description dconfig_options, dcmdline_options;
  dconfig_options.add(opts);
  dcmdline_options.add(opts).add(clo);

  po::store(parse_command_line(argc, argv, dcmdline_options), *conf);
  if (conf->count("config")) {
    ifstream config((*conf)["config"].as<string>().c_str());
    po::store(po::parse_config_file(config, dconfig_options), *conf);
  }
  po::notify(*conf);

  if (conf->count("help") || !conf->count("input_weights") || !conf->count("source") || !conf->count("decoder_config") || !conf->count("reference")) {
    cerr << dcmdline_options << endl;
    return false;
  }
  return true;
}

static const double kMINUS_EPSILON = -1e-6;

struct HypothesisInfo {
  SparseVector<double> features;
  double mt_metric;
};

struct GoodBadOracle {
  boost::shared_ptr<HypothesisInfo> good;
  boost::shared_ptr<HypothesisInfo> bad;
};

struct TrainingObserver : public DecoderObserver {
  TrainingObserver(const int k, const DocumentScorer& d, const EvaluationMetric& m, bool sf, vector<GoodBadOracle>* o) : ds(d), metric(m), oracles(*o), kbest_size(k), sample_forest(sf) {}
  const DocumentScorer& ds;
  const EvaluationMetric& metric;
  vector<GoodBadOracle>& oracles;
  boost::shared_ptr<HypothesisInfo> cur_best;
  const int kbest_size;
  const bool sample_forest;

  const HypothesisInfo& GetCurrentBestHypothesis() const {
    return *cur_best;
  }

  virtual void NotifyTranslationForest(const SentenceMetadata& smeta, Hypergraph* hg) {
    UpdateOracles(smeta.GetSentenceID(), *hg);
  }

  boost::shared_ptr<HypothesisInfo> MakeHypothesisInfo(const SparseVector<double>& feats, const double score) {
    boost::shared_ptr<HypothesisInfo> h(new HypothesisInfo);
    h->features = feats;
    h->mt_metric = score;
    return h;
  }

  void UpdateOracles(int sent_id, const Hypergraph& forest) {
    boost::shared_ptr<HypothesisInfo>& cur_good = oracles[sent_id].good;
    boost::shared_ptr<HypothesisInfo>& cur_bad = oracles[sent_id].bad;
    cur_bad.reset();  // TODO get rid of??

    if (sample_forest) {
      vector<WordID> cur_prediction;
      ViterbiESentence(forest, &cur_prediction);
      SufficientStats sstats;
      ds[sent_id]->Evaluate(cur_prediction, &sstats);
      float sentscore = metric.ComputeScore(sstats);
      cur_best = MakeHypothesisInfo(ViterbiFeatures(forest), sentscore);

      vector<HypergraphSampler::Hypothesis> samples;
      HypergraphSampler::sample_hypotheses(forest, kbest_size, &*rng, &samples);
      for (unsigned i = 0; i < samples.size(); ++i) {
        ds[sent_id]->Evaluate(samples[i].words, &sstats);
        float sentscore = metric.ComputeScore(sstats);
        if (invert_score) sentscore *= -1.0;
        if (!cur_good || sentscore > cur_good->mt_metric)
          cur_good = MakeHypothesisInfo(samples[i].fmap, sentscore);
        if (!cur_bad || sentscore < cur_bad->mt_metric)
          cur_bad = MakeHypothesisInfo(samples[i].fmap, sentscore);
      }
    } else {
      KBest::KBestDerivations<vector<WordID>, ESentenceTraversal> kbest(forest, kbest_size);
      SufficientStats sstats;
      for (int i = 0; i < kbest_size; ++i) {
        const KBest::KBestDerivations<vector<WordID>, ESentenceTraversal>::Derivation* d =
          kbest.LazyKthBest(forest.nodes_.size() - 1, i);
        if (!d) break;
        ds[sent_id]->Evaluate(d->yield, &sstats);
        float sentscore = metric.ComputeScore(sstats);
        if (invert_score) sentscore *= -1.0;
        // cerr << TD::GetString(d->yield) << " ||| " << d->score << " ||| " << sentscore << endl;
        if (i == 0)
          cur_best = MakeHypothesisInfo(d->feature_values, sentscore);
        if (!cur_good || sentscore > cur_good->mt_metric)
          cur_good = MakeHypothesisInfo(d->feature_values, sentscore);
        if (!cur_bad || sentscore < cur_bad->mt_metric)
          cur_bad = MakeHypothesisInfo(d->feature_values, sentscore);
      }
      //cerr << "GOOD: " << cur_good->mt_metric << endl;
      //cerr << " CUR: " << cur_best->mt_metric << endl;
      //cerr << " BAD: " << cur_bad->mt_metric << endl;
    }
  }
};

void ReadTrainingCorpus(const string& fname, vector<string>* c) {
  ReadFile rf(fname);
  istream& in = *rf.stream();
  string line;
  while(in) {
    getline(in, line);
    if (!in) break;
    c->push_back(line);
  }
}

bool ApproxEqual(double a, double b) {
  if (a == b) return true;
  return (fabs(a-b)/fabs(b)) < 0.000001;
}

int main(int argc, char** argv) {
  register_feature_functions();
  SetSilent(true);  // turn off verbose decoder output

  po::variables_map conf;
  if (!InitCommandLine(argc, argv, &conf)) return 1;

  if (conf.count("random_seed"))
    rng.reset(new MT19937(conf["random_seed"].as<uint32_t>()));
  else
    rng.reset(new MT19937);
  const bool sample_forest = conf.count("sample_forest") > 0;
  const bool sample_forest_unit_weight_vector = conf.count("sample_forest_unit_weight_vector") > 0;
  if (sample_forest_unit_weight_vector && !sample_forest) {
    cerr << "Cannot --sample_forest_unit_weight_vector without --sample_forest" << endl;
    return 1;
  }
  vector<string> corpus;
  ReadTrainingCorpus(conf["source"].as<string>(), &corpus);

  string metric_name = UppercaseString(conf["mt_metric"].as<string>());
  if (metric_name == "COMBI") {
    cerr << "WARNING: 'combi' metric is no longer supported, switching to 'COMB:TER=-0.5;IBM_BLEU=0.5'\n";
    metric_name = "COMB:TER=-0.5;IBM_BLEU=0.5";
  } else if (metric_name == "BLEU") {
    cerr << "WARNING: 'BLEU' is ambiguous, assuming 'IBM_BLEU'\n";
    metric_name = "IBM_BLEU";
  }
  EvaluationMetric* metric = EvaluationMetric::Instance(metric_name);
  DocumentScorer ds(metric, conf["reference"].as<vector<string> >());
  cerr << "Loaded " << ds.size() << " references for scoring with " << metric_name << endl;
  invert_score = metric->IsErrorMetric();

  if (ds.size() != corpus.size()) {
    cerr << "Mismatched number of references (" << ds.size() << ") and sources (" << corpus.size() << ")\n";
    return 1;
  }

  ReadFile ini_rf(conf["decoder_config"].as<string>());
  Decoder decoder(ini_rf.stream());

  // load initial weights
  vector<weight_t>& dense_weights = decoder.CurrentWeightVector();
  SparseVector<weight_t> lambdas;
  Weights::InitFromFile(conf["input_weights"].as<string>(), &dense_weights);
  Weights::InitSparseVector(dense_weights, &lambdas);

  const double max_step_size = conf["max_step_size"].as<double>();
  const double mt_metric_scale = conf["mt_metric_scale"].as<double>();

  assert(corpus.size() > 0);
  vector<GoodBadOracle> oracles(corpus.size());

  TrainingObserver observer(conf["k_best_size"].as<int>(), ds, *metric, sample_forest, &oracles);
  int cur_sent = 0;
  int lcount = 0;
  int normalizer = 0;
  double tot_loss = 0;
  int dots = 0;
  int cur_pass = 0;
  SparseVector<double> tot;
  tot += lambdas;          // initial weights
  normalizer++;            // count for initial weights
  int max_iteration = conf["passes"].as<int>() * corpus.size();
  string msg = "# MIRA tuned weights";
  string msga = "# MIRA tuned weights AVERAGED";
  vector<int> order;
  RandomPermutation(corpus.size(), &order);
  while (lcount <= max_iteration) {
    lambdas.init_vector(&dense_weights);
    if ((cur_sent * 40 / corpus.size()) > dots) { ++dots; cerr << '.'; }
    if (corpus.size() == cur_sent) {
      cerr << " [AVG METRIC LAST PASS=" << (tot_loss / corpus.size()) << "]\n";
      Weights::ShowLargestFeatures(dense_weights);
      cur_sent = 0;
      tot_loss = 0;
      dots = 0;
      ostringstream os;
      os << "weights.mira-pass" << (cur_pass < 10 ? "0" : "") << cur_pass << ".gz";
      SparseVector<double> x = tot;
      x /= normalizer;
      ostringstream sa;
      sa << "weights.mira-pass" << (cur_pass < 10 ? "0" : "") << cur_pass << "-avg.gz";
      x.init_vector(&dense_weights);
      Weights::WriteToFile(os.str(), dense_weights, true, &msg);
      ++cur_pass;
      RandomPermutation(corpus.size(), &order);
    }
    if (cur_sent == 0) {
      cerr << "PASS " << (lcount / corpus.size() + 1) << endl;
    }
    decoder.SetId(order[cur_sent]);
    double sc = 1.0;
    if (sample_forest_unit_weight_vector) {
      sc = lambdas.l2norm();
      if (sc > 0) {
        for (unsigned i = 0; i < dense_weights.size(); ++i)
          dense_weights[i] /= sc;
      }
    }
    decoder.Decode(corpus[order[cur_sent]], &observer);  // update oracles
    if (sc && sc != 1.0) {
      for (unsigned i = 0; i < dense_weights.size(); ++i)
        dense_weights[i] *= sc;
    }
    const HypothesisInfo& cur_hyp = observer.GetCurrentBestHypothesis();
    const HypothesisInfo& cur_good = *oracles[order[cur_sent]].good;
    const HypothesisInfo& cur_bad = *oracles[order[cur_sent]].bad;
    tot_loss += cur_hyp.mt_metric;
    if (!ApproxEqual(cur_hyp.mt_metric, cur_good.mt_metric)) {
      const double loss = cur_bad.features.dot(dense_weights) - cur_good.features.dot(dense_weights) +
          mt_metric_scale * (cur_good.mt_metric - cur_bad.mt_metric);
      //cerr << "LOSS: " << loss << endl;
      if (loss > 0.0) {
        SparseVector<double> diff = cur_good.features;
        diff -= cur_bad.features;
        double step_size = loss / diff.l2norm_sq();
        //cerr << loss << " " << step_size << " " << diff << endl;
        if (step_size > max_step_size) step_size = max_step_size;
        lambdas += (cur_good.features * step_size);
        lambdas -= (cur_bad.features * step_size);
        //cerr << "L: " << lambdas << endl;
      }
    }
    tot += lambdas;
    ++normalizer;
    ++lcount;
    ++cur_sent;
  }
  cerr << endl;
  Weights::WriteToFile("weights.mira-final.gz", dense_weights, true, &msg);
  tot /= normalizer;
  tot.init_vector(dense_weights);
  msg = "# MIRA tuned weights (averaged vector)";
  Weights::WriteToFile("weights.mira-final-avg.gz", dense_weights, true, &msg);
  cerr << "Optimization complete.\nAVERAGED WEIGHTS: weights.mira-final-avg.gz\n";
  return 0;
}


//---------------------------------------------------------------------------//
//  MIT License
//
//  Copyright (c) 2020-2021 Mikhail Komarov <nemo@nil.foundation>
//  Copyright (c) 2020-2021 Nikita Kaskov <nemo@nil.foundation>

//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in all
//  copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//  SOFTWARE.
//---------------------------------------------------------------------------//

#ifndef FILECOIN_STORAGE_PROOFS_CORE_COMPONENTS_VARIABLES_HPP
#define FILECOIN_STORAGE_PROOFS_CORE_COMPONENTS_VARIABLES_HPP

#include <boost/variant.hpp>

#include <nil/crypto3/zk/snark/blueprint_variable.hpp>

namespace nil {
    namespace filecoin {
        /*!
         * @brief Root represents a root commitment which may be either a raw value or an already-allocated number.
         * This allows subcomponents to depend on roots which may optionally be shared with their parent
         * or sibling components.
         *
         * @tparam FieldType
         */
        template<typename FieldType>
        struct root {
            typedef FieldType field_type;

            typedef boost::variant<crypto3::zk::snark::blueprint_variable<field_type>, typename field_type::value_type>
                value_type;
        };
    }    // namespace filecoin
}    // namespace nil

#endif // FILECOIN_STORAGE_PROOFS_CORE_COMPONENTS_VARIABLES_HPP


// Boost.Geometry

// Copyright (c) 2020, Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

// Licensed under the Boost Software License version 1.0.
// http://www.boost.org/users/license.html

#ifndef BOOST_GEOMETRY_STRATEGIES_ENVELOPE_CARTESIAN_HPP
#define BOOST_GEOMETRY_STRATEGIES_ENVELOPE_CARTESIAN_HPP

#include <type_traits>

#include <boost/geometry/strategy/cartesian/envelope.hpp>
#include <boost/geometry/strategy/cartesian/envelope_box.hpp>
#include <boost/geometry/strategy/cartesian/envelope_point.hpp>
#include <boost/geometry/strategy/cartesian/envelope_multipoint.hpp>
#include <boost/geometry/strategy/cartesian/envelope_segment.hpp>

#include <boost/geometry/strategies/detail.hpp>
#include <boost/geometry/strategies/envelope/services.hpp>
#include <boost/geometry/strategies/expand/cartesian.hpp>

namespace boost { namespace geometry
{

namespace strategies { namespace envelope
{

template <typename CalculationType = void>
struct cartesian
    : strategies::expand::cartesian<CalculationType>
{
    template <typename Geometry, typename Box>
    static auto envelope(Geometry const&, Box const&,
                         typename util::enable_if_point_t<Geometry> * = nullptr)
    {
        return strategy::envelope::cartesian_point();
    }

    template <typename Geometry, typename Box>
    static auto envelope(Geometry const&, Box const&,
                         typename util::enable_if_multi_point_t<Geometry> * = nullptr)
    {
        return strategy::envelope::cartesian_multipoint();
    }

    template <typename Geometry, typename Box>
    static auto envelope(Geometry const&, Box const&,
                         typename util::enable_if_box_t<Geometry> * = nullptr)
    {
        return strategy::envelope::cartesian_box();
    }

    template <typename Geometry, typename Box>
    static auto envelope(Geometry const&, Box const&,
                         typename util::enable_if_segment_t<Geometry> * = nullptr)
    {
        return strategy::envelope::cartesian_segment<CalculationType>();
    }

    template <typename Geometry, typename Box>
    static auto envelope(Geometry const&, Box const&,
                         typename util::enable_if_polysegmental_t<Geometry> * = nullptr)
    {
        return strategy::envelope::cartesian<CalculationType>();
    }
};

namespace services
{

template <typename Geometry, typename Box>
struct default_strategy<Geometry, Box, cartesian_tag>
{
    using type = strategies::envelope::cartesian<>;
};

template <>
struct strategy_converter<strategy::envelope::cartesian_point>
{
    static auto get(strategy::envelope::cartesian_point const& )
    {
        return strategies::envelope::cartesian<>();
    }
};

template <>
struct strategy_converter<strategy::envelope::cartesian_multipoint>
{
    static auto get(strategy::envelope::cartesian_multipoint const&)
    {
        return strategies::envelope::cartesian<>();
    }
};

template <>
struct strategy_converter<strategy::envelope::cartesian_box>
{
    static auto get(strategy::envelope::cartesian_box const& )
    {
        return strategies::envelope::cartesian<>();
    }
};

template <typename CT>
struct strategy_converter<strategy::envelope::cartesian_segment<CT> >
{
    static auto get(strategy::envelope::cartesian_segment<CT> const&)
    {
        return strategies::envelope::cartesian<CT>();
    }
};

template <typename CT>
struct strategy_converter<strategy::envelope::cartesian<CT> >
{
    static auto get(strategy::envelope::cartesian<CT> const&)
    {
        return strategies::envelope::cartesian<CT>();
    }
};

} // namespace services

}} // namespace strategies::envelope

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_ENVELOPE_CARTESIAN_HPP


//=============================================================================================================
/**
* @file     eegref.cpp
* @author   Viktor Klüber <v.klueber@gmx.net>;
*           Lorenz Esch <Lorenz.Esch@tu-ilmenau.de>;
*           Matti Hamalainen <msh@nmr.mgh.harvard.edu>
* @version  1.0
* @date     January, 2017
*
* @section  LICENSE
*
* Copyright (C) 2017, Viktor Klüber, Lorenz Esch and Matti Hamalainen. 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.
*     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
*       to endorse or promote products derived from this software without specific prior written permission.
*
* 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.
*
*
* @brief    EEGRef class definition.
*
*/

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include "eegref.h"

//*************************************************************************************************************
//=============================================================================================================
// INCLUDES
//=============================================================================================================

#include <Eigen/Dense>

//*************************************************************************************************************
//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================

using namespace REFERENCEPLUGIN;
using namespace Eigen;

//*************************************************************************************************************
//=============================================================================================================
// DEFINE MEMBER METHODS
//=============================================================================================================

EEGRef::EEGRef()
{
}

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

MatrixXd EEGRef::applyCAR(MatrixXd &matIER, FIFFLIB::FiffInfo::SPtr &pFiffInfo)
{
    unsigned int numTrueCh  = 0;
    unsigned int numCh      = pFiffInfo->chs.size();
    MatrixXd matOnes        = MatrixXd::Ones(numCh, numCh);
    MatrixXd matCenter      = MatrixXd::Identity(numCh, numCh);

    //determine the number of true channels
    for(unsigned int i = 0; i < numCh; ++i)
    {
        if(pFiffInfo->chs.at(i).ch_name.contains("EEG") && !pFiffInfo->bads.contains(pFiffInfo->chs.at(i).ch_name))
        {
            numTrueCh++;
        }
        else
        {
            // excluding non-EEG channels from the centering matrix
            matOnes.row(i).setZero();
            matOnes.col(i).setZero();
            matCenter.row(i).setZero();
            matCenter.col(i).setZero();
        }
    }

    //detrmine centering matrix
    matCenter = matCenter - (1/double(numTrueCh))*matOnes;

    // determine EEG CAR data matrix
    MatrixXd matCAR = matCenter*matIER;

    //add former excluded non-EEG channels to the EEG CAR data matrix
    for(unsigned int i = 0; i < numCh; ++i)
    {
        if(!pFiffInfo->chs.at(i).ch_name.contains("EEG"))
        {
            matCAR.row(i) = matIER.row(i);
        }
    }

    return matCAR;
}


/**
 * @file fathometer_collection.cc
 * Container for one fathometer_collection instance.
 */

#include <usml/sensors/fathometer_collection.h>
#include <boost/foreach.hpp>

#include <netcdfcpp.h>

using namespace boost ;
using namespace usml::sensors ;

/**
 * Updates the fathometer data with the parameters provided.
 */
void fathometer_collection::dead_reckon(double delta_time,
                                    double slant_range, double prev_range) {
    // Set new slant_range
    _slant_range = slant_range;

    write_lock_guard guard(_eigenrays_mutex);

    eigenray_list::iterator iter;
    for (iter = _eigenrays.begin(); iter != _eigenrays.end(); ++iter) {

        iter->time = iter->time + delta_time;
        for (int i = 0; i < iter->frequencies->size(); ++i) {
        	iter->intensity[i] = iter->intensity[i] -
                (20*log10(prev_range)) + (20*log10(_slant_range));
        }
    }
}

/**
 * Write fathometer_collection data to to netCDF file.
 */
void fathometer_collection::write_netcdf( const char* filename, const char* long_name )
{

    NcFile* nc_file = new NcFile(filename, NcFile::Replace);
    if (long_name) {
        nc_file->add_att("long_name", long_name);
    }
    nc_file->add_att("Conventions", "COARDS");

    //read_lock_guard guard(_eigenrays_mutex);
    if ( _eigenrays.size() == 0 ) {
        nc_file->add_att("Eigenrays", "None Found");
        // close file
        delete nc_file;
        return;
    }
    // Get the list to get the frequency size
    long num_frequencies = ( long ) _eigenrays.front().frequencies->size();

    // dimensions

    NcDim *freq_dim = nc_file->add_dim("frequency", num_frequencies);
    NcVar *freq_var = nc_file->add_var("frequency", ncDouble, freq_dim);
    NcDim *eigenray_dim = nc_file->add_dim("eigenrays", ( long ) _eigenrays.size());

    // fathometer_collection attributes

    NcVar *source_id = nc_file->add_var("source_id", ncShort);
    NcVar *receiver_id = nc_file->add_var("receiver_id", ncShort);
    NcVar *initial_time = nc_file->add_var("initial_time", ncDouble);
    NcVar *slant_range = nc_file->add_var("slant_range", ncDouble);

    // coordinates

    NcVar *src_lat_var = nc_file->add_var("source_latitude", ncDouble);
    NcVar *src_lng_var = nc_file->add_var("source_longitude", ncDouble);
    NcVar *src_alt_var = nc_file->add_var("source_altitude", ncDouble);

    NcVar *rcv_lat_var = nc_file->add_var("receiver_latitude", ncDouble);
    NcVar *rcv_lng_var = nc_file->add_var("receiver_longitude", ncDouble);
    NcVar *rcv_alt_var = nc_file->add_var("receiver_altitude", ncDouble);

    NcVar *intensity_var = nc_file->add_var("intensity", ncDouble, eigenray_dim, freq_dim);
    NcVar *phase_var = nc_file->add_var("phase", ncDouble, eigenray_dim, freq_dim);
    NcVar *time_var = nc_file->add_var("travel_time", ncDouble, eigenray_dim);
    NcVar *source_de_var = nc_file->add_var("source_de", ncDouble, eigenray_dim);
    NcVar *source_az_var = nc_file->add_var("source_az", ncDouble, eigenray_dim);
    NcVar *target_de_var = nc_file->add_var("target_de", ncDouble, eigenray_dim);
    NcVar *target_az_var = nc_file->add_var("target_az", ncDouble, eigenray_dim);
    NcVar *surface_var = nc_file->add_var("surface", ncShort, eigenray_dim);
    NcVar *bottom_var = nc_file->add_var("bottom", ncShort, eigenray_dim);
    NcVar *caustic_var = nc_file->add_var("caustic", ncShort, eigenray_dim);
    NcVar *upper_var = nc_file->add_var("upper", ncShort, eigenray_dim);
    NcVar *lower_var = nc_file->add_var("lower", ncShort, eigenray_dim);

    // units

    freq_var->add_att("units", "Hertz");
    src_lat_var->add_att("units", "degrees_north");
    src_lng_var->add_att("units", "degrees_east");
    src_alt_var->add_att("units", "meters");
    src_alt_var->add_att("positive", "up");

    rcv_lat_var->add_att("units", "degrees_north");
    rcv_lng_var->add_att("units", "degrees_east");
    rcv_alt_var->add_att("units", "meters");
    rcv_alt_var->add_att("positive", "up");

    intensity_var->add_att("units", "dB");
    phase_var->add_att("units", "radians");
    time_var->add_att("units", "seconds");

    source_de_var->add_att("units", "degrees");
    source_de_var->add_att("positive", "up");
    source_az_var->add_att("units", "degrees_true");
    source_az_var->add_att("positive", "clockwise");

    target_de_var->add_att("units", "degrees");
    target_de_var->add_att("positive", "up");
    target_az_var->add_att("units", "degrees_true");
    target_az_var->add_att("positive", "clockwise");

    surface_var->add_att("units", "count");
    bottom_var->add_att("units", "count");
    caustic_var->add_att("units", "count");
    upper_var->add_att("units", "count");
    lower_var->add_att("units", "count");

    int item;
    double v;
    int record = 0; // current record number

    // write base attributes

    freq_var->put(_eigenrays.front().frequencies->data().begin(), num_frequencies);
    item = _source_id; source_id->put(&item, 1);
    item = _receiver_id; receiver_id->put(&item, 1);

    v = _initial_time;  initial_time->put(&v, 1);
    v = _slant_range;   slant_range->put(&v, 1);

    // write source parameters

    v = _source_position.latitude();    src_lat_var->put(&v, 1);
    v = _source_position.longitude();   src_lng_var->put(&v, 1);
    v = _source_position.altitude();    src_alt_var->put(&v, 1);

    // write receiver parameters

    v = _receiver_position.latitude();    rcv_lat_var->put(&v, 1);
    v = _receiver_position.longitude();   rcv_lng_var->put(&v, 1);
    v = _receiver_position.altitude();    rcv_alt_var->put(&v, 1);

    BOOST_FOREACH(eigenray ray, _eigenrays)
    {
        // set record number for each eigenray data element

        intensity_var->set_cur(record);
        phase_var->set_cur(record);
        time_var->set_cur(record);
        source_de_var->set_cur(record);
        source_az_var->set_cur(record);
        target_de_var->set_cur(record);
        target_az_var->set_cur(record);
        surface_var->set_cur(record);
        bottom_var->set_cur(record);
        caustic_var->set_cur(record);
        upper_var->set_cur(record);
        lower_var->set_cur(record);
        ++record;

        intensity_var->put(ray.intensity.data().begin(), 1, num_frequencies);
        phase_var->put(ray.phase.data().begin(), 1, num_frequencies);
        time_var->put(&( ray.time ), 1);
        source_de_var->put(&( ray.source_de ), 1);
        source_az_var->put(&( ray.source_az ), 1);
        target_de_var->put(&( ray.target_de ), 1);
        target_az_var->put(&( ray.target_az ), 1);
        surface_var->put(&( ray.surface ), 1);
        bottom_var->put(&( ray.bottom ), 1);
        caustic_var->put(&( ray.caustic ), 1);
        upper_var->put(&( ray.caustic ), 1);
        lower_var->put(&( ray.caustic ), 1);

    } // loop over # of eigenrays

    // close file
    delete nc_file; // destructor frees all netCDF temp variables
}


#ifndef STAN_MATH_PRIM_SCAL_PROB_UNIFORM_RNG_HPP
#define STAN_MATH_PRIM_SCAL_PROB_UNIFORM_RNG_HPP

#include <boost/random/uniform_real_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include <stan/math/prim/scal/err/check_consistent_sizes.hpp>
#include <stan/math/prim/scal/err/check_finite.hpp>
#include <stan/math/prim/scal/err/check_greater.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/fun/constants.hpp>
#include <stan/math/prim/scal/fun/value_of.hpp>
#include <stan/math/prim/scal/meta/VectorBuilder.hpp>
#include <stan/math/prim/scal/meta/include_summand.hpp>

namespace stan {
namespace math {

template <class RNG>
inline double uniform_rng(double alpha, double beta, RNG& rng) {
  using boost::random::uniform_real_distribution;
  using boost::variate_generator;

  static const char* function = "uniform_rng";

  check_finite(function, "Lower bound parameter", alpha);
  check_finite(function, "Upper bound parameter", beta);
  check_greater(function, "Upper bound parameter", beta, alpha);

  variate_generator<RNG&, uniform_real_distribution<> > uniform_rng(
      rng, uniform_real_distribution<>(alpha, beta));
  return uniform_rng();
}

}  // namespace math
}  // namespace stan
#endif


/*Copyright (c) 2020 James Gayvert

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
 * qchem_interface.cpp
 */
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <string>
#include "qchem_interface.h"
#include "opencap_exception.h"
#include "gto_ordering.h"
#include "utils.h"
#include "BasisSet.h"
#include <Eigen/Dense>

std::array<std::vector<std::vector<Eigen::MatrixXd>>,2> qchem_read_dms(std::string fchk_filename,BasisSet &bs)
{
	std::ifstream is(fchk_filename);
	size_t nstates = 0;
	bool sep_alpha_beta;
	std::string line, rest;
	while(is.peek() != EOF )
	{
    	std::getline(is, line);
    	if(line.find("Alpha  State Density")!= std::string::npos)
    	{
    		sep_alpha_beta=true;
			int num_elements = stoi(split(line,' ').back());
			if(sqrt(num_elements)!=bs.Nbasis)
				opencap_throw("Error: dimensions of DMs do not match specified basis set.");
    		break;
    	}
    	else if(line.find("State Density")!= std::string::npos)
    	{
    		sep_alpha_beta=false;
			int num_elements = stoi(split(line,' ').back());
			if(sqrt(num_elements)!=bs.Nbasis)
				opencap_throw("Error: dimensions of state DMs do not match specified basis set.");
    		break;
    	}
	}
	is.seekg (0, ios::beg);
	while(is.peek()!=EOF)
	{
    	std::getline(is, line);
    	if(line.find("Alpha  State Density")!= std::string::npos && sep_alpha_beta)
    		nstates++;
    	else if(line.find("State Density")!= std::string::npos && !sep_alpha_beta)
    		nstates++;
	}
	if(nstates==0)
		opencap_throw("Error:Unable to find any densities in:" +fchk_filename);
	if(sep_alpha_beta)
		return qchem_read_in_dms_open_shell(fchk_filename,nstates,bs);
	else
		return qchem_read_in_dms_closed_shell(fchk_filename,nstates,bs);

}

std::array<std::vector<std::vector<Eigen::MatrixXd>>,2> qchem_read_in_dms_open_shell(std::string dmat_filename,
		size_t nstates, BasisSet bs)
{
	std::vector< std::vector<Eigen::MatrixXd>> alpha_opdms(nstates, std::vector<Eigen::MatrixXd> (nstates));
	std::vector< std::vector<Eigen::MatrixXd>> beta_opdms(nstates, std::vector<Eigen::MatrixXd> (nstates));
	//start with state density matrices, alpha and beta densities
	std::ifstream is(dmat_filename);
    if (is.good())
    {
    	std::string line, rest;
    	std::getline(is, line);
    	for (size_t i=0;i<nstates;i++)
    	{
    			//alpha first, then beta
    			for(size_t spin=1;spin<=2;spin++)
    			{
					while(line.find("State Density")== std::string::npos)
					{
						std::getline(is,line);
	    	    		if (is.peek()==EOF)
	    	    			opencap_throw("Error: Reached end of file before densities for "+
	    	    					std::to_string(nstates) + " states were found.");
					}
					//last part of line should be number of elements to read
					int num_elements = stoi(split(line,' ').back());
					if(sqrt(num_elements)!=bs.Nbasis)
						opencap_throw("Error: dimensions of TDMs do not match specified basis set.");
					size_t lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
					std::vector<double> matrix_elements;
					for (size_t k=1;k<=lines_to_read;k++)
					{
						std::getline(is,line);
						std::vector<std::string> tokens = split(line,' ');
						for (auto token:tokens)
						{
							matrix_elements.push_back(std::stod(token));
						}
					}
					Eigen::MatrixXd st_opdm(bs.Nbasis,bs.Nbasis);
					st_opdm=Eigen::MatrixXd::Zero(bs.Nbasis,bs.Nbasis);
					fill_mat(matrix_elements,st_opdm);
					to_opencap_ordering(st_opdm,bs,get_qchem_ids(bs));
					if(spin==1)
						alpha_opdms[i][i]=st_opdm;
					else
						beta_opdms[i][i]=st_opdm;
    			}
		}
    	//now tdms
    	for(size_t i=0;i<nstates;i++)
    	{
    		for (size_t j=i+1;j<nstates;j++)
    		{
    			//alpha first, then beta
    			for (size_t spin=1;spin<=2;spin++)
    			{
					while(line.find("Transition DM")== std::string::npos)
					{
						std::getline(is,line);
	    	    		if (is.peek()==EOF)
	    	    			opencap_throw("Error: Reached end of file before densities for "+
	    	    					std::to_string(nstates) + " states were found.");
					}
					//last part of line should be number of elements to read
					size_t num_elements = stoi(split(line,' ').back());
					if(sqrt(num_elements)!=bs.Nbasis)
						opencap_throw("Error: dimensions of TDMs do not match specified basis set.");
					size_t lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
					std::vector<double> matrix_elements;
					for (size_t k=1;k<=lines_to_read;k++)
					{
						std::getline(is,line);
						std::vector<std::string> tokens = split(line,' ');
						for (auto token:tokens)
							matrix_elements.push_back(std::stod(token));
					}
					Eigen::MatrixXd st_opdm(bs.Nbasis,bs.Nbasis);
					st_opdm=Eigen::MatrixXd::Zero(bs.Nbasis,bs.Nbasis);
					fill_mat(matrix_elements,st_opdm);
					to_opencap_ordering(st_opdm,bs,get_qchem_ids(bs));
					if(spin==1)
					{
						alpha_opdms[i][j]=st_opdm;
						alpha_opdms[j][i]=st_opdm;
					}
					else
					{
						beta_opdms[i][j]=st_opdm;
						beta_opdms[j][i]=st_opdm;
					}
    			}
    		}
    	}
    }
    return {alpha_opdms,beta_opdms};
}

Eigen::MatrixXd qchem_read_overlap(std::string dmat_filename, BasisSet bs)
{
	size_t num_bf = bs.Nbasis;
    std::ifstream is(dmat_filename);
	Eigen::MatrixXd smat(num_bf,num_bf);
	smat=Eigen::MatrixXd::Zero(num_bf,num_bf);
    if (is.good())
    {
    	std::string line, rest;
    	while (line.find("Overlap Matrix")== std::string::npos)
    	{
        	std::getline(is, line);
    		if (is.peek()==EOF)
    			opencap_throw("Error: Reached end of file before Overlap Matrix was found.");
    	}
    	size_t num_elements = stoi(split(line,' ').back());
		size_t lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
		std::vector<double> matrix_elements;
		for (size_t k=1;k<=lines_to_read;k++)
		{
			std::getline(is,line);
			std::vector<std::string> tokens = split(line,' ');
			for (auto token:tokens)
				matrix_elements.push_back(std::stod(token));
		}
		fill_LT(matrix_elements,smat);
		to_opencap_ordering(smat,bs,get_qchem_ids(bs));
    }
    return smat;
}

Eigen::MatrixXd read_qchem_energies(size_t nstates,std::string method,std::string output_file)
{
	Eigen::MatrixXd ZERO_ORDER_H(nstates,nstates);
	ZERO_ORDER_H=Eigen::MatrixXd::Zero(nstates,nstates);
	transform(method.begin(),method.end(),method.begin(),::toupper);
	std::ifstream is(output_file);
    if (is.good())
    {
    	std::string line, rest;
    	std::getline(is, line);
    	size_t state_idx = 1;
    	while (state_idx<=nstates)
    	{
    		if (is.peek()==EOF)
    			opencap_throw("Error: Reached end of file before "+ std::to_string(nstates) + " energies were found.");
    		std::string line_to_find = method +" transition " + std::to_string(state_idx);
    		if (line.find(line_to_find)!= std::string::npos)
    		{
				std::getline(is,line);
				ZERO_ORDER_H(state_idx-1,state_idx-1) = std::stod(split(line,' ')[3]);
				state_idx++;
    		}
    		else
    			std::getline(is,line);
    	}
    }
    return ZERO_ORDER_H;
}

std::array<std::vector<std::vector<Eigen::MatrixXd>>,2> qchem_read_in_dms_closed_shell(std::string dmat_filename,
		size_t nstates, BasisSet bs)
{
	std::vector< std::vector<Eigen::MatrixXd>> opdms(nstates, std::vector<Eigen::MatrixXd> (nstates));
	//start with state density matrices
	std::ifstream is(dmat_filename);
    if (is.good())
    {
    	std::string line, rest;
    	std::getline(is, line);
    	for (size_t i=0;i<nstates;i++)
    	{
				while(line.find("State Density")== std::string::npos)
				{
					std::getline(is,line);
    	    		if (is.peek()==EOF)
    	    			opencap_throw("Error: Reached end of file before densities for "+
    	    					std::to_string(nstates) + " states were found.");
				}
				//last part of line should be number of elements to read
				size_t num_elements = stoi(split(line,' ').back());
				size_t lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
				std::vector<double> matrix_elements;
				for (size_t k=1;k<=lines_to_read;k++)
				{
					std::getline(is,line);
					std::vector<std::string> tokens = split(line,' ');
					for (auto token:tokens)
					{
						matrix_elements.push_back(std::stod(token));
					}
				}
				Eigen::MatrixXd st_opdm(bs.Nbasis,bs.Nbasis);
				st_opdm=Eigen::MatrixXd::Zero(bs.Nbasis,bs.Nbasis);
				fill_mat(matrix_elements,st_opdm);
				to_opencap_ordering(st_opdm,bs,get_qchem_ids(bs));
				opdms[i][i]=0.5*st_opdm;
		}
    	//now tdms
    	for(size_t i=0;i<nstates;i++)
    	{
    		for (size_t j=i+1;j<nstates;j++)
    		{
				while(line.find("Transition DM")== std::string::npos)
				{
					std::getline(is,line);
    	    		if (is.peek()==EOF)
    	    			opencap_throw("Error: Reached end of file before densities for "+
    	    					std::to_string(nstates) + " states were found.");
				}
				//last part of line should be number of elements to read
				size_t num_elements = stoi(split(line,' ').back());
				size_t lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
				std::vector<double> matrix_elements;
				for (size_t k=1;k<=lines_to_read;k++)
				{
					std::getline(is,line);
					std::vector<std::string> tokens = split(line,' ');
					for (auto token:tokens)
						matrix_elements.push_back(std::stod(token));
				}
				Eigen::MatrixXd st_opdm(bs.Nbasis,bs.Nbasis);
				st_opdm=Eigen::MatrixXd::Zero(bs.Nbasis,bs.Nbasis);
				fill_mat(matrix_elements,st_opdm);
				to_opencap_ordering(st_opdm,bs,get_qchem_ids(bs));
				opdms[i][j]=0.5*st_opdm;
				opdms[j][i]=0.5*st_opdm;
    		}
    	}
    }
    return {opdms,opdms};

}

std::vector<Atom> read_geometry_from_fchk(std::string fchk_filename)
{
	std::vector<Atom> atoms;
	std::vector<size_t> atom_nums;
	std::vector<double> coords;
	std::ifstream is(fchk_filename);
    if (is.good())
    {
    	std::string line, rest;
    	std::getline(is, line);
		while(line.find("Atomic numbers")== std::string::npos)
		{
			std::getline(is,line);
    		if (is.peek()==EOF)
    			opencap_throw("Error: Reached end of file before atomic numbers were found.");
		}
		size_t num_elements = stoi(split(line,' ').back());
		size_t lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
		for (size_t k=1;k<=lines_to_read;k++)
		{
			std::getline(is,line);
			std::vector<std::string> tokens = split(line,' ');
			for (auto token:tokens)
				atom_nums.push_back(std::stoi(token));
		}
		while(line.find("Current cartesian coordinates")== std::string::npos)
		{
			std::getline(is,line);
    		if (is.peek()==EOF)
    			opencap_throw("Error: Reached end of file before Current Cartesian Coordinates were found.");
		}
		num_elements = stoi(split(line,' ').back());
		lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
		for (size_t k=1;k<=lines_to_read;k++)
		{
			std::getline(is,line);
			std::vector<std::string> tokens = split(line,' ');
			for (auto token:tokens)
				coords.push_back(std::stod(token));
		}
		//ok now lets populate our atoms
		for(size_t i=0;i<atom_nums.size();i++)
			atoms.push_back(Atom(atom_nums[i],coords[i*3],coords[i*3+1],coords[i*3+2]));
    }
    else
    {
    	opencap_throw("Error: I couldn't read:" + fchk_filename);
    }
    return atoms;
}

BasisSet read_basis_from_fchk(std::string fchk_filename, std::vector<Atom> atoms)
{
	std::vector<int> shell_types;
	std::vector<int> prims_per_shell;
	std::vector<int> atom_ids;
	std::vector<double> exps;
	std::vector<double> coeffs;
	// needed for SP specification
	std::vector<double> p_coeffs;
	bool SP_basis_function = true;
	BasisSet bs;
	for(auto atm:atoms)
		bs.centers.push_back(atm.coords);
	std::vector<shell_id> ids;
	std::ifstream is(fchk_filename);
    if (is.good())
    {
    	std::string line, rest;
    	std::getline(is, line);
    	//First lets figure out if there are SP functions
    	//shell types
		while(line.find("Shell types")== std::string::npos)
		{
			std::getline(is,line);
    		if (is.peek()==EOF)
    			opencap_throw("Error: Reached end of file before shell types were found.");
		}
		size_t num_elements = stoi(split(line,' ').back());
		size_t lines_to_read = num_elements%6==0 ? (num_elements/6) : num_elements/6+1;
		for (size_t k=1;k<=lines_to_read;k++)
		{
			std::getline(is,line);
			std::vector<std::string> tokens = split(line,' ');
			for (auto token:tokens)
			{
				shell_types.push_back(std::stoi(token));
			}
		}
		//prims per shell
		while(line.find("Number of primitives per shell")== std::string::npos)
		{
			std::getline(is,line);
    		if (is.peek()==EOF)
    			opencap_throw("Error: Reached end of file before Number of primitives per shell were found.");
		}
		num_elements = stoi(split(line,' ').back());
		lines_to_read = num_elements%6==0 ? (num_elements/6) : num_elements/6+1;
		for (size_t k=1;k<=lines_to_read;k++)
		{
			std::getline(is,line);
			std::vector<std::string> tokens = split(line,' ');
			for (auto token:tokens)
				prims_per_shell.push_back(std::stoi(token));
		}
		//atom ids
		while(line.find("Shell to atom map")== std::string::npos)
		{
			std::getline(is,line);
    		if (is.peek()==EOF)
    			opencap_throw("Error: Reached end of file before Shell to atom map was found.");
		}
		num_elements = stoi(split(line,' ').back());
		lines_to_read = num_elements%6==0 ? (num_elements/6) : num_elements/6+1;
		for (size_t k=1;k<=lines_to_read;k++)
		{
			std::getline(is,line);
			std::vector<std::string> tokens = split(line,' ');
			for (auto token:tokens)
				atom_ids.push_back(std::stoi(token));
		}
		//prims
		while(line.find("Primitive exponents")== std::string::npos)
		{
			std::getline(is,line);
    		if (is.peek()==EOF)
    			opencap_throw("Error: Reached end of file before Primitive exponents were found.");
		}
		num_elements = stoi(split(line,' ').back());
		lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
		for (size_t k=1;k<=lines_to_read;k++)
		{
			std::getline(is,line);
			std::vector<std::string> tokens = split(line,' ');
			for (auto token:tokens)
				exps.push_back(std::stod(token));
		}
		//coeffs
		while(line.find("Contraction coefficients")== std::string::npos)
		{
			std::getline(is,line);
    		if (is.peek()==EOF)
    			opencap_throw("Error: Reached end of file before Contraction coefficients were found.");
		}
		num_elements = stoi(split(line,' ').back());
		lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
		for (size_t k=1;k<=lines_to_read;k++)
		{
			std::getline(is,line);
			std::vector<std::string> tokens = split(line,' ');
			for (auto token:tokens)
				coeffs.push_back(std::stod(token));
		}
		while(line.find("P(S=P) Contraction coefficients")== std::string::npos)
		{
			std::getline(is,line);
    		if (is.peek()==EOF)
    		{
    			SP_basis_function = false;
    			break;
    		}
		}
		if (SP_basis_function)
		{
			num_elements = stoi(split(line,' ').back());
			lines_to_read = num_elements%5==0 ? (num_elements/5) : num_elements/5+1;
			for (size_t k=1;k<=lines_to_read;k++)
			{
				std::getline(is,line);
				std::vector<std::string> tokens = split(line,' ');
				for (auto token:tokens)
					p_coeffs.push_back(std::stod(token));
			}
		}
    }
    size_t prim_idx=0;
    for(size_t i=0;i<shell_types.size();i++)
    {
    	//SP
    	if(shell_types[i]==-1)
    	{
    		if(p_coeffs.size()==0)
    			opencap_throw("Error: missing section P(S=P) Contraction coefficients.");
    		Shell s_shell(0,atoms[atom_ids[i]-1].coords);
    		Shell p_shell(1,atoms[atom_ids[i]-1].coords);
			int num_prims = prims_per_shell[i];
			for(int j=1;j<=num_prims;j++)
			{
				s_shell.add_primitive(exps[prim_idx],coeffs[prim_idx]);
				p_shell.add_primitive(exps[prim_idx],p_coeffs[prim_idx]);
				prim_idx++;
			}
			bs.add_shell(s_shell);
			bs.add_shell(p_shell);
    	}
    	else
    	{
			Shell new_shell(abs(shell_types[i]),atoms[atom_ids[i]-1].coords);
			if(shell_types[i]>0 && new_shell.l>1)
				new_shell.pure=false;
			int num_prims = prims_per_shell[i];
			for(int j=1;j<=num_prims;j++)
			{
				new_shell.add_primitive(exps[prim_idx],coeffs[prim_idx]);
				prim_idx++;
			}
			bs.add_shell(new_shell);
    	}
    }
    bs.normalize();
    return bs;
}


// -----------------------------------------------------------------------
// RTToolbox - DKFZ radiotherapy quantitative evaluation library
//
// Copyright (c) German Cancer Research Center (DKFZ),
// Software development for Integrated Diagnostics and Therapy (SIDT).
// ALL RIGHTS RESERVED.
// See rttbCopyright.txt or
// http://www.dkfz.de/en/sidt/projects/rttb/copyright.html
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE.  See the above copyright notices for more information.
//
//------------------------------------------------------------------------

#include <iostream>
#include <sstream>
#include <stdexcept>

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

#include "rttbStructure.h"
#include "rttbNullPointerException.h"
#include "rttbInvalidParameterException.h"

namespace rttb
{
	namespace core
	{

		/*! Compares two polygons in the same plane.
			Helper function for sorting of polygons.
		*/
		bool comparePolygon(PolygonType A, PolygonType B)
		{
			PolygonType::iterator it;

			for (it = A.begin(); it != A.end(); ++it)
			{
				if ((*it)(2) != A.at(0)(2))
				{
					throw std::range_error("Error: A must in the same _z plane!");
				}
			}

			PolygonType::iterator it2;

			for (it2 = B.begin(); it2 != B.end(); ++it2)
			{
				if ((*it2)(2) != B.at(0)(2))
				{
					throw std::range_error("Error: B must in the same _z plane!");
				}
			}

			if (A.size() == 0 || B.size() == 0)
			{
				throw std::range_error("Error: A and B must not be empty!");
			}

			return (A.at(0)(2) < B.at(0)(2));

		}

		Structure::Structure() : _structureVector(0), _label("")
		{
			if (_strUID.empty())
			{
				boost::uuids::uuid id;
				boost::uuids::random_generator generator;
				id = generator();

				std::stringstream ss;
				ss << id;

				_strUID = ss.str();
			}
		}

		Structure::Structure(PolygonSequenceType strVector) : Structure()
		{
			_structureVector = strVector;
			sort(_structureVector.begin(), _structureVector.end(), comparePolygon);
		}

		Structure::Structure(const Structure& copy) : _structureVector(copy.getStructureVector()),
			_strUID(copy.getUID()),
			_label(copy.getLabel())
		{
		}

		Structure::~Structure() = default;

		const PolygonSequenceType& Structure::getStructureVector() const
		{
			return _structureVector;
		}

		int Structure::getNumberOfEndpoints() const
		{
			int count = 0;
			PolygonSequenceType::const_iterator itVV;

			for (itVV = _structureVector.begin(); itVV != _structureVector.end(); ++itVV)
			{
				count += (int)(*itVV).size();
			}

			return count;
		}

		IDType Structure::getUID() const
		{

			return _strUID;
		}

		void Structure::setUID(const IDType& aUID)
		{
			_strUID = aUID;
		}

		void Structure::setLabel(const StructureLabel& aLabel)
		{
			_label = aLabel;
		}

		StructureLabel Structure::getLabel() const
		{
			return _label;
		}

	}//end namespace core
}//end namespace rttb


#include <tf/transform_datatypes.h>

#include <urdf_parser/urdf_parser.h>

#include <boost/assign.hpp>
#include <angles/angles.h>
#include <control_toolbox/filters.h>
#include <walrus_pod_controller/walrus_pod_controller.h>

namespace walrus_pod_controller{

Pod::Pod(ros::NodeHandle nh)
  : nh_(nh), next_state_update_(0), controller_state_period_(0.5),
    command_timeout_(0.5) {
  last_command_.mode = walrus_pod_controller::PodCommand::DISABLED;
}

bool Pod::init(hardware_interface::EffortJointInterface* hw, urdf::Model urdf) {
  std::string joint_name;

  nh_.param("command_timeout", command_timeout_, command_timeout_);

  if (!nh_.getParam("joint", joint_name)) {
    ROS_ERROR("No joint given (namespace: %s)", nh_.getNamespace().c_str());
    return false;
  }

  joint_ = hw->getHandle(joint_name);
  boost::shared_ptr<const urdf::Joint> joint_urdf =  urdf.getJoint(joint_name);
  if (!joint_urdf) {
    ROS_ERROR("Could not find joint '%s' in urdf", joint_name.c_str());
    return false;
  }

  if (!pid_controller_.init(ros::NodeHandle(nh_, "pid")))
    return false;

  controller_state_publisher_.reset(new realtime_tools::RealtimePublisher<control_msgs::JointControllerState>(nh_, "state", 1));

  command_sub_ = nh_.subscribe<walrus_pod_controller::PodCommand>("command", 1, &Pod::setCommandCallback, this);
}

void Pod::setCommandCallback(const walrus_pod_controller::PodCommandConstPtr& command) {
  walrus_pod_controller::PodCommandStamped command_stamped;
  command_stamped.header.stamp = ros::Time::now();
  command_stamped.command = *command;
  command_buffer_.writeFromNonRT(command_stamped);
}

void Pod::starting(const ros::Time& time) {
  walrus_pod_controller::PodCommandStamped command;
  command.header.stamp = ros::Time(0);
  command.command.mode = walrus_pod_controller::PodCommand::HOLD_POSITION;
  command_buffer_.initRT(command);
}

void Pod::stopping(const ros::Time& time) {
  joint_.setCommand(0.0); // set joint effort to zero
}

void Pod::update(const ros::Time& time, const ros::Duration& period) {
  double current_position = joint_.getPosition();
  walrus_pod_controller::PodCommandStamped command_stamped = *(command_buffer_.readFromRT());

  const double dt = (time - command_stamped.header.stamp).toSec();
  double command_effort;
  double command_position;
  double error;

  walrus_pod_controller::PodCommand command = command_stamped.command;
  if (dt > command_timeout_) {
    // timeout
    command.mode = walrus_pod_controller::PodCommand::DISABLED;
  }

  if(command.mode == walrus_pod_controller::PodCommand::POSITION ||
     command.mode == walrus_pod_controller::PodCommand::HOLD_POSITION) {
    // switching to position mode so reset the pid controller
    if(last_command_.mode != walrus_pod_controller::PodCommand::POSITION &&
       last_command_.mode != walrus_pod_controller::PodCommand::HOLD_POSITION) {
      pid_controller_.reset();
    }

    if(command.mode == walrus_pod_controller::PodCommand::HOLD_POSITION) {
      // Hold the position that was held last time
      if(last_command_.mode == walrus_pod_controller::PodCommand::HOLD_POSITION) {
	command.set_point = last_command_.set_point;
      }
      else {
	command.set_point = current_position;
      }
    }

    command_position = filters::clamp(command.set_point, -M_PI, M_PI);
    error = angles::shortest_angular_distance(current_position, command_position);
    command_effort = pid_controller_.computeCommand(error, period);
  }
  else if(command.mode == walrus_pod_controller::PodCommand::EFFORT) {
    command_position = std::numeric_limits<double>::quiet_NaN();
    error = std::numeric_limits<double>::quiet_NaN();
    command_effort = command.set_point;
  }
  else { // disabled
    command_position = std::numeric_limits<double>::quiet_NaN();
    error = std::numeric_limits<double>::quiet_NaN();
    command_effort = 0.0;
  }
  last_command_ = command;
  joint_.setCommand(command_effort);

  // publish state
  if (time > next_state_update_) {
    if(controller_state_publisher_ && controller_state_publisher_->trylock()) {
      controller_state_publisher_->msg_.header.stamp = time;
      controller_state_publisher_->msg_.set_point = command_position;
      controller_state_publisher_->msg_.process_value = current_position;
      controller_state_publisher_->msg_.process_value_dot = joint_.getVelocity();
      controller_state_publisher_->msg_.error = error;
      controller_state_publisher_->msg_.time_step = period.toSec();
      controller_state_publisher_->msg_.command = command_effort;

      double dummy;
      pid_controller_.getGains(controller_state_publisher_->msg_.p,
			       controller_state_publisher_->msg_.i,
			       controller_state_publisher_->msg_.d,
			       controller_state_publisher_->msg_.i_clamp,
			       dummy);
      controller_state_publisher_->unlockAndPublish();
    }
    next_state_update_ = time + controller_state_period_;
  }
}

WalrusPodController::WalrusPodController()
{
}

bool WalrusPodController::init(hardware_interface::EffortJointInterface* hw,
				 ros::NodeHandle& root_nh,
				 ros::NodeHandle &controller_nh)
{
  urdf::Model urdf;
  if (!urdf.initParam("robot_description")) {
    ROS_ERROR("Failed to parse urdf file");
    return false;
  }

  front.reset(new Pod(ros::NodeHandle(controller_nh, "front")));
  if(!front->init(hw, urdf))
    return false;

  back.reset(new Pod(ros::NodeHandle(controller_nh, "back")));
  if(!back->init(hw, urdf))
    return false;

  return true;
}

void WalrusPodController::update(const ros::Time& time, const ros::Duration& period)
{
  front->update(time, period);
  back->update(time, period);
}

void WalrusPodController::starting(const ros::Time& time)
{
  front->starting(time);
  back->starting(time);
}

void WalrusPodController::stopping(const ros::Time& time)
{
  front->stopping(time);
  back->stopping(time);
}

} // namespace diff_pod_controller


//
// Created by jestjest on 11/19/17.
//

#include <boost/bind.hpp>
#include <cereal/archives/binary.hpp>
#include <messages/queryMessage.hpp>
#include <messages/responseMessage.hpp>
#include <messages/findQuery.hpp>
#include <messages/storeQuery.hpp>
#include <queue>
#include <messages/findValueResponse.hpp>
#include <messages/findNodeResponse.hpp>
#include "protocol.hpp"
#include "network.hpp"

namespace kdml {

    using boost::asio::ip::udp;
    namespace asio = boost::asio;
    namespace mp = boost::multiprecision;

    Protocol::Protocol(const NodeInfo& node)
            : owner(node),
              routingTable(node.id),
              ioService{},
              ioLock(new asio::io_service::work(ioService)) {

        network = new net::Network(node, ioService, this);
        network->startReceive();
        ioThread = std::thread([this]() { ioService.run(); });
    }

    void Protocol::async_get(mp::uint256_t key, kdml::GetCallback callback) {
        node_lookup(key, callback, true, false);
    }

    void Protocol::async_store(mp::uint256_t key, NodeInfo value) {
        node_lookup(key, NULL, false, true);
    }

    // TODO queue user commands while bootstrapping).
    void Protocol::bootstrap(const NodeInfo& peer) {
        Nodes endpoints = resolveEndpoint(peer);
        probePeers(std::move(endpoints));
    }

    void Protocol::join() {
        ioService.stop();
        if (ioThread.joinable()) {
            ioThread.join();
        }
    }

    void Protocol::handleReceive(const boost::system::error_code& error,
                                 std::size_t /*bytes_transferred*/) {

        if (error == asio::error::operation_aborted) {
            return;
        }

        asio::streambuf sb;
        boost::system::error_code recvError = network->populateBuf(sb);

        if (!recvError) {
            NodeInfo sender(network->getRemotePeer());
            std::shared_ptr<kdml::net::Message> message;
            {
                std::istream istream(&sb);
                cereal::BinaryInputArchive iarchive(istream);
                iarchive(message);
            }

            std::cout << "Parsed message: " << *message << std::endl;
            handleMessage(message, sender);
        }
        network->startReceive();
    }

    void Protocol::probePeers(Nodes endpoints) {
        if (endpoints.empty()) {
            throw std::string("Failed to find peers");
        } else {
            NodeInfo ep = endpoints.back();
            endpoints.pop_back();

            SimpleCallback onPong = [=](bool failure) {
                if (failure) {
                    probePeers(endpoints);
                } else {
                    auto bucket = routingTable.insertNode(ep);
                    node_lookup(owner.id, [bucket](Nodes found) {
                        // TODO: refresh buckets after bucket
                    }, false, false);
                }
            };

            network->send_ping(ep, onPong);
        }
    }

    Nodes Protocol::resolveEndpoint(const NodeInfo& endpoint) {
        udp::resolver resolver(ioService);
        udp::resolver::query query(udp::v4(), endpoint.getIpAddr(),
                                   std::to_string(endpoint.port));
        Nodes eps;
        auto resolvedEndpoints = resolver.resolve(query);
        decltype(resolvedEndpoints) end;

        while (resolvedEndpoints != end) {
            auto ep = (*resolvedEndpoints).endpoint();
            eps.emplace_back(NodeInfo{ep.address().to_string(), ep.port()});
            resolvedEndpoints++;
        }
        return eps;
    }

    void Protocol::handleMessage(std::shared_ptr<net::Message> msg,
                                 NodeInfo sender) {

        switch (msg->getMessageType()) {
            case net::MessageType::ERROR: {
                std::cerr << "Received error message: " << *msg << std::endl;
                break;
            }
            case net::MessageType::QUERY: {
                routingTable.insertNode(sender);
                handleQuery(std::dynamic_pointer_cast<net::QueryMessage>(msg), std::move(sender));
                break;
            }
            case net::MessageType::RESPONSE: {
                routingTable.insertNode(sender);
                handleResponse(std::dynamic_pointer_cast<net::ResponseMessage>(msg));
                break;
            }
        }
    }

    void Protocol::find_value_callback(Nodes nodes, bool found, GetCallback callback) {
        if(found) {
            std::cout << "Found nodes: first one: " << (*nodes.begin()).getIpAddr() << std::endl;
            callback(nodes);
        } else {
            std::cout << "Did not find a knowledgeable node." << std::endl;
            callback({});
        }
    }

    void Protocol::store_callback(mp::uint256_t key, Nodes nodes) {
        for(NodeInfo node : nodes) {
            network->send_store(key, node);
        }
    }

    /*
     * Pick a closest nodes to key from bucket, send asynchronous FIND_NODE rpcs to each.
     */
    void Protocol::node_lookup(mp::uint256_t key, GetCallback callback, bool findValue, bool store) {

        std::vector<NodeInfo> a_closest_nodes = routingTable.getAClosestNodes(ALPHA, key);
        RequestState request_state;
        request_state.key = key;
        request_state.findValue = findValue;
        request_state.store = store;
        request_state.callback = callback;
        for(NodeInfo node : a_closest_nodes) {
            NodeInfoWrapper node_wrapper(key, node);
            request_state.k_closest_nodes.insert(node_wrapper);
            request_state.responses_waiting++;
            if (request_state.findValue) {
                network->send_find_value(key, node);
            } else {
                network->send_find_node(key, node);
            }
        }
        lookups[key] = request_state;
    }

    //TODO: Nodes that fail to respond should be removed from consideration unless they respond

    void Protocol::node_lookup_callback(mp::uint256_t sender, RequestState& request_state, Nodes k_nodes,
                                        mp::uint256_t key, bool found) {

        request_state.responses_waiting--;
        request_state.responded_nodes.insert(sender);

        if (found) {
            assert(request_state.findValue);
            // If found, k_nodes will be the list of values associated with key
            find_value_callback(k_nodes, found, request_state.callback);
            lookups.erase(lookups.find(key), lookups.end());
            return;
        }

        for(NodeInfo node : k_nodes) {
            NodeInfoWrapper node_wrapper(key, node);
            request_state.k_closest_nodes.insert(node_wrapper);
        }

        // Query alpha more nodes
        // Terminates when has queried and heard responses from k closest nodes
        int responded = 0;
        bool k_responded = true;
        for (auto it=request_state.k_closest_nodes.begin(); it!=request_state.k_closest_nodes.end(); ++it) {
            if (request_state.responses_waiting >= ALPHA) break;
            if (k_responded && responded >= kdml::K) break;

            NodeInfoWrapper node_wrapper = *it;

            // Keep track if k closest nodes have responded
            auto responded_it = request_state.responded_nodes.find(node_wrapper.node.id);
            if (responded_it != request_state.responded_nodes.end()) {
                responded++;
            } else {
                k_responded = false;
            }

            // Do not query if node has already been queried
            auto queried_it = request_state.queried_nodes.find(node_wrapper.node.id);
            if (queried_it != request_state.queried_nodes.end()) {
                continue;
            }

            request_state.responses_waiting++;
            request_state.queried_nodes.insert(node_wrapper.node.id);
            if (request_state.findValue) {
                network->send_find_value(key, node_wrapper.node);
            } else {
                network->send_find_node(key, node_wrapper.node);
            }
        }

        if (request_state.responses_waiting == 0) {
            Nodes k_closest_nodes_list;
            for (auto it=request_state.k_closest_nodes.begin(); it!=request_state.k_closest_nodes.end()
                    && k_closest_nodes_list.size() < kdml::K; ++it) {
                NodeInfoWrapper node_wrapper = *it;
                k_closest_nodes_list.push_back(node_wrapper.node);
            }
            if (request_state.findValue) {
                find_value_callback(k_closest_nodes_list, found, request_state.callback);
            } else if (request_state.store){
                store_callback(key, k_closest_nodes_list);
            } else {
                std::cout << "Completed lookup for bootstrapping" << std::endl;
            }

            lookups.erase (lookups.find(key), lookups.end());
        }
    }

    void Protocol::handleQuery(std::shared_ptr<net::QueryMessage> msg,
                               NodeInfo sender) {

        switch (msg->getQueryType()) {
            case net::QueryType::PING: {
                network->send_ping_response(sender, msg->getTid());
                break;
            }
            case net::QueryType::FIND_NODE: {
                auto query = std::dynamic_pointer_cast<net::FindQuery>(msg);
                auto nodes = routingTable.getKClosestNodes(query->getTarget());
                network->send_find_node_response(sender, nodes, msg->getTid());
                break;
            }
            case net::QueryType::FIND_VALUE: {
                auto query = std::dynamic_pointer_cast<net::FindQuery>(msg);
                auto value = storage.find(query->getTarget());
                bool found = (value != storage.end());
                Nodes result;
                if (found) {
                    result = value->second;
                } else {
                    result = routingTable.getKClosestNodes(query->getTarget());
                }
                network->send_find_value_response(sender, found, result,
                                                  msg->getTid());
                break;
            }
            case net::QueryType::STORE: {
                auto query = std::dynamic_pointer_cast<net::StoreQuery>(msg);
                bool success = false;

                try {
                    storage[query->getKey()].emplace_back(query->getVal());
                    success = true;
                } catch (...) {}

                network->send_store_response(sender, success, msg->getTid());
                break;
            }
        }
    }

    void Protocol::handleResponse(std::shared_ptr<net::ResponseMessage> msg) {
        uint32_t tid = msg->getTid();

        if (!network->containsRequest(tid)) {
            std::cerr << "Received alien message" << *msg << std::endl;
            return;
        }

        Request outstanding = network->getRequest(tid);
        if (msg->getQueryType() == net::QueryType::PING) {
            network->removeRequest(tid);
            outstanding.onDone(msg, false);
        } else {
            auto it = lookups.find(outstanding.key);
            if (it != lookups.end()) {
                RequestState& request_state = it->second;
                if (outstanding.findValue) {
                    auto value_res = std::dynamic_pointer_cast<net::FindValueResponse>(msg);
                    node_lookup_callback(msg->id, request_state, value_res->data, outstanding.key, value_res->found);
                } else {
                    auto find_res = std::dynamic_pointer_cast<net::FindNodeResponse>(msg);
                    node_lookup_callback(msg->id, request_state, find_res->nodes, outstanding.key, false);
                }
            } else {
                std::cout << "Got response for nonexistent nodes lookup" << std::endl;
            }
        }
    }

}


#pragma once

#include <unordered_map>

#include <boost/gil/gil_all.hpp>
#include <boost/optional.hpp>

// assimp::Importer
#include <assimp/Importer.hpp>
// aiPostProcessSteps for assimp::Importer::ReadFile
#include <assimp/postprocess.h>
// aiScene, aiNode
#include <assimp/scene.h>

#include "../glew.detail/c.hxx"
#include "../glew.detail/gl_type.hxx"
#include "../glew.detail/error.hxx"

#include "helper.hxx"
#include "material.hxx"
#include "bone.hxx"
#include "animation_data.hxx"

#include "../shader.detail/constant.hxx"

namespace wonder_rabbit_project
{
  namespace wonderland
  {
    namespace renderer
    {
      namespace model
      {

        struct vertex_buffer_t
        {
          using float_t = float;

          glm::vec4 position;
          glm::vec4 color;
          glm::vec2 texcoord0;
          glm::vec2 texcoord1;
          glm::vec2 texcoord2;
          glm::vec2 texcoord3;
          glm::vec2 texcoord4;
          glm::vec2 texcoord5;
          glm::vec2 texcoord6;
          glm::vec2 texcoord7;
          glm::vec3 normal;
          glm::vec3 tangent;
          glm::vec3 bitangent;
          glm::vec4 bone_ids;
          glm::vec4 bone_weights;

          vertex_buffer_t
          ( glm::vec4&& pos
          , glm::vec4&& col
          , glm::vec2&& tex0
          , glm::vec2&& tex1
          , glm::vec2&& tex2
          , glm::vec2&& tex3
          , glm::vec2&& tex4
          , glm::vec2&& tex5
          , glm::vec2&& tex6
          , glm::vec2&& tex7
          , glm::vec3&& nor
          , glm::vec3&& tan
          , glm::vec3&& bit
          , glm::vec4&& bid
          , glm::vec4&& bwt
          )
          : position     ( std::move( pos ) )
          , color        ( std::move( col ) )
          , texcoord0    ( std::move( tex0 ) )
          , texcoord1    ( std::move( tex1 ) )
          , texcoord2    ( std::move( tex2 ) )
          , texcoord3    ( std::move( tex3 ) )
          , texcoord4    ( std::move( tex4 ) )
          , texcoord5    ( std::move( tex5 ) )
          , texcoord6    ( std::move( tex6 ) )
          , texcoord7    ( std::move( tex7 ) )
          , normal       ( std::move( nor ) )
          , tangent      ( std::move( tan ) )
          , bitangent    ( std::move( bit ) )
          , bone_ids     ( std::move( bid ) )
          , bone_weights ( std::move( bwt ) )
          { }

          template < class T = float_t >
          auto to_ptr() const
          -> const T*
          { return reinterpret_cast< const T* >( &position.x ); }

          static constexpr auto size_of_element = sizeof( float_t );

          static constexpr auto size_of_memory
          = ( sizeof( decltype( position ) )
          + sizeof( decltype( color ) )
          + sizeof( decltype( texcoord0 ) )
          + sizeof( decltype( texcoord1 ) )
          + sizeof( decltype( texcoord2 ) )
          + sizeof( decltype( texcoord3 ) )
          + sizeof( decltype( texcoord4 ) )
          + sizeof( decltype( texcoord5 ) )
          + sizeof( decltype( texcoord6 ) )
          + sizeof( decltype( texcoord7 ) )
          + sizeof( decltype( normal ) )
          + sizeof( decltype( tangent ) )
          + sizeof( decltype( bitangent ) )
          + sizeof( decltype( bone_ids ) )
          + sizeof( decltype( bone_weights ) )
          );

          static constexpr auto count_of_elements = size_of_memory / size_of_element;

          static constexpr auto count_of_position_elements     = sizeof( decltype( position )     ) / size_of_element;
          static constexpr auto count_of_color_elements        = sizeof( decltype( color )        ) / size_of_element;
          static constexpr auto count_of_texcoord0_elements    = sizeof( decltype( texcoord0 )    ) / size_of_element;
          static constexpr auto count_of_texcoord1_elements    = sizeof( decltype( texcoord1 )    ) / size_of_element;
          static constexpr auto count_of_texcoord2_elements    = sizeof( decltype( texcoord2 )    ) / size_of_element;
          static constexpr auto count_of_texcoord3_elements    = sizeof( decltype( texcoord3 )    ) / size_of_element;
          static constexpr auto count_of_texcoord4_elements    = sizeof( decltype( texcoord4 )    ) / size_of_element;
          static constexpr auto count_of_texcoord5_elements    = sizeof( decltype( texcoord5 )    ) / size_of_element;
          static constexpr auto count_of_texcoord6_elements    = sizeof( decltype( texcoord6 )    ) / size_of_element;
          static constexpr auto count_of_texcoord7_elements    = sizeof( decltype( texcoord7 )    ) / size_of_element;
          static constexpr auto count_of_normal_elements       = sizeof( decltype( normal )       ) / size_of_element;
          static constexpr auto count_of_tangent_elements      = sizeof( decltype( tangent )      ) / size_of_element;
          static constexpr auto count_of_bitangent_elements    = sizeof( decltype( bitangent )    ) / size_of_element;
          static constexpr auto count_of_bone_ids_elements     = sizeof( decltype( bone_ids )     ) / size_of_element;
          static constexpr auto count_of_bone_weights_elements = sizeof( decltype( bone_weights ) ) / size_of_element;

          static constexpr auto memory_offset_of_position     = std::size_t( 0 );
          static constexpr auto memory_offset_of_color        = memory_offset_of_position  + sizeof( decltype( position  ) );
          static constexpr auto memory_offset_of_texcoord0    = memory_offset_of_color     + sizeof( decltype( color     ) );
          static constexpr auto memory_offset_of_texcoord1    = memory_offset_of_texcoord0 + sizeof( decltype( texcoord0 ) );
          static constexpr auto memory_offset_of_texcoord2    = memory_offset_of_texcoord1 + sizeof( decltype( texcoord1 ) );
          static constexpr auto memory_offset_of_texcoord3    = memory_offset_of_texcoord2 + sizeof( decltype( texcoord2 ) );
          static constexpr auto memory_offset_of_texcoord4    = memory_offset_of_texcoord3 + sizeof( decltype( texcoord3 ) );
          static constexpr auto memory_offset_of_texcoord5    = memory_offset_of_texcoord4 + sizeof( decltype( texcoord4 ) );
          static constexpr auto memory_offset_of_texcoord6    = memory_offset_of_texcoord5 + sizeof( decltype( texcoord5 ) );
          static constexpr auto memory_offset_of_texcoord7    = memory_offset_of_texcoord6 + sizeof( decltype( texcoord6 ) );
          static constexpr auto memory_offset_of_normal       = memory_offset_of_texcoord7 + sizeof( decltype( texcoord7 ) );
          static constexpr auto memory_offset_of_tangent      = memory_offset_of_normal    + sizeof( decltype( normal    ) );
          static constexpr auto memory_offset_of_bitangent    = memory_offset_of_tangent   + sizeof( decltype( tangent   ) );
          static constexpr auto memory_offset_of_bone_ids     = memory_offset_of_bitangent + sizeof( decltype( bitangent ) );
          static constexpr auto memory_offset_of_bone_weights = memory_offset_of_bone_ids  + sizeof( decltype( bone_ids  ) );
        };

        class mesh_t
        {
          friend class node_t;

          glew::gl_type::GLuint  _triangle_vb_id;
          glew::gl_type::GLuint  _triangle_ib_id;
          glew::gl_type::GLsizei _count_of_indices;

          glew::gl_type::GLuint  _triangle_vao_id;

          const material_t&      _material;

          // ボーンデータ
          std::vector< glm::mat4 >& _bone_offsets;

          std::unordered_map< std::string, unsigned >& _bone_name_to_bone_index_mapping;

          const std::unordered_map< std::string, animation_t >& _animations;

          using vertices_buffer_t = std::vector< vertex_buffer_t >;
          using indices_buffer_t  = std::vector< glew::gl_type::GLuint >;

          auto initialize_prepare_buffers( const aiMesh* mesh, vertices_buffer_t& vb, indices_buffer_t& ib )
            -> void
          {

            constexpr auto indices_of_triangle = 3;

            for ( auto n_vertex = 0u; n_vertex < mesh -> mNumVertices; ++ n_vertex )
              vb.emplace_back
              ( std::move( helper::to_glm_vec4( mesh -> mVertices           + n_vertex ) )
              , std::move( mesh -> mColors[ 0 ]        ? helper::to_glm_vec4( mesh -> mColors[ 0 ]        + n_vertex ) : glm::vec4( std::nanf("") ) )
              , std::move( mesh -> mTextureCoords[ 0 ] ? helper::to_glm_vec2( mesh -> mTextureCoords[ 0 ] + n_vertex ) : glm::vec2( std::nanf("") ) )
              , std::move( mesh -> mTextureCoords[ 1 ] ? helper::to_glm_vec2( mesh -> mTextureCoords[ 1 ] + n_vertex ) : glm::vec2( std::nanf("") ) )
              , std::move( mesh -> mTextureCoords[ 2 ] ? helper::to_glm_vec2( mesh -> mTextureCoords[ 2 ] + n_vertex ) : glm::vec2( std::nanf("") ) )
              , std::move( mesh -> mTextureCoords[ 3 ] ? helper::to_glm_vec2( mesh -> mTextureCoords[ 3 ] + n_vertex ) : glm::vec2( std::nanf("") ) )
              , std::move( mesh -> mTextureCoords[ 4 ] ? helper::to_glm_vec2( mesh -> mTextureCoords[ 4 ] + n_vertex ) : glm::vec2( std::nanf("") ) )
              , std::move( mesh -> mTextureCoords[ 5 ] ? helper::to_glm_vec2( mesh -> mTextureCoords[ 5 ] + n_vertex ) : glm::vec2( std::nanf("") ) )
              , std::move( mesh -> mTextureCoords[ 6 ] ? helper::to_glm_vec2( mesh -> mTextureCoords[ 6 ] + n_vertex ) : glm::vec2( std::nanf("") ) )
              , std::move( mesh -> mTextureCoords[ 7 ] ? helper::to_glm_vec2( mesh -> mTextureCoords[ 7 ] + n_vertex ) : glm::vec2( std::nanf("") ) )
              , std::move( mesh -> mNormals            ? helper::to_glm_vec3( mesh -> mNormals            + n_vertex ) : glm::vec3( std::nanf("") ) )
              , std::move( mesh -> mTangents           ? helper::to_glm_vec3( mesh -> mTangents           + n_vertex ) : glm::vec3( std::nanf("") ) )
              , std::move( mesh -> mBitangents         ? helper::to_glm_vec3( mesh -> mBitangents         + n_vertex ) : glm::vec3( std::nanf("") ) )
              , std::move( glm::vec4( 0.0f ) )
              , std::move( glm::vec4( 0.0f ) )
              );

            for ( auto n_face = 0u; n_face < mesh -> mNumFaces; ++ n_face )
            {
              const auto face = mesh -> mFaces + n_face;

              if ( face -> mNumIndices not_eq indices_of_triangle )
                throw std::runtime_error( "required must be indices of face is 3. try create_model with aiProcess_Triangulate." );

              ib.emplace_back( std::move( face -> mIndices[0] ) );
              ib.emplace_back( std::move( face -> mIndices[1] ) );
              ib.emplace_back( std::move( face -> mIndices[2] ) );

            }

            _count_of_indices = ib.size();

          }

          auto initialize_animation_bone
          ( const aiMesh* mesh
          , vertices_buffer_t& vb
          )
            -> void
          {
            // アニメーション・ボーンまわり

            const auto bones = mesh -> mBones;

            for ( auto n_bone = 0u; n_bone < mesh -> mNumBones; ++n_bone )
            {
              const auto bone = bones[ n_bone ];

              const std::string bone_name( bone -> mName.C_Str() );

              unsigned bone_index = 0;

              if ( _bone_name_to_bone_index_mapping.find( bone_name ) == _bone_name_to_bone_index_mapping.end() )
              {
                bone_index = _bone_name_to_bone_index_mapping.size();
                _bone_offsets.push_back( glm::mat4( 1.0f ) );

                if ( _bone_offsets.size() > shader::max_bones )
                  throw std::runtime_error
                  ( "bone offset size " + std::to_string( _bone_offsets.size() )
                  + " over shader::max_bones " + std::to_string( shader::max_bones )
                  + "."
                  );
              }
              else
                bone_index = _bone_name_to_bone_index_mapping[ bone_name ];

              _bone_name_to_bone_index_mapping[ bone_name ] = bone_index;

              // TODO: もしかしたら .x 以外では bone_offset に transpose していると怪奇現象化するかも。要確認
              // pattern: .x is ok
              _bone_offsets[ bone_index ] = glm::transpose( helper::to_glm_mat4( bone -> mOffsetMatrix ) );
              //_bone_offsets[ bone_index ] = helper::to_glm_mat4( bone -> mOffsetMatrix );

              for ( auto n_weight = 0u; n_weight < bone -> mNumWeights; ++n_weight )
              {
                const auto& weight = bone -> mWeights[ n_weight ];

                auto& vertex = vb[ weight.mVertexId ];
                bool overflow_check = true;

                for ( auto n = 0; n < 4; ++n )
                  if ( vertex.bone_weights[ n ] == 0.0f )
                  {
                    vertex.bone_ids    [ n ] = bone_index;
                    vertex.bone_weights[ n ] = weight.mWeight;
                    overflow_check = false;
                    break;
                  }

                  if ( overflow_check )
                    throw std::runtime_error( "bone buffer is not enought, need limit data bone/vertex <= 4, or fix engine." );
              }
            }
          }

          auto initialize_generate_buffers( const vertices_buffer_t& vb, const indices_buffer_t& ib )
            -> void
          {
            // 三角群のバッファーを生成
            // http://www.opengl.org/sdk/docs/man/html/glGenVertexArrays.xhtml
            //  glew::gl_type::GLsizei n, glew::gl_type::GLuint* arrays
            glew::c::glGenVertexArrays( 1, &_triangle_vao_id );
            glew::test_error( __FILE__, __LINE__ );

            // http://www.opengl.org/sdk/docs/man/html/glBindVertexArray.xhtml
            //  glew::gl_type::GLuint array
            glew::c::glBindVertexArray( _triangle_vao_id );
            glew::test_error( __FILE__, __LINE__ );

            // http://www.opengl.org/sdk/docs/man/html/glGenBuffers.xhtml
            //  glew::gl_type::GLsizei n, glew::gl_type::GLuint* buffers
            glew::c::glGenBuffers( 1, &_triangle_vb_id);
            glew::test_error( __FILE__, __LINE__ );

            glew::c::glGenBuffers( 1, &_triangle_ib_id);
            glew::test_error( __FILE__, __LINE__ );

            // http://www.opengl.org/sdk/docs/man/html/glBindBuffer.xhtml
            //  GLenum target, glew::gl_type::GLuint buffer
            glew::c::glBindBuffer( GL_ARRAY_BUFFER, _triangle_vb_id );
            glew::test_error( __FILE__, __LINE__ );

            glew::c::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _triangle_ib_id );
            glew::test_error( __FILE__, __LINE__ );

            // http://www.opengl.org/sdk/docs/man/html/glBufferData.xhtml
            //  GLenum target, glew::gl_type::GLsizeiptr size, const GLvoid* data, GLenum usage
            glew::c::glBufferData
            ( GL_ARRAY_BUFFER
            , vb.size() * vertex_buffer_t::size_of_memory
            , vb.data() -> to_ptr<void>()
            , glew::gl_type::GLenum( GL_STATIC_DRAW )
            );
            glew::test_error( __FILE__, __LINE__ );

            glew::c::glBufferData
            ( GL_ELEMENT_ARRAY_BUFFER
            , ib.size() * sizeof( indices_buffer_t::value_type )
            , reinterpret_cast< const void* >( ib.data() )
            , glew::gl_type::GLenum( GL_STATIC_DRAW )
            );
            glew::test_error( __FILE__, __LINE__ );

            // 後始末
            glew::c::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
            glew::test_error( __FILE__, __LINE__ );

            glew::c::glBindBuffer( GL_ARRAY_BUFFER, 0 );
            glew::test_error( __FILE__, __LINE__ );

            glew::c::glBindVertexArray( 0 );
            glew::test_error( __FILE__, __LINE__ );
          }

        public:

          ~mesh_t()
          {
            // http://www.opengl.org/sdk/docs/man/html/glDeleteBuffers.xhtml
            //  glew::gl_type::GLsizei n, const glew::gl_type::GLuint buffers
            glew::c::glDeleteBuffers( 1, &_triangle_ib_id );
            glew::c::glDeleteBuffers( 1, &_triangle_vb_id );

            // http://www.opengl.org/sdk/docs/man/html/glDeleteVertexArrays.xhtml
            //  glew::gl_type::GLsizei n, const glew::gl_type::GLuint* arrays
            glew::c::glDeleteVertexArrays( 1, &_triangle_vao_id );
          }

          mesh_t
          ( const aiMesh* mesh
          , const std::vector<material_t>& materials_
          , std::vector< glm::mat4 >& bone_offsets
          , std::unordered_map< std::string, unsigned >& bone_name_to_bone_index_mapping
          , const std::unordered_map< std::string, animation_t >& animations
          )
            : _material( materials_.at( mesh -> mMaterialIndex ) )
            , _bone_offsets( bone_offsets )
            , _bone_name_to_bone_index_mapping( bone_name_to_bone_index_mapping )
            , _animations( animations )
          {
            vertices_buffer_t vb;
            indices_buffer_t ib;

            initialize_prepare_buffers ( mesh, vb, ib );
            initialize_animation_bone  ( mesh, vb     );
            initialize_generate_buffers(       vb, ib );
          }

          auto draw( const glew::gl_type::GLint program_id )
            -> void
          {
            // vertex transfar

            constexpr glew::gl_type::GLenum    attribute = GL_FLOAT;
            constexpr glew::gl_type::GLboolean normalize_on  = true;
            constexpr glew::gl_type::GLboolean normalize_off = false;

            const auto set_vertex_attribute = [ this, program_id ]
            {
              const auto location_of_vs_position     = glew::c::glGetAttribLocation( program_id, "position"     );
              const auto location_of_vs_color        = glew::c::glGetAttribLocation( program_id, "color"        );
              const auto location_of_vs_texcoord0    = glew::c::glGetAttribLocation( program_id, "texcoord0"    );
              const auto location_of_vs_texcoord1    = glew::c::glGetAttribLocation( program_id, "texcoord1"    );
              const auto location_of_vs_texcoord2    = glew::c::glGetAttribLocation( program_id, "texcoord2"    );
              const auto location_of_vs_texcoord3    = glew::c::glGetAttribLocation( program_id, "texcoord3"    );
              const auto location_of_vs_texcoord4    = glew::c::glGetAttribLocation( program_id, "texcoord4"    );
              const auto location_of_vs_texcoord5    = glew::c::glGetAttribLocation( program_id, "texcoord5"    );
              const auto location_of_vs_texcoord6    = glew::c::glGetAttribLocation( program_id, "texcoord6"    );
              const auto location_of_vs_texcoord7    = glew::c::glGetAttribLocation( program_id, "texcoord7"    );
              const auto location_of_vs_normal       = glew::c::glGetAttribLocation( program_id, "normal"       );
              const auto location_of_vs_tangent      = glew::c::glGetAttribLocation( program_id, "tangent"      );
              const auto location_of_vs_bitangent    = glew::c::glGetAttribLocation( program_id, "bitangent"    );
              const auto location_of_vs_bone_ids     = glew::c::glGetAttribLocation( program_id, "bone_ids"     );
              const auto location_of_vs_bone_weights = glew::c::glGetAttribLocation( program_id, "bone_weights" );

              if ( location_of_vs_position not_eq -1 )
              {
                // http://www.opengl.org/sdk/docs/man/html/glVertexAttribPointer.xhtml
                //  glew::gl_type::GLuint index, GLint size, GLenum type, GLboolean normalized, glew::gl_type::GLsizei stride, const GLvoid* pointer
                glew::c::glVertexAttribPointer( location_of_vs_position, vertex_buffer_t::count_of_position_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_position ) );
                // http://www.opengl.org/sdk/docs/man/html/glEnableVertexAttribArray.xhtml
                //  glew::gl_type::GLuint index
                glew::c::glEnableVertexAttribArray( location_of_vs_position );
              }

              if ( location_of_vs_color not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_color, vertex_buffer_t::count_of_color_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_color ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_color );
              }

              if ( location_of_vs_texcoord0 not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_texcoord0, vertex_buffer_t::count_of_texcoord0_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_texcoord0 ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_texcoord0 );
              }
              if ( location_of_vs_texcoord1 not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_texcoord1, vertex_buffer_t::count_of_texcoord1_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_texcoord1 ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_texcoord1 );
              }
              if ( location_of_vs_texcoord2 not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_texcoord2, vertex_buffer_t::count_of_texcoord2_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_texcoord2 ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_texcoord2 );
              }
              if ( location_of_vs_texcoord3 not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_texcoord3, vertex_buffer_t::count_of_texcoord3_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_texcoord3 ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_texcoord3 );
              }
              if ( location_of_vs_texcoord4 not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_texcoord4, vertex_buffer_t::count_of_texcoord4_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_texcoord4 ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_texcoord4 );
              }
              if ( location_of_vs_texcoord5 not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_texcoord5, vertex_buffer_t::count_of_texcoord5_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_texcoord5 ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_texcoord5 );
              }
              if ( location_of_vs_texcoord6 not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_texcoord6, vertex_buffer_t::count_of_texcoord6_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_texcoord6 ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_texcoord6 );
              }
              if ( location_of_vs_texcoord7 not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_texcoord7, vertex_buffer_t::count_of_texcoord7_elements, attribute, normalize_off, vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_texcoord7 ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_texcoord7 );
              }

              if ( location_of_vs_normal not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_normal  , vertex_buffer_t::count_of_normal_elements  , attribute, normalize_on , vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_normal   ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_normal   );
              }

              if ( location_of_vs_tangent not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_tangent , vertex_buffer_t::count_of_tangent_elements , attribute, normalize_off , vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_tangent  ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_tangent  );
              }

              if ( location_of_vs_bitangent not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_bitangent , vertex_buffer_t::count_of_bitangent_elements , attribute, normalize_off , vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_bitangent  ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_bitangent  );
              }

              if ( location_of_vs_bone_ids not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_bone_ids , vertex_buffer_t::count_of_bone_ids_elements , attribute, normalize_off , vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_bone_ids  ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_bone_ids  );
              }

              if ( location_of_vs_bone_weights not_eq -1 )
              {
                glew::c::glVertexAttribPointer( location_of_vs_bone_weights , vertex_buffer_t::count_of_bone_weights_elements , attribute, normalize_off , vertex_buffer_t::size_of_memory, reinterpret_cast<void*>( vertex_buffer_t::memory_offset_of_bone_weights  ) );
                glew::c::glEnableVertexAttribArray( location_of_vs_bone_weights  );
              }
            };

            // 三角群描画
            {
              glew::c::glBindVertexArray( _triangle_vao_id );
              WRP_GLEW_TEST_ERROR

              // http://www.opengl.org/sdk/docs/man/html/glBindBuffer.xhtml
              //  GLenum target, glew::gl_type::GLuint buffer
              glew::c::glBindBuffer( GL_ARRAY_BUFFER, _triangle_vb_id );
              WRP_GLEW_TEST_ERROR

              glew::c::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, _triangle_ib_id );
              WRP_GLEW_TEST_ERROR

              set_vertex_attribute();
              WRP_GLEW_TEST_ERROR

              // マテリアルの有効化
              auto materia_scopes = _material.draw( program_id );
              WRP_GLEW_TEST_ERROR

              // http://www.opengl.org/wiki/GLAPI/glDrawElements
              //  GLenum mode, GLsizei count, GLenum type, const GLvoid* indices
              glew::c::glDrawElements( GL_TRIANGLES, _count_of_indices, GL_UNSIGNED_INT, nullptr );
              WRP_GLEW_TEST_ERROR
            }

            // 後始末
            glew::c::glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
            glew::c::glBindBuffer( GL_ARRAY_BUFFER, 0 );
            glew::c::glBindVertexArray( 0 );
          }

        };
      }
    }
  }
}