text: #include <limits>
  #include <sstream>
  #include <iomanip>
  #include <algorithm>
  #include <cmath>
  
  #include "DensityCurve.h"
  #include "../utils/utils.h"
  #include "../utils/utilsImpl.h"
  #include "../utils/timeSeriesUtils.h"
  
  using namespace timeSeries;
  
  DensityCurve::DensityCurve()
  {
  }
  
  DensityCurve::~DensityCurve()
  {
  }
  
  void DensityCurve::getNormalized(std::vector<int> &rOutDensities){
      // ensure that it is clear
      rOutDensities.clear();
  
      // set output
      rOutDensities.assign(_normalized.begin(), _normalized.end());
  }
  
  void DensityCurve::getNormalizedInverted(std::vector<int> &rOutDensities){
      // ensure that it is clear
      rOutDensities.clear();
  
      // set output
      rOutDensities.assign(_inverted.begin(), _inverted.end());
  }
  
  void DensityCurve::saveCsv(const char *pInOutputFileName)
  {
      if (_density.size() == 0)
          exit(1);
  
      FILE *pOutputFile;
      pOutputFile = fopen(pInOutputFileName, "w");
  
      // write header
      fprintf(pOutputFile, "count\n");
      for (unsigned int density_curve_point = 0; density_curve_point < _density.size(); density_curve_point++)
      {
          fprintf(pOutputFile, "%d\n", _density[density_curve_point]);
      }
      fclose(pOutputFile);
  }
  
  void DensityCurve::setCurve(std::vector<double> &rInTimeSeries, std::vector<int> &rInDensityCurve, const int windowSize)
  {
      // assign original time series to keep track of samples and its values
      _timeSeries.assign(rInTimeSeries.begin(), rInTimeSeries.end());
  
      // assign density curve generated by some algorithm
      _density.assign(rInDensityCurve.begin(), rInDensityCurve.end());
  
      // normalize densities and generate an inverse density curve for better visualisation
      generateNormalizedAndInvert();
  
      // set this object window size to guarantee that the initial and ending positions will be ignored since they
      // mey have noise
      _windowSize = windowSize;
  }
  
  void DensityCurve::getJson(
          std::string &rOutTimeSeries, 
          std::string &rOutDensityString, 
          std::string &rOutNormalizedDensityString, 
          std::string &rOutInvertedDensityString)
  {
      if (_density.size() == 0)
          exit(1);
  
      // guarantee that it will be clear
      rOutTimeSeries.clear();
      rOutDensityString.clear();
      rOutNormalizedDensityString.clear();
      rOutInvertedDensityString.clear();
  
      // string stream to handle the json creation
      std::ostringstream time_series_stream;
      std::ostringstream density_stream;
      std::ostringstream normazied_stream;
      std::ostringstream inverted_stream;
      time_series_stream << "[[\"point\", \"value\"],";
      density_stream << "[[\"point\", \"value\"],";
      normazied_stream << "[[\"point\", \"value\"],";
      inverted_stream << "[[\"point\", \"value\"],";
      for (int i = 0; i < _density.size() - 1; i++)
      {
          int density_cv_val = 0;
          double time_s_val = _timeSeries[i];
          double normalized_density_cv_val = 0;
          double inverted_density_cv_val = 0;
          if (i < _windowSize * 2 || i >= _density.size() - 1 - _windowSize )
          {
              density_cv_val = 0;
              normalized_density_cv_val = 0;
              inverted_density_cv_val = 0;
          }
          else
          {
              density_cv_val = _density[i];
              normalized_density_cv_val = _normalized[i];
              inverted_density_cv_val = _inverted[i];
          }
  
          time_series_stream << "[" << i + 1 << ", " << time_s_val << "],";
          density_stream << "[" << i + 1 << ", " << density_cv_val << "],";
          normazied_stream << "[" << i + 1 << ", " << normalized_density_cv_val << "],";
          inverted_stream << "[" << i + 1 << ", " << inverted_density_cv_val << "],";
      }
      time_series_stream << "[" << _timeSeries.size() << ", " << _timeSeries[_timeSeries.size() - 1] << "]]";
      density_stream << "[" << _density.size() << ", " << 0 << "]]";
      normazied_stream << "[" << _normalized.size() << ", " << 0 << "]]";
      inverted_stream << "[" << _inverted.size() << ", " << 0 << "]]";
  
      // set the output json string
      rOutTimeSeries.assign(time_series_stream.str());
      rOutDensityString.assign(density_stream.str());
      rOutNormalizedDensityString.assign(normazied_stream.str());
      rOutInvertedDensityString.assign(inverted_stream.str());
  }
  
  void DensityCurve::generateNormalizedAndInvert()
  {
      // series max value
      double max = (double)*std::max_element(_density.begin(), _density.end());
  
      // series min value
      double min = (double)*std::min_element(_density.begin(), _density.end());
      double denominator = max - min;
  
      // create normed vec
      _normalized = std::vector<double>(_density.size(), 0.0);
      _inverted = std::vector<double>(_density.size(), 0.0);
  
      for (int i = 0; i < _density.size(); i++)
      {
          // normalized element
          double normed_element = ((double)_density[i] - min) / denominator;
          _normalized[i] = normed_element;
  
          // invert element
          double inverted_element = 1.0 - normed_element;
          _inverted[i] = inverted_element;
      }
  }