// ======================================================================
/*!
 * \file
 * \brief Implementation of namespace Imagine::NFmiColorReduce
 */
// ======================================================================
/*!
 * \namespace Imagine::NFmiColorReduce
 *
 * \brief Tools for reducing the number of colors in an image
 *
 * The main idea in reducing colors in an image is to make it
 * smaller so that the image can be downloaded faster. Typically
 * one sets a maximum number of allowed colors, but often a better
 * alternative might be to reduce colors until the error would become
 * too great.
 *
 */
// ======================================================================

#include "NFmiColorReduce.h"
#include "NFmiColorTools.h"
#include "NFmiImage.h"
#include <macgyver/Exception.h>

#include <boost/shared_ptr.hpp>

#include <iomanip>
#include <map>
#include <vector>

// std version not available in RHEL5, must use boost instead

#include <boost/unordered_map.hpp>
using namespace std;

// The actual public interfaces

namespace Imagine
{
// ======================================================================
// Local subroutines
// ======================================================================

namespace
{
//! Histogram data
struct ColorInfo
{
  ColorInfo(NFmiColorTools::Color c) : color(c), count(0), keeper(false) {}
  NFmiColorTools::Color color;
  int count;
  bool keeper;

  ColorInfo& operator++()
  {
    ++count;
    return *this;
  }
  void keep() { keeper = true; }
};

//! Histogram information
typedef boost::unordered_map<NFmiColorTools::Color, ColorInfo> Counter;

//! Internal histogram
typedef std::vector<ColorInfo> ColorHistogram;

//! Colormap transformation
typedef map<NFmiColorTools::Color, NFmiColorTools::Color> ColorMap;

// ----------------------------------------------------------------------
/*!
 * \brief Initialize the given gamma correction table
 */
// ----------------------------------------------------------------------

void init_gamma_table(vector<float>& theTable, bool& theFlag)
{
  try
  {
    const float gamma = 2.2f;
    const float coeff = 255 / (pow(255.f, gamma));

    for (int i = 0; i < 256; i++)
      theTable[i] = coeff * pow(static_cast<float>(i), gamma);

    theFlag = true;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Calculate the occurrance count of each color in the given image
 *
 * \param theImage The image
 * \return The colormap with occurrance counts
 */
// ----------------------------------------------------------------------

Counter calc_counts(const NFmiImage& theImage)
{
  try
  {
    // The default bucket size in SGI is 100, which is quite
    // small for the typical number of colours we encounter
    // in images.

#ifdef UNIX
    Counter counter(4096);
#else  // #ifdef _MSC_VER  // MSVisualC++ k��nt�j�n mukana tulleessa hash_map -koodissa
    // ei ollut buckettien m��r�n s��t� mahdollisuutta.
    Counter counter;
#endif

    // Safety check

    if (theImage.Height() * theImage.Width() == 0)
      return counter;

    // Insert the first color so that we can initialize the iterator cache
    // Note that we insert count 0, but the first loop will fix the number

    counter.insert(Counter::value_type(theImage(0, 0), ColorInfo(theImage(0, 0))));

    Counter::iterator last1 = counter.begin();
    Counter::iterator last2 = counter.begin();

    for (int j = 0; j < theImage.Height(); j++)
    {
      for (int i = 0; i < theImage.Width(); i++)
      {
        NFmiColorTools::Color color = theImage(i, j);

        if (last1->first == color)
        {
          ++last1->second;
          // test if the color is the same in a 3x3 box and is hence a new color to be kept
          if (!last1->second.keeper && i > 1 && j > 1)
          {
            // Note: theImage(i-1,j) is already known to have the same color (last1 points to it)
            if (theImage(i - 2, j) == color && theImage(i, j - 1) == color &&
                theImage(i - 1, j - 1) == color && theImage(i - 2, j - 1) == color &&
                theImage(i, j - 2) == color && theImage(i - 1, j - 2) == color &&
                theImage(i - 2, j - 2) == color)
            {
              last1->second.keep();
            }
          }
        }
        else if (last2->first == color)
        {
          ++last2->second;
          swap(last1, last2);
        }
        else
        {
          pair<Counter::iterator, bool> result =
              counter.insert(Counter::value_type(color, ColorInfo(color)));
          last2 = last1;
          last1 = result.first;
          ++last1->second;
        }
      }
    }
    return counter;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief A near-tree of colors and their occurrance counts
 *
 * The idea is to feed in colors in the order of their popularity
 * and then use the near-tree maximum distance information
 * to group colors for color reduction.
 */
// ----------------------------------------------------------------------

class ColorTree
{
 public:
  typedef NFmiColorTools::Color value_type;

  ColorTree();
  void insert(value_type theColor);
  int size() const;
  void clear();
  bool empty() const;
  value_type nearest(value_type theColor);

  static float distance(value_type theColor1, value_type theColor2);

 private:
  ColorTree(const ColorTree& theTree);
  ColorTree& operator=(const ColorTree& theTree);

  bool nearest(value_type theColor, value_type& theNearest, float& theRadius) const;

  boost::shared_ptr<value_type> itsLeftObject;
  boost::shared_ptr<value_type> itsRightObject;
  float itsMaxLeft;
  float itsMaxRight;
  boost::shared_ptr<ColorTree> itsLeftBranch;
  boost::shared_ptr<ColorTree> itsRightBranch;
  int itsCount;
};

// ----------------------------------------------------------------------
/*!
 * \brief Default constructor
 */
// ----------------------------------------------------------------------

ColorTree::ColorTree()
    : itsLeftObject(),
      itsRightObject(),
      itsMaxLeft(-1.0),
      itsMaxRight(-1.0),
      itsLeftBranch(),
      itsRightBranch(),
      itsCount(0)
{
}

// ----------------------------------------------------------------------
/*!
 * \brief Euclidian distance between two colors
 *
 * See http://www.compuphase.com/cmetric.htm
 */
// ----------------------------------------------------------------------

float ColorTree::distance(ColorTree::value_type theColor1, ColorTree::value_type theColor2)
{
  try
  {
    static vector<float> gamma(256, 0);
    static bool initialized = false;

    if (!initialized)
      init_gamma_table(gamma, initialized);

    const float r =
        (gamma[NFmiColorTools::GetRed(theColor1)] - gamma[NFmiColorTools::GetRed(theColor2)]);
    const float g =
        (gamma[NFmiColorTools::GetGreen(theColor1)] - gamma[NFmiColorTools::GetGreen(theColor2)]);
    const float b =
        (gamma[NFmiColorTools::GetBlue(theColor1)] - gamma[NFmiColorTools::GetBlue(theColor2)]);
    const float a = static_cast<float>(NFmiColorTools::GetAlpha(theColor1) -
                                       NFmiColorTools::GetAlpha(theColor2));

    return sqrt(3.0f * r * r + 4.0f * g * g + 2.0f * b * b + a * a);
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Test whether the tree is empty
 */
// ----------------------------------------------------------------------

bool ColorTree::empty() const
{
  try
  {
    return (itsLeftObject.get() == 0);
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}
// ----------------------------------------------------------------------
/*!
 * \brief Return number of colours in the tree.
 */
// ----------------------------------------------------------------------

int ColorTree::size() const
{
  return itsCount;
}

void ColorTree::clear()
{
  try
  {
    itsRightBranch.reset(new ColorTree);
    itsLeftBranch.reset(new ColorTree);
    itsCount = 0;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Insert a color into the color tree
 *
 * Algorithm:
 *
 *  -# If left position is free, put the color there
 *  -# If right position is free, put the color there
 *  -# Insert into subtree whose root is closer to the color
 */
// ----------------------------------------------------------------------

void ColorTree::insert(ColorTree::value_type theColor)
{
  try
  {
    if (itsLeftObject.get() == 0)
      itsLeftObject.reset(new value_type(theColor));

    else if (itsRightObject.get() == 0)
      itsRightObject.reset(new value_type(theColor));

    else
    {
      const float dist_left = distance(theColor, *itsLeftObject);
      const float dist_right = distance(theColor, *itsRightObject);

      if (dist_left > dist_right)
      {
        if (itsRightBranch.get() == 0)
          itsRightBranch.reset(new ColorTree);

        // note that constructor sets itsMaxRight to be negative

        itsMaxRight = max(itsMaxRight, dist_right);

        itsRightBranch->insert(theColor);
        itsCount++;
      }
      else
      {
        if (itsLeftBranch.get() == 0)
          itsLeftBranch.reset(new ColorTree);

        // note that constructor sets itsMaxLeft to be negative

        itsMaxLeft = max(itsMaxLeft, dist_left);

        itsLeftBranch->insert(theColor);
        itsCount++;
      }
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Find the nearest color
 *
 * \param theColor The color for which to find the nearest color
 * \return The nearest color
 */
// ----------------------------------------------------------------------

ColorTree::value_type ColorTree::nearest(ColorTree::value_type theColor)
{
  try
  {
    value_type bestcolor;
    float radius = -1;
    if (!nearest(theColor, bestcolor, radius))
      throw Fmi::Exception(BCP, "Invalid use of color reduction tables");

    return bestcolor;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Recursively find nearest color
 *
 * This is only intended to be used by the public nearest method
 */
// ----------------------------------------------------------------------

bool ColorTree::nearest(ColorTree::value_type theColor,
                        ColorTree::value_type& theNearest,
                        float& theRadius) const
{
  try
  {
    float left_dist = -1;
    float right_dist = -1;
    bool found = false;

    // first test each of the left and right positions to see if
    // one holds a color nearer than the nearest so far discovered

    if (itsLeftObject.get() != 0)
    {
      left_dist = distance(theColor, *itsLeftObject);
      if (theRadius < 0 || left_dist <= theRadius)
      {
        theRadius = left_dist;
        theNearest = *itsLeftObject;
        found = true;
      }
    }

    if (itsRightObject.get() != 0)
    {
      right_dist = distance(theColor, *itsRightObject);
      if (theRadius < 0 || right_dist <= theRadius)
      {
        theRadius = right_dist;
        theNearest = *itsRightObject;
        found = true;
      }
    }

    // if theRadius is negative at this point, the tree is empty
    // on the other hand, if the radius is zero, we found a match

    if (theRadius <= 0)
      return found;

    // Now we test to see if the branches below might hold an object
    // nearer than the best so far found. The triangle rule is used
    // to test whether it's even necessary to descend. We may be
    // able to skip skanning both branches if we can guess which
    // branch is most likely to contain the nearest match. We simply
    // guess, that it is the branch which is nearer. Note that
    // the first and third if-clauses are mutually exclusive,
    // hence only parts 1-2 or 2-3 will be executed.

    const bool left_closer = (left_dist < right_dist);

    if (!left_closer && (itsRightBranch.get() != 0) && ((theRadius + itsMaxRight) >= right_dist))
      found |= itsRightBranch->nearest(theColor, theNearest, theRadius);

    if ((itsLeftBranch.get() != 0) && ((theRadius + itsMaxLeft) >= left_dist))
      found |= itsLeftBranch->nearest(theColor, theNearest, theRadius);

    if (left_closer && (itsRightBranch.get() != 0) && ((theRadius + itsMaxRight) >= right_dist))
      found |= itsRightBranch->nearest(theColor, theNearest, theRadius);

    return found;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Perform color replacement
 */
// ----------------------------------------------------------------------

void replace_colors(NFmiImage& theImage, const ColorMap& theMap)
{
  try
  {
    boost::unordered_map<NFmiColorTools::Color, NFmiColorTools::Color> colormap(256);

    for (ColorMap::const_iterator it = theMap.begin(); it != theMap.end(); ++it)
    {
      colormap[it->first] = it->second;
    }

    NFmiColorTools::Color last_color1 = NFmiColorTools::NoColor;
    NFmiColorTools::Color last_choice1 = NFmiColorTools::NoColor;
    NFmiColorTools::Color last_color2 = NFmiColorTools::NoColor;
    NFmiColorTools::Color last_choice2 = NFmiColorTools::NoColor;

    for (int j = 0; j < theImage.Height(); j++)
    {
      for (int i = 0; i < theImage.Width(); i++)
      {
        if (theImage(i, j) == last_color1)
          theImage(i, j) = last_choice1;
        else if (theImage(i, j) == last_color2)
        {
          theImage(i, j) = last_choice2;
          swap(last_color1, last_color2);
          swap(last_choice1, last_choice2);
        }
        else
        {
          last_color2 = last_color1;
          last_choice2 = last_choice1;
          last_color1 = theImage(i, j);
          last_choice1 = colormap[theImage(i, j)];
          theImage(i, j) = last_choice1;
        }
      }
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Build a color tree and a colormap
 */
// ----------------------------------------------------------------------

void build_tree(const NFmiImage& theImage,
                const ColorHistogram& theHistogram,
                ColorTree& theTree,
                ColorMap& theMap,
                float theQuality)
{
  try
  {
    const float ratio = static_cast<float>(1.0 / (theImage.Width() * theImage.Height()));
    const float factor = -theQuality / log(10.0f);

    for (ColorHistogram::const_iterator it = theHistogram.begin(); it != theHistogram.end(); ++it)
    {
      if (theTree.empty() || it->keeper)
      {
        theTree.insert(it->color);
        theMap.insert(ColorMap::value_type(it->color, it->color));
      }
      else
      {
        NFmiColorTools::Color nearest = theTree.nearest(it->color);
        float dist = theTree.distance(nearest, it->color);

        float limit = factor * log(ratio * it->count);

        if (dist < limit)
        {
          theMap.insert(ColorMap::value_type(it->color, nearest));
        }
        else
        {
          theTree.insert(it->color);
          theMap.insert(ColorMap::value_type(it->color, it->color));
        }
      }
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Build a color tree and a colormap
 */
// ----------------------------------------------------------------------

void build_tree(const NFmiImage& theImage,
                const ColorHistogram& theHistogram,
                ColorTree& theTree,
                ColorMap& theMap,
                float theQuality,
                int theMaxCount,
                float theErrorFactor)
{
  try
  {
    const float ratio = static_cast<float>(1.0 / (theImage.Width() * theImage.Height()));

    bool done = false;
    while (!done)
    {
      done = true;
      const float factor = -theQuality / log(10.0f);

      for (ColorHistogram::const_iterator it = theHistogram.begin(); it != theHistogram.end(); ++it)
      {
        if (theTree.empty() || it->keeper)
        {
          theTree.insert(it->color);
          theMap.insert(ColorMap::value_type(it->color, it->color));
        }
        else
        {
          NFmiColorTools::Color nearest = theTree.nearest(it->color);
          float dist = theTree.distance(nearest, it->color);

          const float limit = factor * log(ratio * it->count);
          if (dist < limit)
            theMap.insert(ColorMap::value_type(it->color, nearest));
          else if (theTree.size() < theMaxCount)
          {
            theTree.insert(it->color);
            theMap.insert(ColorMap::value_type(it->color, it->color));
          }
          else
          {
            theTree.clear();
            theMap.clear();
            done = false;
            theQuality *= theErrorFactor;
            break;
          }
        }
      }
    }
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

bool colorcmp(const ColorInfo& c1, const ColorInfo& c2)
{
  try
  {
    if (c1.keeper == c2.keeper)
      return (c2.count < c1.count);

    return c1.keeper;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Calculate the histogram for color reduction
 *
 * \param theImage The image
 * \return The histogram object
 */
// ----------------------------------------------------------------------

const ColorHistogram CalcColorHistogram(const NFmiImage& theImage)
{
  try
  {
    Counter counter = calc_counts(theImage);

    ColorHistogram histogram;

    Counter::const_iterator end = counter.end();
    for (Counter::const_iterator it = counter.begin(); it != end; ++it)
    {
      histogram.push_back(it->second);
    }
    sort(histogram.begin(), histogram.end(), &colorcmp);

    return histogram;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

}  // namespace

// ======================================================================
// The actual public interfaces
// ======================================================================

namespace NFmiColorReduce
{
// ----------------------------------------------------------------------
/*!
 * \brief Calculate the occurrance count of each color in the given image
 *
 * \param theImage The image
 * \return The histogram object
 */
// ----------------------------------------------------------------------

const Histogram CalcHistogram(const NFmiImage& theImage)
{
  try
  {
    Counter counter = calc_counts(theImage);

    NFmiColorReduce::Histogram histogram;

    Counter::const_iterator end = counter.end();
    for (Counter::const_iterator it = counter.begin(); it != end; ++it)
    {
      histogram.insert(NFmiColorReduce::Histogram::value_type(it->second.count, it->first));
    }

    return histogram;
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

// ----------------------------------------------------------------------
/*!
 * \brief Reduce colors from the image adaptively
 *
 * Algorithm:
 *
 *  -# Calculate the histogram
 *  -# In order of popularity, feed the colors into a color tree
 *     which discards colors which are close enough to a color
 *     chosen earlier.
 *  -# For each color, ask the color tree which is the nearest
 *     color for it.
 *  -# Replace the original colors with the colors found in the previous
 *     step.
 *
 * Note that this is very very close to the common popularity based
 * algorithm. However, the twist is that we have an adaptive error
 * criteria, we are not aiming for a fixed number of colors. Hence
 * we get the benefit of the speed of the popularity based algorithm
 * but will never get too big errors.
 *
 * An algorithm which might produce better quality results would
 * be to aim for color diversity as follows:
 *
 *  -# Perform the insertion phase as in algorithm 1, but
 *     with the error limit doubled.
 *  -# Perform the insertion phase as in algorithm 1, now
 *     with a normal error limit
 *  -# Continue by asking the nearest color for all colors
 *
 * However, we require best possible speed and thus do everything
 * in a single pass. What we could do is to find a way to quickly
 * reorder the histogram to produce more diversity in the colors.
 *
 * \param theImage The image to modify
 * \param theQuality The quality ratio, 10 = good, 20 = poor and so on
 */
// ----------------------------------------------------------------------

void AdaptiveReduce(NFmiImage& theImage, float theQuality)
{
  try
  {
    using namespace Imagine::NFmiColorTools;

    // A sanity check on the maximum error

    if (theQuality < 1)
      return;

    // Calculate the histogram

    ColorHistogram histogram = CalcColorHistogram(theImage);

    // Select the colors

    ColorTree tree;
    ColorMap colormap;

    build_tree(theImage, histogram, tree, colormap, theQuality);

    // Now perform the replacements. Since we expect to encounter
    // sequences of color, we speed of the searches by caching
    // the last replacement.

    replace_colors(theImage, colormap);
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

void AdaptiveReduce(NFmiImage& theImage, float theQuality, int theMaxColors, float theErrorFactor)
{
  try
  {
    using namespace Imagine::NFmiColorTools;

    if (theQuality < 1)
      throw Fmi::Exception(BCP, "Quality was too low.");

    // Calculate the histogram

    ColorHistogram histogram = CalcColorHistogram(theImage);

    // Select the colors

    ColorTree tree;
    ColorMap colormap;

    build_tree(theImage, histogram, tree, colormap, theQuality, theMaxColors, theErrorFactor);

    // Now perform the replacements. Since we expect to encounter
    // sequences of color, we speed of the searches by caching
    // the last replacement.

    replace_colors(theImage, colormap);
  }
  catch (...)
  {
    throw Fmi::Exception::Trace(BCP, "Operation failed!");
  }
}

}  // namespace NFmiColorReduce
}  // namespace Imagine

// ======================================================================


#include <iostream>
#include <cstdio>
#include <vector>
#include <boost/python.hpp>
#include <boost/python/numpy.hpp>
#include <numpy/ndarrayobject.h>
#include "boost/tuple/tuple.hpp"
#include "boost/python/object.hpp"
#include <../include/API.h>

namespace bpn = boost::python::numpy;
namespace bp =  boost::python;

typedef boost::tuple< std::vector< std::vector<uint32_t> >, std::vector<uint32_t> > Custom_tuple;

struct VecToArray
{//converts a vector<uint32_t> to a numpy array
    static PyObject * convert(const std::vector<uint32_t> & vec) {
    npy_intp dims = vec.size();
    PyObject * obj = PyArray_SimpleNew(1, &dims, NPY_UINT32);
    void * arr_data = PyArray_DATA((PyArrayObject*)obj);
    memcpy(arr_data, &vec[0], dims * sizeof(uint32_t));
    return obj;
    }
};

struct FloatVecToArray
{//converts a vector<uint32_t> to a numpy array
    static PyObject * convert(const std::vector<float> & vec, uint32_t n_ver, uint32_t n_obs) {
    npy_intp dims[2];
    dims[0] = (npy_intp)n_ver;
    dims[1] = (npy_intp)n_obs;
    PyObject * obj = PyArray_SimpleNew(2, dims, NPY_FLOAT32);
    void * arr_data = PyArray_DATA((PyArrayObject*)obj);
    memcpy(arr_data, &vec[0], n_ver * n_obs * sizeof(float));
    return obj;
    }
};

template<class T>
struct VecvecToList
{//converts a vector< vector<T> > to a list
        static PyObject* convert(const std::vector< std::vector<T> > & vecvec)
    {
        boost::python::list* pylistlist = new boost::python::list();
        for(size_t i = 0; i < vecvec.size(); i++)
        {
            boost::python::list* pylist = new boost::python::list();
            for(size_t j = 0; j < vecvec[i].size(); j++)
            {
                pylist->append(vecvec[i][j]);
            }
            pylistlist->append((pylist, pylist[0]));
        }
        return pylistlist->ptr();
    }
};

struct to_py_tuple
{//converts output to a python tuple
    static PyObject* convert(const Custom_tuple& c_tuple){
        bp::list values;
        //add all c_tuple items to "values" list

        PyObject * vecvec_pyo = VecvecToList<uint32_t>::convert(c_tuple.get<0>());
        PyObject * vec_pyo = VecToArray::convert(c_tuple.get<1>());

        values.append(bp::handle<>(bp::borrowed(vecvec_pyo)));
        values.append(bp::handle<>(bp::borrowed(vec_pyo)));

        return bp::incref( bp::tuple( values ).ptr() );
    }
};

PyObject * cutpursuit_seg(const bpn::ndarray & obs, const bpn::ndarray & source, const bpn::ndarray & target,const bpn::ndarray & edge_weight,  float lambda)
{//read data and run the L0-cut pursuit partition algorithm
    const uint32_t n_ver = bp::len(obs);
    const uint32_t n_edg = bp::len(source);
    const uint32_t n_obs = bp::len(obs[0]);
    const float * obs_data = reinterpret_cast<float*>(obs.get_data());
    const uint32_t * source_data = reinterpret_cast<uint32_t*>(source.get_data());
    const uint32_t * target_data = reinterpret_cast<uint32_t*>(target.get_data());
    const float * edge_weight_data = reinterpret_cast<float*>(edge_weight.get_data());
//    float solution [n_ver * n_obs];
    std::vector<float> solution(n_ver * n_obs, 0.0f);
    std::vector<float> node_weight(n_ver, 1.0f);
    std::vector<uint32_t> in_component(n_ver,0);
    std::vector< std::vector<uint32_t> > components(1,std::vector<uint32_t>(1,0.f));
    CP::cut_pursuit<float>(n_ver, n_edg, n_obs, obs_data, source_data, target_data, edge_weight_data, &node_weight[0]
             , solution.data(), in_component, components, lambda, 1.f, 2.f, 2.f);
//    delete[]solution
    return to_py_tuple::convert(Custom_tuple(components, in_component));
}

PyObject * cutpursuit_reg(const bpn::ndarray & obs, const bpn::ndarray & source, const bpn::ndarray & target,const bpn::ndarray & edge_weight,  float lambda)
{//read data and run the L0-cut pursuit partition algorithm
    const uint32_t n_ver = bp::len(obs);
    const uint32_t n_edg = bp::len(source);
    const uint32_t n_obs = bp::len(obs[0]);
    const float * obs_data = reinterpret_cast<float*>(obs.get_data());
    const uint32_t * source_data = reinterpret_cast<uint32_t*>(source.get_data());
    const uint32_t * target_data = reinterpret_cast<uint32_t*>(target.get_data());
    const float * edge_weight_data = reinterpret_cast<float*>(edge_weight.get_data());
//    float solution [n_ver * n_obs];
    std::cout << n_ver << " " << n_obs << std::endl;
    std::vector<float> solution(n_ver * n_obs, 0.0f);
    std::vector<float> node_weight(n_ver, 1.0f);
    std::vector< std::vector<uint32_t> > components(1,std::vector<uint32_t>(1,0.f));
    CP::cut_pursuit<float>(n_ver, n_edg, n_obs, obs_data, source_data, target_data, edge_weight_data, &node_weight[0]
             , solution.data(), lambda, 1.f, 2.f, 2.f);
//    delete[]solution
    return FloatVecToArray::convert(solution, n_ver, n_obs);
}

BOOST_PYTHON_MODULE(libcp)
{
    _import_array();
    Py_Initialize();
    bpn::initialize();
    bp::to_python_converter< Custom_tuple, to_py_tuple>();
    def("cutpursuit", cutpursuit_seg);
    def("cutpursuit_reg", cutpursuit_reg);
}


#include <cstring>
#include <fstream>
#include <unistd.h>

#include <NTL/ZZX.h>
#include <NTL/vector.h>

#include "FHE.h"
#include "timing.h"
#include "EncryptedArray.h"

Ctxt FHE_Add(Ctxt Ea, Ctxt Eb)
{
    Ctxt ctSum = Ea;
    ctSum += Eb;
    return ctSum;
}

int main(int argc, char *argv[]) {
    ArgMapping amap;

    string owner = "owner";
    string firstCtxtFileName = "data/candidate/owner-0.txt";
    string secondCtxtFileName = "data/candidate/owner-1.txt";
    string resultCtxtFileName = "data/owner-result.txt";

    amap.arg("o", owner, "owner's address");
    amap.arg("f", firstCtxtFileName, "first Ctxt file's name");
    amap.arg("s", secondCtxtFileName, "second Ctxt file's name");
    amap.arg("r", resultCtxtFileName, "result Ctxt file's name");
    amap.parse(argc, argv);

    // file names
    const string secretKeyBinaryFileName = "data/secretKey/" + owner + ".bin";

    ifstream secretBinFile(secretKeyBinaryFileName.c_str(), ios::binary);
    ifstream firstCtxtFile(firstCtxtFileName.c_str(), ios::binary);
    ifstream secondCtxtFile(secondCtxtFileName.c_str(), ios::binary);
    ofstream resultCtxtFile(resultCtxtFileName.c_str(), ios::binary);

    // check open file
    assert(secretBinFile.is_open());
    assert(firstCtxtFile.is_open());
    assert(secondCtxtFile.is_open());
    assert(resultCtxtFile.is_open());

    // Read in context,
    std::unique_ptr<FHEcontext> context = buildContextFromBinary(secretBinFile);
    readContextBinary(secretBinFile, *context);

    // Read in SecKey and PubKey.
    // Got to insert pubKey into seckey obj first.
    std::unique_ptr<FHESecKey> secKey(new FHESecKey(*context));
    FHEPubKey *pubKey = (FHEPubKey *) secKey.get();

    // read publicKey
    readPubKeyBinary(secretBinFile, *pubKey);
    readSecKeyBinary(secretBinFile, *secKey);

    secretBinFile.close();

    cout << "read PublicKey & SecretKey successful.\n" << flush;

    // ready to add two Ctxts
    Ctxt firstCtxt(*pubKey);
    Ctxt secondCtxt(*pubKey);

    // get Ctxt to file
    firstCtxtFile >> firstCtxt;
    secondCtxtFile >> secondCtxt;

    // add two Ctxt
    Ctxt resultCtxt = FHE_Add(firstCtxt, secondCtxt);

    // show result
    ZZX ptSum;
    secKey->Decrypt(ptSum, resultCtxt);
    cout << "result : " << ptSum <<endl;

    // save resultCtxt
    resultCtxtFile << resultCtxt << endl;
}


//---------------------------------------------------------------------------//
//!
//! \file   MonteCarlo_PairProductionPhotoatomicReaction.hpp
//! \author Alex Robinson
//! \brief  The pair production photoatomic reaction class decl.
//!
//---------------------------------------------------------------------------//

#ifndef MONTE_CARLO_PAIR_PRODUCTION_PHOTOATOMIC_REACTION_HPP
#define MONTE_CARLO_PAIR_PRODUCTION_PHOTOATOMIC_REACTION_HPP

// Boost Includes
#include <boost/function.hpp>

// Trilinos Includes
#include <Teuchos_RCP.hpp>

// FRENSIE Includes
#include "MonteCarlo_StandardPhotoatomicReaction.hpp"

namespace MonteCarlo{

//! The pair production photoatomic reaction class
template<typename InterpPolicy, bool processed_cross_section = true>
class PairProductionPhotoatomicReaction : public StandardPhotoatomicReaction<InterpPolicy,processed_cross_section>
{

public:

  //! Basic constructor
  PairProductionPhotoatomicReaction(
		const Teuchos::ArrayRCP<const double>& incoming_energy_grid,
		const Teuchos::ArrayRCP<const double>& cross_section,
		const unsigned threshold_energy_index,
		const bool use_detailed_electron_emission_physics = true );

  //! Constructor
  PairProductionPhotoatomicReaction(
       const Teuchos::ArrayRCP<const double>& incoming_energy_grid,
       const Teuchos::ArrayRCP<const double>& cross_section,
       const unsigned threshold_energy_index,
       const Teuchos::RCP<const Utility::HashBasedGridSearcher>& grid_searcher,
       const bool use_detailed_electron_emission_physics = true );

  //! Destructor
  ~PairProductionPhotoatomicReaction()
  { /* ... */ }

  //! Return the number of photons emitted from the rxn at the given energy
  unsigned getNumberOfEmittedPhotons( const double energy ) const;

  //! Return the reaction type
  PhotoatomicReactionType getReactionType() const;

  //! Simulate the reaction
  void react( PhotonState& photon,
	      ParticleBank& bank,
	      SubshellType& shell_of_interaction ) const;

private:

  // The basic pair production model
  static void basicInteraction( PhotonState& photon,
				ParticleBank& bank );

  // The detailed pair production model
  static void detailedInteraction( PhotonState& photon,
				   ParticleBank& bank );

  // The number of photons emitted from pair production using simple model
  static unsigned basicInteractionPhotonEmission();

  // The number of photons emitted from pair production using detailed model
  static unsigned detailedInteractionPhotonEmission();

  // The pair production model
  boost::function<void (PhotonState&,ParticleBank&)> d_interaction_model;

  // The number of photons emitted from the interaction (model dependent)
  boost::function<unsigned (void)> d_interaction_model_emission;
};

} // end MonteCarlo namespace

//---------------------------------------------------------------------------//
// Template Includes.
//---------------------------------------------------------------------------//

#include "MonteCarlo_PairProductionPhotoatomicReaction_def.hpp"

//---------------------------------------------------------------------------//

#endif // end MONTE_CARLO_PAIR_PRODUCTION_PHOTOATOMIC_REACTION_HPP

//---------------------------------------------------------------------------//
// end MonteCarlo_PairProductionPhotoatomicReaction.hpp
//---------------------------------------------------------------------------//


/*
 * MIT License
 *
 * Alexandria.org
 *
 * Copyright (c) 2021 Josef Cullhed, <info@alexandria.org>, et al.
 *
 * 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.
 */

#include "Counter.h"

#include <iostream>
#include <future>
#include <vector>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/filesystem.hpp>
#include "config.h"
#include "parser/URL.h"
#include "link/Link.h"
#include "link/LinkCounter.h"
#include "transfer/Transfer.h"
#include "algorithm/hyper_log_log.h"
#include "algorithm/algorithm.h"
#include "urlstore/UrlStore.h"

using namespace std;

namespace Tools {

	algorithm::hyper_log_log<size_t> *count_urls(const vector<string> &warc_paths) {

		algorithm::hyper_log_log<size_t> *counter = new algorithm::hyper_log_log<size_t>();

		size_t idx = 0;
		for (const string &warc_path : warc_paths) {
			ifstream infile(warc_path);
			boost::iostreams::filtering_istream decompress_stream;
			decompress_stream.push(boost::iostreams::gzip_decompressor());
			decompress_stream.push(infile);

			string line;
			while (getline(decompress_stream, line)) {
				const URL url(line.substr(0, line.find("\t")));
				counter->insert_hash(url.hash());
			}

			if (idx % 100 == 0) {
				cout << warc_path << " done " << idx << "/" << warc_paths.size() << endl;
			}

			idx++;
		}

		return counter;
	}

	algorithm::hyper_log_log<size_t> *count_links(const vector<string> &warc_paths) {

		algorithm::hyper_log_log<size_t> *counter = new algorithm::hyper_log_log<size_t>();

		size_t idx = 0;
		for (const string &warc_path : warc_paths) {
			ifstream infile(warc_path);
			boost::iostreams::filtering_istream decompress_stream;
			decompress_stream.push(boost::iostreams::gzip_decompressor());
			decompress_stream.push(infile);

			string line;
			while (getline(decompress_stream, line)) {
				const Link::Link link(line);
				counter->insert_hash(link.target_url().hash());
			}

			if (idx % 100 == 0) {
				cout << warc_path << " done " << idx << "/" << warc_paths.size() << endl;
			}

			idx++;
		}

		return counter;
	}

	void run_counter() {

		const size_t num_threads = 12;

		vector<string> files;
		vector<string> link_files;

		for (const string &batch : Config::batches) {

			const string file_name = string("/mnt/crawl-data/") + batch + "/warc.paths.gz";

			ifstream infile(file_name);

			boost::iostreams::filtering_istream decompress_stream;
			decompress_stream.push(boost::iostreams::gzip_decompressor());
			decompress_stream.push(infile);

			string line;
			while (getline(decompress_stream, line)) {
				string warc_path = string("/mnt/") + line;
				const size_t pos = warc_path.find(".warc.gz");
				if (pos != string::npos) {
					warc_path.replace(pos, 8, ".gz");
				}

				files.push_back(warc_path);
			}
		}

		for (const string &batch : Config::link_batches) {

			const string file_name = string("/mnt/crawl-data/") + batch + "/warc.paths.gz";

			ifstream infile(file_name);

			boost::iostreams::filtering_istream decompress_stream;
			decompress_stream.push(boost::iostreams::gzip_decompressor());
			decompress_stream.push(infile);

			string line;
			while (getline(decompress_stream, line)) {
				string warc_path = string("/mnt/") + line;
				const size_t pos = warc_path.find(".warc.gz");

				if (pos != string::npos) {
					warc_path.replace(pos, 8, ".links.gz");
				}

				link_files.push_back(warc_path);
			}
		}

		vector<vector<string>> thread_input;
		algorithm::vector_chunk(files, ceil((double)files.size() / num_threads), thread_input);

		vector<vector<string>> link_thread_input;
		algorithm::vector_chunk(link_files, ceil((double)link_files.size() / num_threads), link_thread_input);

		mutex write_file_mutex;

		/*
		Run url counters
		*/
		vector<future<algorithm::hyper_log_log<size_t> *>> futures;
		for (size_t i = 0; i < num_threads && i < thread_input.size(); i++) {
			futures.emplace_back(std::async(launch::async, count_urls, thread_input[i]));
		}

		algorithm::hyper_log_log<size_t> url_counter;
		for (auto &future : futures) {
			algorithm::hyper_log_log<size_t> *result = future.get();
			url_counter += *(result);
			delete result;
		}

		futures.clear();

		/*
		Run link counters
		*/
		for (size_t i = 0; i < num_threads && i < link_thread_input.size(); i++) {
			futures.emplace_back(std::async(launch::async, count_links, link_thread_input[i]));
		}

		algorithm::hyper_log_log<size_t> link_counter;
		for (auto &future : futures) {
			algorithm::hyper_log_log<size_t> *result = future.get();
			link_counter += *(result);
			delete result;
		}

		cout << "Uniq urls: " << url_counter.size() << endl;
		cout << "Uniq links: " << link_counter.size() << endl;
	}

	vector<string> download_link_batch(const string &batch, size_t limit, size_t offset) {

		File::TsvFileRemote warc_paths_file(string("crawl-data/") + batch + "/warc.paths.gz");
		vector<string> warc_paths;
		warc_paths_file.read_column_into(0, warc_paths);

		vector<string> files_to_download;
		for (size_t i = offset; i < warc_paths.size() && i < (offset + limit); i++) {
			string warc_path = warc_paths[i];
			const size_t pos = warc_path.find(".warc.gz");
			if (pos != string::npos) {
				warc_path.replace(pos, 8, ".links.gz");
			}
			files_to_download.push_back(warc_path);
		}

		return Transfer::download_gz_files_to_disk(files_to_download);
	}

	void count_link_batch(const SubSystem *sub_system, const string &batch, size_t sub_batch, const vector<string> &files,
			map<string, map<size_t, float>> &counter) {

		const size_t chunk_size = 500;

		vector<vector<string>> chunks;
		algorithm::vector_chunk(files, chunk_size, chunks);

		for (const vector<string> &chunk : chunks) {
			Link::run_link_counter(sub_system, batch, sub_batch, chunk, counter);
		}

	}

	void count_all_links() {

		SubSystem *sub_system = new SubSystem();

		for (const string &batch : Config::link_batches) {

			vector<string> files = download_link_batch(batch, 50000, 0);

			for (size_t sub_batch = 0; sub_batch < 5; sub_batch++) {

				map<string, map<size_t, float>> counter;
				count_link_batch(sub_system, batch, sub_batch, files, counter);
				// Upload link counts.
				Link::upload_link_counts(batch, sub_batch, counter);
			}

			Transfer::delete_downloaded_files(files);
		}
		delete sub_system;
	}
}


/*

Copyright (c) 2005-2021, University of Oxford.
All rights reserved.

University of Oxford means the Chancellor, Masters and Scholars of the
University of Oxford, having an administrative office at Wellington
Square, Oxford OX1 2JD, UK.

This file is part of Chaste.

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 the University of Oxford 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 HOLDER 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.

*/

#ifndef ABSTRACTRUSHLARSENCARDIACCELL_HPP_
#define ABSTRACTRUSHLARSENCARDIACCELL_HPP_

#include "ChasteSerialization.hpp"
#include <boost/serialization/base_object.hpp>
#include "ClassIsAbstract.hpp"

#include "AbstractCardiacCell.hpp"
#include "PetscTools.hpp"

/**
 * This is the base class for cardiac cells solved using the Rush-Larsen method.
 * It is based on code contributed by Megan Lewis, University of Saskatchewan
 *
 * The basic approach to solving such models is:
 *  \li Compute alpha & beta values for gating variables, and derivatives for
 *      other state variables.
 *  \li Update the transmembrane potential, either from solving an external PDE,
 *      or using a forward Euler step.
 *  \li Update any eligible gating variables (or similar) with Rush-Larsen scheme.
 *  \li Update the remaining state variables using a forward Euler step.
 */
class AbstractRushLarsenCardiacCell : public AbstractCardiacCell
{
private:
    /** Needed for serialization. */
    friend class boost::serialization::access;
    /**
     * Archive the member variables.
     *
     * @param archive
     * @param version
     */
    template<class Archive>
    void serialize(Archive & archive, const unsigned int version)
    {
        // This calls serialize on the base class.
        archive & boost::serialization::base_object<AbstractCardiacCell>(*this);
    }

public:
    /**
     * Standard constructor for a cell.
     *
     * @param numberOfStateVariables  the size of the ODE system
     * @param voltageIndex  the index of the variable representing the transmembrane
     *     potential within the state variable vector
     * @param pIntracellularStimulus  the intracellular stimulus function
     *
     * Some notes for future reference:
     *  \li It's a pity that inheriting from AbstractCardiacCell forces us to store a
     *      null pointer (for the unused ODE solver) in every instance.  We may want
     *      to revisit this design decision at a later date.
     */
    AbstractRushLarsenCardiacCell(
        unsigned numberOfStateVariables,
        unsigned voltageIndex,
        boost::shared_ptr<AbstractStimulusFunction> pIntracellularStimulus);

    /** Virtual destructor */
    virtual ~AbstractRushLarsenCardiacCell();

    /**
     * Simulates this cell's behaviour between the time interval [tStart, tEnd],
     * with timestep #mDt.  Uses a forward Euler step to update the transmembrane
     * potential at each timestep.
     *
     * The length of the time interval must be a multiple of the timestep.
     *
     * @param tStart  beginning of the time interval to simulate
     * @param tEnd  end of the time interval to simulate
     * @param tSamp  sampling interval for returned results (defaults to #mDt)
     * @return  the values of each state variable, at intervals of tSamp.
     */
    OdeSolution Compute(double tStart, double tEnd, double tSamp=0.0);

    /**
     * Simulates this cell's behaviour between the time interval [tStart, tEnd],
     * with timestep #mDt.  The transmembrane potential is kept fixed throughout,
     * but the other state variables are updated.
     *
     * The length of the time interval must be a multiple of the timestep.
     *
     * @param tStart  beginning of the time interval to simulate
     * @param tEnd  end of the time interval to simulate
     */
    void ComputeExceptVoltage(double tStart, double tEnd);

    /**
     * Simulate this cell's behaviour between the time interval [tStart, tEnd],
     * with timestemp #mDt, updating the internal state variable values.
     *
     * @param tStart  beginning of the time interval to simulate
     * @param tEnd  end of the time interval to simulate
     */
    void SolveAndUpdateState(double tStart, double tEnd);

private:
// LCOV_EXCL_START
    /**
     * This function should never be called - the cell class incorporates its own solver.
     *
     * @param time
     * @param rY
     * @param rDY
     */
    void EvaluateYDerivatives(double time, const std::vector<double> &rY, std::vector<double> &rDY)
    {
        NEVER_REACHED;
    }
// LCOV_EXCL_STOP

protected:
    /**
     * Update the values of elligible gating variables using the Rush-Larsen method,
     * and of other non-V variables using forward Euler, for a single timestep.
     *
     * \note This method must be provided by subclasses.
     *
     * @param rDY  vector containing dy/dt values
     * @param rAlphaOrTau  vector containing alpha or tau values, depending on the formulation
     * @param rBetaOrInf  vector containing beta or inf values, depending on the formulation
     */
    virtual void ComputeOneStepExceptVoltage(const std::vector<double> &rDY,
                                             const std::vector<double> &rAlphaOrTau,
                                             const std::vector<double> &rBetaOrInf)=0;

    /**
     * Perform a forward Euler step to update the transmembrane potential.
     *
     * @param rDY  vector containing dy/dt values
     */
    void UpdateTransmembranePotential(const std::vector<double> &rDY);

     /**
     * Compute dy/dt and alpha and beta values.
     *
     * \note This method must be provided by subclasses.
     *
     * @param time  start of this timestep
     * @param rDY  vector to fill in with dy/dt values
     * @param rAlphaOrTau  vector to fill in with alpha or tau values, depending on the formulation
     * @param rBetaOrInf  vector to fill in with beta or inf values, depending on the formulation
     */
    virtual void EvaluateEquations(double time,
                                   std::vector<double> &rDY,
                                   std::vector<double> &rAlphaOrTau,
                                   std::vector<double> &rBetaOrInf)=0;
};

CLASS_IS_ABSTRACT(AbstractRushLarsenCardiacCell)

#endif // ABSTRACTRUSHLARSENCARDIACCELL_HPP_


// This alternative to numpy is not currently being used
// but is retained in CVS for possible future use.
// In particular, information about array objects such
// as curve cannot currently be cached because we don't
// know when a numpy pos array has been changed.

// Copyright (c) 2000, 2001, 2002, 2003 by David Scherer and others.
// See the file license.txt for complete license terms.
// See the file authors.txt for a complete list of contributors.

#include <boost/python/class.hpp>
#include <boost/python/object.hpp>
#include <boost/python/overloads.hpp>
#include <boost/python/init.hpp>
#include <boost/python/operators.hpp>
#include <boost/python/def.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/return_internal_reference.hpp>
#include <boost/python/iterator.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/module.hpp>

#include "python/vector_array.hpp"
#include "python/scalar_array.hpp"

// TODO: Figure out what to do with this...
#define PY_ARRAY_UNIQUE_SYMBOL visual_PyArrayHandle
#define NO_IMPORT_ARRAY
#include <numpy/arrayobject.h>

#include <iostream>
#include <stdexcept>

namespace cvisual {namespace python {

		vector_array::vector_array( const boost::python::list& sequence)
		: data( boost::python::extract<int>( sequence.attr("__len__")()))
		{
			iterator i = data.begin();
			for (int s_i = 0; s_i < sequence.attr("__len__")(); ++s_i, ++i) {
				boost::python::extract<vector> v_extractor( sequence[s_i]);
				if (v_extractor.check()) {
					*i = v_extractor();
				}
				else {
					boost::python::object elem = sequence[s_i];
					*i = vector();
					switch (boost::python::extract<int>(elem.attr("__len__")())) {
						case 3:
						i->z = boost::python::extract<double>(elem[2]);
						case 2:
						i->y = boost::python::extract<double>(elem[1]);
						i->x = boost::python::extract<double>(elem[0]);
						default:
						throw std::invalid_argument(
								"Can only construct a vector from a "
								"sequence of 2 or 3 doubles.");
					}
				}
			}
		}

		vector_array::vector_array( boost::python::numeric::array sequence)
		: data( ((PyArrayObject*)sequence.ptr())->dimensions[0])
		{
			const PyArrayObject* seq_ptr = (const PyArrayObject*)sequence.ptr();

			if (!( seq_ptr->nd == 2
							&& seq_ptr->dimensions[1] == 3
							&& seq_ptr->descr->type_num == PyArray_DOUBLE)) {
				throw std::invalid_argument( "Must construct a vector_array from an Nx3 array of type Float64.");
			}

			const double* seq_i = (const double*)seq_ptr->data;
			iterator i = this->begin();
			for (; i != this->end(); ++i, seq_i += 3) {
				*i = vector( seq_i[0], seq_i[1], seq_i[2]);
			}
		}

		void
		vector_array::append( const vector& v)
		{
			data.push_back( v);
		}

		void
		vector_array::append( const vector_array& va)
		{
			data.insert( data.end(), va.data.begin(), va.data.end());
		}

		void
		vector_array::prepend( const vector& v)
		{
			data.push_front( v);
		}

		void
		vector_array::head_clip()
		{
			data.pop_front();
		}

		void
		vector_array::head_crop( int i_)
		{
			if (i_ < 0)
			throw std::invalid_argument( "Cannot crop a negative amount.");
			size_t i = (size_t)i_;
			if (i >= data.size())
			throw std::invalid_argument( "Cannot crop greater than the array's length.");

			iterator begin = data.begin();
			data.erase( begin, begin+i);
		}

		void
		vector_array::tail_clip()
		{
			data.pop_back();
		}

		void
		vector_array::tail_crop( int i_)
		{
			if (i_ < 0)
			throw std::invalid_argument( "Cannot crop a negative amount.");
			size_t i = (size_t)i_;
			if (i >= data.size())
			throw std::invalid_argument( "Cannot crop greater than the array's length.");

			iterator end = data.end();
			data.erase( end-i, end);
		}

		vector_array
		vector_array::operator*( double s) const
		{
			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = *i * s;
			}
			return ret;
		}

		// Possibly dangerous, this provides block multiplication elementwise, not to be
		// confused with dot() or cross().
		vector_array
		vector_array::operator*( vector v) const
		{
			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				vector v_i = *i;
				*r_i = vector( v_i[0]*v[0], v_i[1]*v[1], v_i[2]*v[2]);
			}
			return ret;
		}

		vector_array
		vector_array::operator*( const scalar_array& s) const
		{
			if (data.size() != s.data.size())
			throw std::out_of_range( "Incompatible vector array multiplication.");

			vector_array ret( data.size());
			scalar_array::const_iterator s_i = s.begin();
			iterator r_i = ret.begin();

			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i, ++s_i) {
				*r_i = *i * *s_i;
			}
			return ret;
		}

		vector_array
		vector_array::operator/( double s) const
		{
			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = *i / s;
			}
			return ret;
		}

		vector_array
		vector_array::operator/( const scalar_array& s) const
		{
			if (data.size() != s.data.size())
			throw std::out_of_range( "Incompatible vector array division.");

			vector_array ret( data.size());
			scalar_array::const_iterator s_i = s.begin();
			iterator r_i = ret.begin();

			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i, ++s_i) {
				*r_i = *i / *s_i;
			}
			return ret;
		}

		vector_array
		vector_array::operator-() const
		{
			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = -(*i);
			}
			return ret;
		}

		const vector_array&
		vector_array::operator*=( double s)
		{
			for (iterator i = data.begin(); i != data.end(); ++i) {
				*i *= s;
			}
			return *this;
		}

		const vector_array&
		vector_array::operator*=( const scalar_array& s)
		{
			if (data.size() != s.data.size())
			throw std::out_of_range( "Incompatible vector array multiplication.");

			scalar_array::const_iterator s_i = s.begin();
			for (iterator i = data.begin(); i != data.end(); ++i, ++s_i) {
				*i *= *s_i;
			}
			return *this;
		}

		const vector_array&
		vector_array::operator/=( double s)
		{
			for (iterator i = data.begin(); i != data.end(); ++i) {
				*i /= s;
			}
			return *this;
		}

		const vector_array&
		vector_array::operator/=( const scalar_array& s)
		{
			if (data.size() != s.data.size())
			throw std::out_of_range( "Incompatible vector array multiplication.");

			scalar_array::const_iterator s_i = s.begin();
			for (iterator i = data.begin(); i != data.end(); ++i, ++s_i) {
				*i /= *s_i;
			}
			return *this;
		}

		vector_array
		vector_array::operator+( const vector& v) const
		{
			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = *i + v;
			}
			return ret;
		}

		vector_array
		vector_array::operator+( const vector_array& v) const
		{
			if (data.size() != v.data.size())
			throw std::out_of_range( "Incompatible vector array addition.");

			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			const_iterator v_i = v.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i, ++v_i) {
				*r_i = *i + *v_i;
			}
			return ret;
		}

		vector_array
		vector_array::operator-( const vector& v) const
		{
			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i ) {
				*r_i = *i - v;
			}
			return ret;
		}

		vector_array
		vector_array::operator-( const vector_array& v) const
		{
			if (data.size() != v.data.size())
			throw std::out_of_range( "Incompatible vector array subtraction.");

			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			const_iterator v_i = v.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i, ++v_i) {
				*r_i = *i - *v_i;
			}
			return ret;
		}

		const vector_array&
		vector_array::operator+=( const vector& v)
		{
			for (iterator i = data.begin(); i != data.end(); ++i) {
				*i += v;
			}
			return *this;
		}

		const vector_array&
		vector_array::operator+=( const vector_array& v)
		{
			if (data.size() != v.data.size())
			throw std::out_of_range( "Incompatible vector array addition.");

			const_iterator v_i = v.data.begin();
			for (iterator i = data.begin(); i != data.end(); ++i, ++v_i) {
				*i += *v_i;
			}
			return *this;
		}

		const vector_array&
		vector_array::operator-=( const vector& v)
		{
			for (iterator i = data.begin(); i != data.end(); ++i) {
				*i -= v;
			}
			return *this;
		}

		const vector_array&
		vector_array::operator-=( const vector_array& v)
		{
			if (data.size() != v.data.size())
			throw std::out_of_range( "Incompatible vector array subtraction.");

			const_iterator v_i = v.data.begin();
			for (iterator i = data.begin(); i != data.end(); ++i, ++v_i) {
				*i -= *v_i;
			}
			return *this;

		}

		vector_array
		vector_array::cross( const vector& v)
		{
			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->cross(v);
			}
			return ret;
		}

		vector_array
		vector_array::cross( const vector_array& v)
		{
			if (v.data.size() != data.size())
			throw std::out_of_range( "Incompatible vector_array types." );

			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			const_iterator v_i = v.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i, ++v_i) {
				*r_i = i->cross( *v_i);
			}
			return ret;
		}

		vector_array
		vector_array::norm() const
		{
			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->norm();
			}
			return ret;
		}

		vector_array
		vector_array::fabs() const
		{
			vector_array ret( data.size());

			iterator r_i = ret.data.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->fabs();
			}
			return ret;
		}

		vector_array
		vector_array::proj( const vector& v)
		{
			vector_array ret( data.size());
			iterator r_i = ret.data.begin();

			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->proj( v);
			}
			return ret;
		}

		vector_array
		vector_array::proj( const vector_array& v)
		{
			if (v.data.size() != data.size())
			throw std::out_of_range( "Incompatible vector_array types." );

			vector_array ret( data.size());
			iterator r_i = ret.data.begin();
			const_iterator v_i = v.data.begin();

			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i, ++v_i) {
				*r_i = i->proj( *v_i);
			}
			return ret;
		}

		scalar_array
		vector_array::mag() const
		{
			scalar_array ret( data.size());
			scalar_array::iterator r_i = ret.begin();
			for ( const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->mag();
			}
			return ret;
		}

		scalar_array
		vector_array::mag2() const
		{
			scalar_array ret( data.size());
			scalar_array::iterator r_i = ret.begin();
			for ( const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->mag2();
			}
			return ret;

		}

		void
		vector_array::rotate( const double& d, vector axis)
		{
			for (iterator i = data.begin(); i != data.end(); ++i) {
				i->rotate( d, axis);
			}
		}

		vector&
		vector_array::py_getitem( int index)
		{
			if (index < 0) {
				// Negative indexes are counted from the end of the array in Python.
				index += data.size();
			}

			return data.at(index);
		}

		void
		vector_array::py_setitem( int index, vector value)
		{
			if (index < 0)
			index += data.size();

			data.at(index) = value;
		}

		scalar_array
		vector_array::dot( const vector& v)
		{
			scalar_array ret( data.size());
			scalar_array::iterator r_i = ret.begin();
			for ( const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->dot( v);
			}
			return ret;
		}

		scalar_array
		vector_array::dot( const vector_array& v)
		{
			if (v.data.size() != data.size())
			throw std::out_of_range( "Incompatible vector_array types." );

			scalar_array ret( data.size());
			scalar_array::iterator r_i = ret.begin();
			const_iterator v_i = v.begin();

			for ( const_iterator i = data.begin(); i != data.end(); ++i, ++r_i, ++v_i) {
				*r_i = i->dot( *v_i);
			}
			return ret;
		}

		scalar_array
		vector_array::comp( const vector& v)
		{
			scalar_array ret( data.size());

			scalar_array::iterator r_i = ret.begin();
			for (iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->comp( v);
			}
			return ret;
		}

		scalar_array
		vector_array::comp( const vector_array& v)
		{
			if ( data.size() != v.data.size() )
			throw std::out_of_range( "Incompatible array scalar projection.");

			scalar_array ret( data.size());

			scalar_array::iterator r_i = ret.begin();
			const_iterator v_i = v.begin();
			for (const_iterator i = data.begin(); i !=data.end(); ++i, ++v_i, ++r_i) {
				*r_i = i->comp( *v_i);
			}
			return ret;
		}

		scalar_array
		vector_array::get_x() const
		{
			scalar_array ret( data.size());
			scalar_array::iterator r_i = ret.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->get_x();
			}
			return ret;
		}

		scalar_array
		vector_array::get_y() const
		{
			scalar_array ret( data.size());
			scalar_array::iterator r_i = ret.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->get_y();
			}
			return ret;
		}

		scalar_array
		vector_array::get_z() const
		{
			scalar_array ret( data.size());
			scalar_array::iterator r_i = ret.begin();
			for (const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = i->get_z();
			}
			return ret;
		}

		void
		vector_array::set_x( const scalar_array& x)
		{
			if (data.empty()) {
				// In case we are building this thing up from single columns
				data = std::deque<vector>( x.size());
			}
			if (x.data.size() != data.size())
			throw std::out_of_range( "Incompatible array assignment.");

			scalar_array::const_iterator x_i = x.begin();
			for (iterator i = data.begin(); i != data.end(); ++i, ++x_i) {
				i->set_x( *x_i);
			}
		}

		void
		vector_array::set_y( const scalar_array& y)
		{
			if (data.empty()) {
				// In case we are building this thing up from single columns
				data = std::deque<vector>( y.size());
			}
			if (y.data.size() != data.size())
			throw std::out_of_range( "Incompatible array assignment.");

			scalar_array::const_iterator y_i = y.begin();
			for (iterator i = data.begin(); i != data.end(); ++i, ++y_i) {
				i->set_y( *y_i);
			}
		}

		void
		vector_array::set_z( const scalar_array& z)
		{
			if (data.empty()) {
				// In case we are building this thing up from single columns
				data = std::deque<vector>( z.size());
			}
			if (z.data.size() != data.size())
			throw std::out_of_range( "Incompatible array assignment.");

			scalar_array::const_iterator z_i = z.begin();
			for (iterator i = data.begin(); i != data.end(); ++i, ++z_i) {
				i->set_z( *z_i);
			}
		}

		void
		vector_array::set_x( const boost::python::list& sequence)
		{
			this->set_x( scalar_array( sequence));
		}

		void
		vector_array::set_y( const boost::python::list& sequence)
		{
			this->set_y( scalar_array( sequence));
		}

		void
		vector_array::set_z( const boost::python::list& sequence)
		{
			this->set_z( scalar_array( sequence));
		}

		vector
		vector_array::sum() const
		{
			vector ret;
			for (const_iterator i = data.begin(); i != data.end(); ++i) {
				ret += *i;
			}
			return ret;
		}

		// Evaluate expression vector - vector_array
		vector_array
		vector_array::lhs_sub( const vector& v) const
		{
			vector_array ret( data.size());
			iterator r_i = ret.begin();
			for ( const_iterator i = data.begin(); i != data.end(); ++i, ++r_i) {
				*r_i = v - *i;
			}
			return ret;
		}

		vector_array
		vector_array::operator>=( const double& s) const
		{
			vector_array ret( data.size());
			iterator r_i = ret.begin();
			for (const_iterator i = data.begin(); i!= data.end(); ++i, ++r_i) {
				*r_i = vector( (i->x >= s) ? 1.0: 0.0
						, (i->y >= s) ? 1.0: 0.0
						, (i->z >= s) ? 1.0: 0.0);
			}
			return ret;
		}

		vector_array
		vector_array::operator>=( const scalar_array& s) const
		{
			if (s.data.size() != data.size())
			throw std::out_of_range( "Incompatible vector_array to scalar_array comparison." );

			vector_array ret( data.size());
			iterator r_i = ret.begin();
			scalar_array::const_iterator s_i = s.begin();
			for (const_iterator i = data.begin(); i!= data.end(); ++i, ++r_i, ++s_i) {
				*r_i = vector( (i->x >= *s_i) ? 1.0: 0.0
						, (i->y >= *s_i) ? 1.0: 0.0
						, (i->z >= *s_i) ? 1.0: 0.0);
			}
			return ret;
		}

		vector_array
		vector_array::operator>=( const vector_array& v) const
		{
			if (v.data.size() != data.size())
			throw std::out_of_range( "Incompatible vector_array to vector_array comparison" );

			vector_array ret( data.size());
			iterator r_i = ret.begin();
			const_iterator v_i = v.begin();
			for (const_iterator i = data.begin(); i!= data.end(); ++i, ++r_i, ++v_i) {
				*r_i = vector( (i->x >= v_i->x) ? 1.0: 0.0
						, (i->y >= v_i->y) ? 1.0: 0.0
						, (i->z >= v_i->z) ? 1.0: 0.0);
			}
			return ret;
		}

		vector_array
		vector_array::operator<=( const double& s) const
		{
			vector_array ret( data.size());
			iterator r_i = ret.begin();
			for (const_iterator i = data.begin(); i!= data.end(); ++i, ++r_i) {
				*r_i = vector( (i->x <= s) ? 1.0: 0.0
						, (i->y <= s) ? 1.0: 0.0
						, (i->z <= s) ? 1.0: 0.0);
			}
			return ret;
		}

		vector_array
		vector_array::operator<=( const scalar_array& s) const
		{
			if (s.data.size() != data.size())
			throw std::out_of_range( "Incompatible vector_array comparison." );

			vector_array ret( data.size());
			iterator r_i = ret.begin();
			scalar_array::const_iterator s_i = s.begin();
			for (const_iterator i = data.begin(); i!= data.end(); ++i, ++r_i, ++s_i) {
				*r_i = vector( (i->x <= *s_i) ? 1.0: 0.0
						, (i->y <= *s_i) ? 1.0: 0.0
						, (i->z <= *s_i) ? 1.0: 0.0);
			}
			return ret;
		}

		vector_array
		vector_array::operator<=( const vector_array& v) const
		{
			if (v.data.size() != data.size())
			throw std::out_of_range( "Incompatible vector_array comparison." );

			vector_array ret( data.size());
			iterator r_i = ret.begin();
			const_iterator v_i = v.begin();
			for (const_iterator i = data.begin(); i!= data.end(); ++i, ++r_i, ++v_i) {
				*r_i = vector( (i->x <= v_i->x) ? 1.0: 0.0
						, (i->y <= v_i->y) ? 1.0: 0.0
						, (i->z <= v_i->z) ? 1.0: 0.0);
			}
			return ret;
		}

		vector_array
		vector_array::operator*( const vector_array& v) const
		{
			if (v.data.size() != data.size())
			throw std::out_of_range( "Incompatible vector_array multiplication." );

			vector_array ret( data.size());
			iterator r_i = ret.begin();
			const_iterator v_i = v.begin();
			for (const_iterator i = data.begin(); i!= data.end(); ++i, ++r_i, ++v_i) {
				*r_i = vector( (i->x * v_i->x)
						, (i->y * v_i->y)
						, (i->z * v_i->z));
			}
			return ret;
		}

		void
		vector_array::set_x( boost::python::numeric::array x)
		{
			this->set_x( scalar_array( x));
		}

		void
		vector_array::set_y( boost::python::numeric::array y)
		{
			this->set_y( scalar_array( y));
		}

		void
		vector_array::set_z( boost::python::numeric::array z)
		{
			this->set_z( scalar_array( z));
		}

		void
		vector_array::set_x( double x)
		{
			this->set_x( scalar_array( size(), x));
		}

		void
		vector_array::set_y( double y)
		{
			this->set_y( scalar_array( size(), y));
		}

		void
		vector_array::set_z( double z)
		{
			this->set_z( scalar_array( size(), z));
		}

		boost::python::handle<PyObject>
		vector_array::as_array() const
		{
			// Make space for the returned array
			int dims[] = {size(), 3};
			boost::python::handle<> ret( PyArray_FromDims( 2, dims, PyArray_DOUBLE));

			// A direct pointer to the PyArrayObject
			PyArrayObject* ret_ptr = (PyArrayObject *)ret.get();

			// Iterable pointers to copy the data.
			double* r_i = (double *) ret_ptr->data;
			const_iterator i = this->begin();
			// Copy the data.
			for (; i != this->end(); ++i, r_i += 3) {
				r_i[0] = i->get_x();
				r_i[1] = i->get_y();
				r_i[2] = i->get_z();
			}
			return ret;
		}

		// This algorithm is failing to recognize all of the collisions...
		boost::python::list
		sphere_collisions( const vector_array& pos, const scalar_array& radius)
		{
			if (pos.size() != radius.size())
			throw std::out_of_range( "Unequal array lengths.");
			assert( pos.size());

			boost::python::list ret;
			vector_array::const_iterator pos_i = pos.begin();
			scalar_array::const_iterator r_i = radius.begin();
			// In spite of using two iterators and a counter, this is a two-pointer
			// search: i, and j.
			for ( int i = 0; pos_i != pos.end(); ++pos_i, ++r_i, ++i) {
				vector_array::const_iterator pos_j = pos_i + 1;
				scalar_array::const_iterator r_j = r_i + 1;
				for ( int j = i+1; pos_j != pos.end(); ++pos_j, ++r_j, ++j) {
					// If the magnitude of the differential distance is smaller
					// than the radius between them, a collision!
					if ((*pos_i - *pos_j).mag() < (*r_i + *r_j)) {
						// A collision
						ret.append( boost::python::make_tuple( i, j));
					}
				}
			}
			return ret;
		}

		// Return a list of integers for those spheres which have collided with
		// the plane specified by a normal vector 'normal' and point on the plane 'origin'
		boost::python::list
		sphere_to_plane_collisions( const vector_array& pos
				, const scalar_array& radius
				, vector normal
				, vector origin)
		{
			boost::python::list ret;
			vector_array::const_iterator p_i = pos.begin();
			scalar_array::const_iterator r_i = radius.begin();
			for( int i = 0; p_i != pos.end(); ++p_i, ++r_i, ++i) {
				// Compute the scalar projection of a vector from the origin to pos
				// to the normal vector of the plane.
				double dist = (*p_i - origin).comp( normal);
				if ( dist < *r_i) {
					ret.append( i);
				}
			}
			return ret;
		}

		/************************ python interface code ******************************/

		namespace {
			BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS( vector_array_rotate, vector_array::rotate, 1, 2)
		}

		void
		wrap_vector_array()
		{
			using namespace boost::python;
			// For the uninitiated (and sane), these are member function pointers.
			// They have the following syntax:
			// return-type (class-type::* pointer-name)(argument-list) = &class-type::member-function;

			vector_array (vector_array::* proj_vector)(const vector&) = &vector_array::proj;
			vector_array (vector_array::* proj_vector_array)( const vector_array&) = &vector_array::proj;
			vector_array (vector_array::* cross_vector)(const vector&) = &vector_array::cross;
			vector_array (vector_array::* cross_vector_array)(const vector_array&) = &vector_array::cross;

			scalar_array (vector_array::* dot_vector)(const vector&) = &vector_array::dot;
			scalar_array (vector_array::* dot_vector_array)(const vector_array&) = &vector_array::dot;
			scalar_array (vector_array::* comp_vector) (const vector&) = &vector_array::comp;
			scalar_array (vector_array::* comp_vector_array)(const vector_array&) = &vector_array::comp;

			void (vector_array::* append_vector)(const vector&) = &vector_array::append;
			void (vector_array::* prepend_vector)(const vector&) = &vector_array::prepend;

			// Overloaded setters for 'x', 'y', and 'z' properties
			void (vector_array::* set_x_s)(const scalar_array&) = &vector_array::set_x;
			void (vector_array::* set_x_n)(numeric::array) = &vector_array::set_x;
			void (vector_array::* set_x_d)(double) = &vector_array::set_x;
			void (vector_array::* set_y_s)(const scalar_array&) = &vector_array::set_y;
			void (vector_array::* set_y_n)(numeric::array) = &vector_array::set_y;
			void (vector_array::* set_y_d)(double) = &vector_array::set_y;
			void (vector_array::* set_z_s)(const scalar_array&) = &vector_array::set_z;
			void (vector_array::* set_z_n)(numeric::array) = &vector_array::set_z;
			void (vector_array::* set_z_d)(double) = &vector_array::set_z;

			vector_array (vector_array::* truediv_double)( double) const = &vector_array::operator/;
			vector_array (vector_array::* truediv_scalar_array)(const scalar_array&) const = &vector_array::operator/;
			const vector_array& (vector_array::* itruediv_double)(double) = &vector_array::operator/=;
			const vector_array& (vector_array::* itruediv_scalar_array)(const scalar_array&) = &vector_array::operator/=;

			class_<vector_array> vector_array_wrapper( "vector_array", init< optional<int, vector> >( args("size", "fill")));
			vector_array_wrapper.def( init<const list&>())
			.def( init<numeric::array>())
			.def( self * double())
			.def( double() * self)
			.def( self * other<scalar_array>())
			.def( other<scalar_array>() * self)
			.def( self * other<vector_array>())
			.def( other<vector_array>() * self)
			.def( self * other<vector>())
			.def( self / double())
			.def( self / other<scalar_array>())
			.def( -self)
			.def( self *= double())
			.def( self *= other<scalar_array>())
			.def( self /= double())
			.def( self /= other<scalar_array>())
			.def( self + self)
			.def( self + other<vector>())
			.def( other<vector>() + self)
			.def( self - self)
			.def( self - other<vector>())
			.def( other<vector>() - self)
			.def( self += self)
			.def( self += other<vector>())
			.def( self -= self)
			.def( self -= other<vector>())
			.def( self >= self)
			.def( self >= other<scalar_array>())
			.def( self >= other<double>())
			.def( self <= self)
			.def( self <= other<scalar_array>())
			.def( self <= other<double>())
			.def( "__truediv__", truediv_double)
			.def( "__truediv__", truediv_scalar_array)
			.def( "__itruediv__", itruediv_double, return_value_policy<copy_const_reference>())
			.def( "__itruediv__", itruediv_scalar_array, return_value_policy<copy_const_reference>())
			.def( "proj", proj_vector)
			.def( "proj", proj_vector_array, "Returns a vector_array of vector projections of this array"
					" to a vector, or another vector_array of identical length.")
			.def( "cross", cross_vector)
			.def( "cross", cross_vector_array, "Returns a vector_array of cross products of this array"
					" and a vector, or another vector_array of identical length.")
			.def( "size", &vector_array::size, "Get the number of elements in the array.")
			.def( "rotate", &vector_array::rotate, vector_array_rotate( args("angle", "axis"), "Rotate a vector_array about an axis vector through an angle."))
			.def( "append", append_vector, "Add an element to the end of the array.")
			.def( "prepend", prepend_vector, "Add an element to the beginning of the array.")
			.def( "head_clip", &vector_array::head_clip, "Remove an element from the beginning of the arrray.")
			.def( "head_crop", &vector_array::head_crop, "Remove n elements from the beginning of the array.")
			.def( "tail_clip", &vector_array::tail_clip, "Remove an element from the end of the array.")
			.def( "tail_crop", &vector_array::tail_crop, "Remove n elements from the end of the array.")
			.def( "dot", dot_vector)
			.def( "dot", dot_vector_array, "Returns a scalar_array of dot products of this array"
					" and a vector, or another vector_array of identical length.")
			.def( "comp", comp_vector)
			.def( "comp", comp_vector_array, "Returns a scalar_array of scalar projections of this array"
					" to a vector, or another vector_array of identical length.")
			.def( "mag", &vector_array::mag, "Returns a scalar_array of the magnitudes of every element in this array.")
			.def( "mag2", &vector_array::mag2, "Equivalant to x.mag() * x.mag(), but faster.")
			.def( "norm", &vector_array::norm, "Returns a vector_array of the unit vectors of this array.")
			.def( "abs", &vector_array::fabs, "Returns a vector_array of absolute values of the vectors in the array.")
			// Fancy C++ -> python iterator access
			.def( "__iter__", iterator<vector_array>())
			.def( "__len__", &vector_array::size)
			.def( "__getitem__", &vector_array::py_getitem, return_internal_reference<>(), "Index this array by a single integer.\n"
					"Returns a reference to the indexed vector.")
			.def( "__setitem__", &vector_array::py_setitem)
			.def( "get_x", &vector_array::get_x)
			.def( "set_x", set_x_s)
			.def( "set_x", set_x_n)
			.def( "set_x", set_x_d)
			.def( "get_y", &vector_array::get_y)
			.def( "set_y", set_y_s)
			.def( "set_y", set_y_n)
			.def( "set_y", set_y_d)
			.def( "get_z", &vector_array::get_z)
			.def( "set_z", set_z_s)
			.def( "set_z", set_z_n)
			.def( "set_z", set_z_d)
			.def( "sum", &vector_array::sum, "Returns the sum of all elements in the array.")
			.def( "as_array", &vector_array::as_array, "Create a self.__len__() x 3 Numeric.array from this vector_array.")
			;

			vector_array_wrapper.add_property( "x", vector_array_wrapper.attr("get_x"),
					vector_array_wrapper.attr("set_x"));
			vector_array_wrapper.add_property( "y", vector_array_wrapper.attr("get_y"),
					vector_array_wrapper.attr("set_y"));
			vector_array_wrapper.add_property( "z", vector_array_wrapper.attr("get_z"),
					vector_array_wrapper.attr("set_z"));

			def( "sphere_intercollisions", &sphere_collisions, args( "pos", "radius"),
					"Evaluate collisions between spheres with centers == pos, and radii == radius.\n"
					"Returns a list of two-integer tuple indexes.\n"
					"The indexes corrispond to collision pairs indexed by pos.");
			def( "sphere_to_plane_collisions", &sphere_to_plane_collisions
					, args( "pos", "radius", "normal", "origin")
					, "Evaluate collisions between spheres with centers == pos, and radii == radius with\n"
					"a plane specified by a normal vector == normal, and a point on the plane == origin.\n"
					"Returns a list of integers corrisponding to colliding spheres' indexed into pos." );

		}

	}} // !namespace cvisual::python


/**
 \file
 Implementation of the Corsika shower reader

 \author Lukas Nellen
 \version $Id$
 \date 29 Jan 2004
 */

#include <corsika/Constants.h>
#include <corsika/LongFile.h>
#include <corsika/IOException.h>

#include <fstream>
#include <sstream>
#include <string>
#include <cmath>
#include <cstdio>
#include <iostream>

#include <boost/tokenizer.hpp>

using namespace std;
using namespace corsika;

typedef boost::tokenizer<boost::char_separator<char> > mytok;

static void log(const std::string& mess)
{
    std::cout << mess << std::endl;
}
static void log(const std::ostringstream& mess)
{
    std::cout << mess.str() << std::endl;
}

#define INFO(mess) log(mess);
#define ERROR(mess) log(mess);
#define FATAL(mess) log(mess);

namespace
{
    bool SplitLine(string l, vector<string>& vec, string s = " ")
    {
        boost::char_separator<char> sep(s.c_str());
        mytok tok(l,sep);
        vec.clear();
        for (mytok::const_iterator titer=tok.begin();titer!=tok.end();++titer) {
            vec.push_back(*titer);
        }
        return true;
    }

    string particleProfileStr = " LONGITUDINAL DISTRIBUTION IN";
    string dEdX_ProfileStr = " LONGITUDINAL ENERGY DEPOSIT";
    string GHFit_Str = " FIT OF THE HILLAS CURVE";

}

LongFile::LongFile(string filename, double zenith):
    fFilename(filename),
    fCosZenith(cos(zenith)),
    fIsSlantDepthProfile(false),
    event_count(0),
    fDx(0.),
    fNBinsParticles(0),
    fNBinsEnergyDeposit(0),
    fLongDataFile(new std::ifstream(fFilename.c_str()))
{
    // check if file exists and is readable here
    Scan();
}

LongProfile LongFile::GetProfile(size_t event)
{
    if (event >= event_count)
    {
        ostringstream msg;
        msg << "Trying to get event " << event << ", but file " << fFilename << " has only " << event_count << " events (starting at zero).";
        throw IOException(msg.str());
    }
    return FetchProfile(event);
}

void LongFile::Scan()
{
    // scan for number of bins and dX,
    // also check if there are energy deposit profiles

    //static const double g = 1;
    //static const double cm = 1;
    //static const double cm2 = cm*cm;
    //static const double deg = 3.14159/180;

    // Read CORSIKA profile if available
    if (!fLongDataFile->is_open()) return;

    string line;
    while (getline(*fLongDataFile.get(), line) && (fPartProfiles.size()==0 || fdEdXProfiles.size()==0))
    {

        if (line.find(particleProfileStr) != string::npos)
        {
            fPartProfiles.push_back(fLongDataFile->tellg());
            char s1[50], s2[50], s3[50], isVertOrSlant[50];
            sscanf(line.c_str(), "%s %s %s %zu %s", s1, s2, s3, &fNBinsParticles, isVertOrSlant);
            string testStr(isVertOrSlant);
            if (testStr == "VERTICAL")
                fIsSlantDepthProfile = false;
            else if (testStr == "SLANT")
                fIsSlantDepthProfile = true;
            else {
                ostringstream err;
                err << "Corsika longitunal file \"" << fFilename << "\" is invalid:\n"
                "line: \"" << line << "\" "
                "contains invalid string at                ^^^^^^^\n"
                "which is neither VERTICAL nor SLANT !";
                ERROR(err);
                throw IOException(err.str());
            }
        }

        if (line.find(dEdX_ProfileStr) != string::npos)
        {
            fdEdXProfiles.push_back(fLongDataFile->tellg());
            char s1[50], s2[50], s3[50], s4[50], isVertOrSlant[50], s5 [50], s6 [50];
            sscanf(line.c_str(),
                   "%s %s %s %s %zu %s %s %s %f",
                   s1, s2, s3, s4, &fNBinsEnergyDeposit, isVertOrSlant, s5, s6, &fDx);
            string testStr(isVertOrSlant);
            if (testStr == "VERTICAL")
            {
                fIsSlantDepthProfile = false;
                fDx /= fCosZenith;
            }
            else if (testStr == "SLANT")
            {
                fIsSlantDepthProfile = true;
            }
            else
            {
                ostringstream err;
                err << "Corsika longitunal file \"" << fFilename << "\" is invalid:\n"
                "line: \"" << line << "\" "
                "contains invalid string at                ^^^^^^^\n"
                " which is neither VERTICAL nor SLANT !";
                ERROR(err);
                throw IOException(err.str());
            }
        }
    } // --  end scan file  ---
    while (getline(*fLongDataFile.get(), line))
    {
        if (line.find(particleProfileStr) != string::npos)
            fPartProfiles.push_back(fLongDataFile->tellg());

        if (line.find(dEdX_ProfileStr) != string::npos)
            fdEdXProfiles.push_back(fLongDataFile->tellg());
    }
    event_count = (fPartProfiles.size()?fPartProfiles.size():fdEdXProfiles.size());
}

LongProfile LongFile::FetchProfile(size_t findShower)
{
    bool aux_flag = false;
    size_t i = 0;
    size_t j = 0;
    string line;

    vector<double> auxDepth(fNBinsParticles);
    vector<double> auxCharge(fNBinsParticles);
    vector<double> auxMuons(fNBinsParticles);
    vector<double> auxAntiMuons(fNBinsParticles);
    vector<double> auxGammas(fNBinsParticles);
    vector<double> auxElectrons(fNBinsParticles);
    vector<double> auxPositrons(fNBinsParticles);
    vector<double> auxHadrons(fNBinsParticles);
    vector<double> auxNuclei(fNBinsParticles);
    vector<double> auxCherenkov(fNBinsParticles);

    vector<double> auxDepth_dE (fNBinsEnergyDeposit);
    vector<double> auxDeltaEn (fNBinsEnergyDeposit);

    GaisserHillasParameter gh;
    double energyDepositSum = 0.;

    // Read CORSIKA profile if available
    if (!fLongDataFile->is_open())
    {
        ERROR("Reading failed for some reason.");
        return LongProfile();
    }
    fLongDataFile->clear();
    fLongDataFile->seekg(fPartProfiles[findShower]);

    vector<string> tokens;
    while (getline(*fLongDataFile.get(), line))
    {
        SplitLine(line, tokens);
        if (line.find(particleProfileStr) != string::npos)
        {
            getline(*fLongDataFile.get(), line);
            SplitLine(line, tokens);
        }
        if (line.find(dEdX_ProfileStr) != string::npos || aux_flag)
        {
            if (line.find(dEdX_ProfileStr) != string::npos)
            {
                getline(*fLongDataFile.get(), line);
                SplitLine(line, tokens);
            }
            if (tokens.size() > 1 && tokens[0] == string("DEPTH") && tokens[1] == string("GAMMA"))
            {
                aux_flag = true;
                getline(*fLongDataFile.get(), line);
                SplitLine(line, tokens);
            }
            if (line.find(GHFit_Str) != string::npos) break;

            if (aux_flag && j < fNBinsParticles)
            {

                float xdepth = 0.0;
                float gamma = 0.0;
                float emIoniz = 0.0;
                float emCut = 0.0;
                float muonIoniz = 0.0;
                float muonCut = 0.0;
                float hadronIoniz = 0.0;
                float hadronCut = 0.0;
                float neutrino = 0.0;
                float sumEnergy = 0.0;

                sscanf(line.c_str(), "%f %f %f %f %f %f %f %f %f %f",
                       &xdepth, &gamma, &emIoniz, &emCut, &muonIoniz, &muonCut,
                       &hadronIoniz, &hadronCut, &neutrino, &sumEnergy);

                if (!fIsSlantDepthProfile)
                    xdepth /= fCosZenith;

                // dEdX profile has slightly different depth-bins
                auxDepth_dE[j] = xdepth;
                //cout << j << " " << hadroncut  << " " << muoncut
                //   << " " << neutrino << " " << sumenergy << endl;
                // Subtracting neutrino energy
                // and take fraction for muons and hadrons from
                //     Barbosa et al Astro. Part. Phys. 22, (2004) p. 159
                static float muonFraction = 0.575;
                static float hadronFraction = 0.261;
                static float hadronGroundFraction = 0.390;
                auxDeltaEn[j] = sumEnergy - neutrino -
                muonFraction * muonCut -
                hadronFraction * hadronCut;

                if ( j+1 < fNBinsEnergyDeposit )
                    energyDepositSum += auxDeltaEn[j];
                else
                {
                    double groundEnergy = (1.-hadronGroundFraction)*hadronCut + hadronIoniz + muonIoniz + emIoniz+emCut + gamma;
                    energyDepositSum += groundEnergy;
                }

                auxDeltaEn[j] /= fDx;

                ++j;
            }
        }

        if (line.find(dEdX_ProfileStr) == string::npos || (tokens[0] == "DEPTH" && tokens[1] == "GAMMAS" && i < fNBinsParticles))
        {
            if (aux_flag) continue;

            float xdepth = 0.0;
            float chargedparticles = 0.0;
            float gammas, positrons, electrons, muplus, muminus, hadrons;
            float nuclei, cerenkov;
            sscanf(line.c_str(), "%f %f %f %f %f %f %f %f %f %f",
                   &xdepth, &gammas, &positrons, &electrons, &muplus, &muminus,
                   &hadrons, &chargedparticles, &nuclei, &cerenkov);

            if (xdepth > 0)
            {
                //BRD 17/2/05 CORSIKA depths are vertical assuming a flat
                //Earth.  So apply cosTheta correction here.
                // RU Wed Jun 13 14:46:44 CEST 2007
                // if SLANT was used in CORSIKA, don't do the fCosZenith correction
                if (!fIsSlantDepthProfile)
                    xdepth /= fCosZenith;
                auxDepth[i] = xdepth;
                auxCharge[i] = chargedparticles;
                auxMuons[i] =  muminus;
                auxAntiMuons[i] =  muminus;
                auxGammas[i] = gammas;
                auxElectrons[i] = electrons;
                auxPositrons[i] = positrons;
                auxHadrons[i] = hadrons;
                auxNuclei[i] = nuclei;
                auxCherenkov[i] = cerenkov;
                ++i;
            }
        }
    }

    float anmax;
    float ax0;
    float axmax;
    float achi;
    while (getline(*fLongDataFile.get(), line))
    {
        if (line.find(" PARAMETERS         = ") != string::npos)
        {

            float aapar, abpar, acpar;
            sscanf(line.c_str(), " PARAMETERS         =   %f %f %f %f %f %f", &anmax,
                   &ax0, &axmax, &aapar, &abpar, &acpar);
            getline(*fLongDataFile.get(), line);
            sscanf(line.c_str(), " CHI**2/DOF         = %f", &achi);

            const bool hasValidGHfit = (achi>0) && (achi*fNBinsParticles<1e15);

            double A = aapar * (g/cm2);
            double B = abpar;
            double C = acpar / (g/cm2);

            if (!fIsSlantDepthProfile)
            {
                A /= fCosZenith;
                B /= fCosZenith;
                C /= fCosZenith;
                axmax /= fCosZenith;
            }

            // GaisserHillas6Parameter gh;
            if (hasValidGHfit)
            {
                gh.SetXMax(axmax*(g/cm2), 0);
                gh.SetNMax(anmax, 0);
                gh.SetXZero(ax0*(g/cm2), 0);
                gh.SetChiSquare(fNBinsParticles*achi, fNBinsParticles);
                gh.SetA(A, 0.);
                gh.SetB(B, 0.);
                gh.SetC(C, 0.);
            }

            break;
        }
    }

    LongProfile profile;
    profile.fChargeProfile = auxCharge;
    profile.fGammaProfile = auxGammas;
    profile.fElectronProfile = auxElectrons;
    profile.fPositronProfile = auxPositrons;
    profile.fMuonProfile = auxMuons;
    profile.fAntiMuonProfile = auxAntiMuons;
    profile.fDepth = auxDepth;
    profile.fHadronProfile = auxHadrons;
    profile.fNucleiProfile = auxNuclei;
    profile.fCherenkovProfile = auxCherenkov;
    //profile.fdEdX = auxDeltaEn;
    //profile.fDepth_dE = auxDepth_dE;

    profile.fCalorimetricEnergy = energyDepositSum;
    profile.fGaisserHillas = gh;
    return profile;
}

void CorrectProfile(LongProfile& profile, double dX)
{
    // This code is quarantined because is mostly dead code, very
    // specific for other purposes, so maybe it should not be here at
    // all.
    //
    // CORSIKA writes into the last bin of the dEdX profile not only
    // the energy deposit in the atmosphere, but also the energy hitting
    // the ground (observation level). For vertical event this can be
    // remarkable, and should not be used for FD simulations !
    //
    // - Fix this by replacing the entries in the last bin by GH fitted
    //   extrapolations.
    // - Add one more bin in depth (extrapolation) to make sure
    //   that also for a different atmospheric profile model the
    //   shower will hit the ground, and does not just disappear
    //   somewhere in the middle of the atmosphere !

    vector<double> auxDepth = profile.fDepth;
    vector<double> auxCharge = profile.fChargeProfile;
    vector<double> auxMuons = profile.fMuonProfile;
    vector<double> auxAntiMuons = profile.fAntiMuonProfile;
    vector<double> auxGammas = profile.fGammaProfile;
    vector<double> auxElectrons = profile.fElectronProfile;
    vector<double> auxPositrons = profile.fPositronProfile;
    vector<double> auxHadrons = profile.fHadronProfile;
    vector<double> auxNuclei = profile.fNucleiProfile;
    vector<double> auxCherenkov = profile.fCherenkovProfile;

    vector<double> auxDepth_dE = profile.fDepth_dE;
    vector<double> auxDeltaEn = profile.fdEdX;
    size_t i = auxDepth.size();

    size_t nBinsEnergyDeposit = auxDepth_dE.size();
    //size_t nBinsParticles = auxDepth.size();

    // go three bins back, since CORSIKA sometimes has a funny second-last bin
    //const double normDepth_dedx = auxDepth_dE[nBinsEnergyDeposit-3];
    const double normN_dedx = 1;
    // const double normN_dedx =
    //   theShower.HasGHParameters() ?
    //     theShower.GetGHParameters().Eval(normDepth_dedx * g/cm2) : 1;
    const double normdEdX = auxDeltaEn[i-3];

    // const double normDepth_part = auxDepth[nBinsEnergyDeposit-1];
    const double normN_part = 1.;
    // const double normN_part =
    //   theShower.HasGHParameters() ?
    //     theShower.GetGHParameters().Eval(normDepth_part * g/cm2) : 1.;
    const double normCharge = auxCharge[i-1];
    const double normMuons = auxMuons[i-1];
    const double normGammas = auxGammas[i-1];
    const double normElectrons = auxElectrons[i-1];

    const double lastBinDepth_dEdX = auxDepth_dE[profile.fDepth_dE.size()-1];
    const double lastBinDepth_part = auxDepth[profile.fDepth.size()-1];

    // recalculate last dedx bins
    for (size_t iCorrect = 0; iCorrect < 2; ++iCorrect)
    {
        size_t binCorrect = nBinsEnergyDeposit - 2 + iCorrect;
        //const double binDepth = auxDepth_dE[binCorrect];
        auxDeltaEn[binCorrect] = normdEdX / normN_dedx;
        // auxDeltaEn[binCorrect] = normdEdX / normN_dedx *
        //   (theShower.HasGHParameters() ?
        //      theShower.GetGHParameters().Eval(binDepth*g/cm2) : 1);
    }

    // prolongate profiles by some bins
    for (size_t iBinAdd = 0; iBinAdd < 2; ++iBinAdd)
    {
        const double addDepth_dEdX = lastBinDepth_dEdX + dX * (iBinAdd+1);
        const double addDepth_part = lastBinDepth_part + dX * (iBinAdd+1);
        const double Nch_dedx = 1;
        // const double Nch_dedx =
        //   theShower.HasGHParameters() ?
        //     theShower.GetGHParameters().Eval(addDepth_dEdX * g/cm2) : 1;
        const double Nch_part = 1;
        // const double Nch_part =
        //   theShower.HasGHParameters() ?
        //     theShower.GetGHParameters().Eval(addDepth_part * g/cm2) : 1;
        const double adddEdX = normdEdX / normN_dedx * Nch_dedx;

        auxDepth_dE.push_back(addDepth_dEdX);
        auxDeltaEn.push_back(adddEdX);

        const double fac = Nch_part / normN_part;
        const double addCharge = normCharge * fac;
        const double addMuons = normMuons * fac;
        const double addGammas = normGammas * fac;
        const double addElectrons = normElectrons * fac;

        auxDepth.push_back(addDepth_part);
        auxCharge.push_back(addCharge);
        auxMuons.push_back(addMuons);
        auxGammas.push_back(addGammas);
        auxElectrons.push_back(addElectrons);

    }

    profile.fChargeProfile = auxCharge;
    profile.fGammaProfile = auxGammas;
    profile.fElectronProfile = auxElectrons;
    profile.fPositronProfile = auxPositrons;
    profile.fMuonProfile = auxMuons;
    profile.fAntiMuonProfile = auxAntiMuons;
    profile.fDepth = auxDepth;
    profile.fHadronProfile = auxHadrons;
    profile.fNucleiProfile = auxNuclei;
    profile.fCherenkovProfile = auxCherenkov;
    //profile.fdEdX = auxDeltaEn;
    //profile.fDepth_dE = auxDepth_dE;
}