The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
extern "C" {
  #include "EXTERN.h"
  #include "perl.h"
  #include "XSUB.h"
  #undef seed
  #undef do_open
  #undef do_close
}

#include <string>
#include <vector>
#include <iostream>
#include <sstream>

#include <interval_tree.h>

#define do_open   Perl_do_open
#define do_close  Perl_do_close

class SV_ptr {
  SV *sv;
  public:
    SV_ptr() : sv(0) {}
    SV_ptr(SV *sv) : sv(sv) {
      if (sv) SvREFCNT_inc(sv);
    }
    SV_ptr(const SV_ptr &ptr) : sv(ptr.get()) {
      if (sv) SvREFCNT_inc(sv);
    }
    virtual ~SV_ptr() {
      if (sv) SvREFCNT_dec(sv);
    }
    SV_ptr& operator=(SV_ptr ptr) {
      if (sv) SvREFCNT_dec(sv);
      sv = ptr.get();
      if (sv) SvREFCNT_inc(sv);
      return *this;
    }
    bool operator!=(SV_ptr &ptr) {
      return sv != ptr.get();
    }
    bool defined() {
      return sv != 0;
    }
    SV * get() {
      return sv; 
    }
    SV * get() const {
      return sv; 
    }
};

std::ostream& operator<<(std::ostream &out, SV_ptr value) {
  out << "Node:" << value.get();
  return out;
}

class RemoveFunctor {
  SV *callback;
  public:
    RemoveFunctor(SV *callback_) : callback(callback_) {}
    bool operator()(SV_ptr value, long low, long high) const {
      // pass args into callback
      dSP;
      ENTER;
      SAVETMPS;
      PUSHMARK(SP);
      XPUSHs(value.get());
      XPUSHs(sv_2mortal(newSViv(low)));
      XPUSHs(sv_2mortal(newSViv(high+1)));
      PUTBACK;

      // get result from callback and return
      I32 count = call_sv(callback, G_SCALAR);

      SPAGAIN;

      if (count < 1) {
        PUTBACK;
        FREETMPS;
        LEAVE;
        return false;
      }

      SV *retval_sv = POPs;
      bool retval = SvTRUE(retval_sv);

      PUTBACK;
      FREETMPS;
      LEAVE;
      return retval;
    }
};

typedef IntervalTree<SV_ptr,long> PerlIntervalTree;
typedef IntervalTree<SV_ptr,long>::Node PerlIntervalTree_Node;

MODULE = Set::IntervalTree PACKAGE = Set::IntervalTree

PerlIntervalTree *
PerlIntervalTree::new()

SV *
PerlIntervalTree::str()
  CODE:
    std::string str = THIS->str();
    const char *tree = str.c_str();
    RETVAL = newSVpv(tree, 0);
  OUTPUT:
    RETVAL

SV *
PerlIntervalTree::fetch_nearest_up(long value)
  CODE:
    SV_ptr ptr = THIS->fetch_nearest_up(value);
    SV *ret = ptr.get();
    SvREFCNT_inc(ret);
    RETVAL = ret;
    if (RETVAL == 0)
          XSRETURN_UNDEF;
  OUTPUT:
    RETVAL

SV *
PerlIntervalTree::fetch_nearest_down(long value)
  CODE:
    SV_ptr ptr = THIS->fetch_nearest_down(value-1);
    SV *ret = ptr.get();
    SvREFCNT_inc(ret);
    RETVAL = ret;
    if (RETVAL == 0)
          XSRETURN_UNDEF;
  OUTPUT:
    RETVAL

void
PerlIntervalTree::insert(SV *value, long low, long high)
  PROTOTYPE: $;$;$
  CODE: 
    if (high <= low) Perl_croak(aTHX_ "Intervals must have positive width");
    SV_ptr ptr(value);
    THIS->insert(ptr, low, high-1);

AV *
PerlIntervalTree::remove(long low, long high, ...)
  CODE:
    if (high <= low) Perl_croak(aTHX_ "Intervals must have positive width");
    RETVAL = newAV();
    sv_2mortal((SV*)RETVAL);

    if (items > 3) {
      SV *callback = ST(3); 
      RemoveFunctor remove_functor(callback);
      std::vector<SV_ptr> removed;
      THIS->remove(low, high-1, remove_functor, removed);

      for (std::vector<SV_ptr>::iterator
          i=removed.begin(); i!=removed.end(); ++i) 
      {
        SV *value = i->get();
        SvREFCNT_inc(value);
        av_push(RETVAL, value);
      }
    }
    else {
      std::vector<SV_ptr> removed; 
      THIS->remove(low, high-1, removed);

      for (std::vector<SV_ptr>::iterator
          i=removed.begin(); i!=removed.end(); ++i) 
      {
        SV *value = i->get();
        SvREFCNT_inc(value);
        av_push(RETVAL, value);
      }
    }
  OUTPUT:
    RETVAL

AV *
PerlIntervalTree::remove_window(long low, long high, ...)
  CODE:
    if (high <= low) Perl_croak(aTHX_ "Intervals must have positive width");
    RETVAL = newAV();
    sv_2mortal((SV*)RETVAL);

    if (items > 3) {
      SV *callback = ST(3); 
      RemoveFunctor remove_functor(callback);
      std::vector<SV_ptr> removed;
      THIS->remove_window(low, high-1, remove_functor, removed);

      for (std::vector<SV_ptr>::iterator
          i=removed.begin(); i!=removed.end(); ++i) 
      {
        SV *value = i->get();
        SvREFCNT_inc(value);
        av_push(RETVAL, value);
      }
    }
    else {
      std::vector<SV_ptr> removed; 
      THIS->remove_window(low, high-1, removed);

      for (std::vector<SV_ptr>::iterator
          i=removed.begin(); i!=removed.end(); ++i) 
      {
        SV *value = i->get();
        SvREFCNT_inc(value);
        av_push(RETVAL, value);
      }
    }
  OUTPUT:
    RETVAL

AV *
PerlIntervalTree::fetch(long low, long high)
  PROTOTYPE: $;$
  CODE:
    if (high <= low) Perl_croak(aTHX_ "Intervals must have positive width");
    RETVAL = newAV();
    sv_2mortal((SV*)RETVAL);
    std::vector<SV_ptr> intervals;
    THIS->fetch(low, high-1, intervals);
    for (size_t i=0; i<intervals.size(); i++) {
      SV *value = intervals[i].get();
      SvREFCNT_inc(value);
      av_push(RETVAL, value);
    }
  OUTPUT:
    RETVAL

AV *
PerlIntervalTree::fetch_window(long low, long high)
  PROTOTYPE: $;$
  CODE:
    if (high <= low) Perl_croak(aTHX_ "Intervals must have positive width");
    RETVAL = newAV();
    sv_2mortal((SV*)RETVAL);
    std::vector<SV_ptr> intervals;
    THIS->fetch_window(low, high-1, intervals);
    for (size_t i=0; i<intervals.size(); i++) {
      SV *value = intervals[i].get();
      SvREFCNT_inc(value);
      av_push(RETVAL, value);
    }
  OUTPUT:
    RETVAL

void 
PerlIntervalTree::DESTROY()

MODULE = Set::IntervalTree PACKAGE = Set::IntervalTree::Node

PerlIntervalTree_Node *
PerlIntervalTree_Node::new()

int
PerlIntervalTree_Node::low()
  CODE:
    RETVAL = THIS->low();
  OUTPUT:
    RETVAL

int
PerlIntervalTree_Node::high()
  CODE:
    RETVAL = THIS->high()+1;
  OUTPUT:
    RETVAL

SV *
PerlIntervalTree_Node::value()
  CODE:
    RETVAL = THIS->value().get();
  OUTPUT:
    RETVAL

void 
PerlIntervalTree_Node::DESTROY()