/* Copyright (c) 1997-2018
   Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
   http://www.polymake.org

   This program is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 2, or (at your option) any
   later version: http://www.gnu.org/licenses/gpl.txt.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
--------------------------------------------------------------------------------
*/

#include "polymake/perl/Ext.h"

namespace pm { namespace perl { namespace glue {
namespace {

int Scope_local_marker_index;

void localize_marker(pTHX_ void* p)
{
   if (PL_in_eval & ~(EVAL_INREQUIRE))  // functionality of $^S less the lex test
      Perl_croak(aTHX_ "Scope::end missing");
}

#define PmSaveAllocN(n, type, ptrs)      \
   type* ptrs;                           \
   I32 save_ix=PL_savestack_ix;          \
   (void)SSNEWt(n, type);                \
   ptrs=(type*)(PL_savestack+save_ix)

#define PmSaveAlloc(type, ptrs) PmSaveAllocN(1, type, ptrs)

#define PmSaveReturn() \
   return NUM2PTR(void*, PL_savestack_ix-save_ix)

#define PmSaveRestore(type, ptrs) \
   type* ptrs=(type*)(PL_savestack+(PL_savestack_ix-PTR2IV(p)))

struct local_var_ptrs {
   SV* var;
   void* orig_any;
   U32 orig_flags;
   char* orig_pv;       // as a representative for SV_HEAD_UNION
   SV* temp_owner;
};

void* do_local_var(pTHX_ SV* var, SV* value)
{
   PmSaveAlloc(local_var_ptrs, ptrs);
   ptrs->var=var;
   ptrs->orig_any=SvANY(var);
   ptrs->orig_flags=SvFLAGS(var) & ~SVs_TEMP;
   ptrs->orig_pv=var->sv_u.svu_pv;
   var->sv_u.svu_pv=value->sv_u.svu_pv;
   SvANY(var)=SvANY(value);
   SvFLAGS(var)=SvFLAGS(value) & ~SVs_TEMP;
   ptrs->temp_owner=value;
   SvREFCNT_inc_simple_void_NN(var);
   SvREFCNT_inc_simple_void_NN(value);
   PmSaveReturn();
}

void undo_local_var(pTHX_ void* p)
{
   PmSaveRestore(local_var_ptrs, ptrs);
   SV* var=ptrs->var;
   SvANY(var)=ptrs->orig_any;
   SvFLAGS(ptrs->temp_owner)=SvFLAGS(var);
   ptrs->temp_owner->sv_u.svu_pv=var->sv_u.svu_pv;
   var->sv_u.svu_pv=ptrs->orig_pv;
   SvFLAGS(var)=ptrs->orig_flags;
   SvREFCNT_dec(var);
   SvREFCNT_dec(ptrs->temp_owner);
}

struct local_scalar_ptrs {
   SV* var;
   SV orig;
};

void* do_local_scalar(pTHX_ SV* var, SV* value)
{
   PmSaveAlloc(local_scalar_ptrs, ptrs);
   ptrs->var=var;
   ptrs->orig.sv_any=var->sv_any;
   ptrs->orig.sv_refcnt=var->sv_refcnt;
   ptrs->orig.sv_flags=var->sv_flags;
   ptrs->orig.sv_u.svu_pv=var->sv_u.svu_pv;
   var->sv_any=NULL;
   var->sv_flags=0;
   var->sv_refcnt=1;
   sv_setsv(var,value);
   PmSaveReturn();
}

void undo_local_scalar(pTHX_ void* p)
{
   PmSaveRestore(local_scalar_ptrs, ptrs);
   SV* var=ptrs->var;
   if (SvREFCNT(var)>1) {
      SvREFCNT_dec(var);
   } else {
      SvREFCNT(var)=0;  sv_clear(var);
   }
   var->sv_any=ptrs->orig.sv_any;
   var->sv_refcnt=ptrs->orig.sv_refcnt;
   var->sv_flags=ptrs->orig.sv_flags;
   var->sv_u.svu_pv=ptrs->orig.sv_u.svu_pv;
}

struct local_incr_ptrs {
   SV* var;
   I32 incr;
}; 

void* do_local_incr(pTHX_ SV* var, I32 incr)
{
   PmSaveAlloc(local_incr_ptrs, ptrs);
   ptrs->var=var;
   ptrs->incr=incr;
   if (SvIOK(var) || SvPOK(var))
      sv_setiv(var, SvIV(var)+incr);
   else if (SvNOK(var))
      sv_setnv(var, SvNVX(var)+incr);
   else
      sv_setiv(var,incr);
   PmSaveReturn();
}

void undo_local_incr(pTHX_ void* p)
{
   PmSaveRestore(local_incr_ptrs, ptrs);
   SV* var=ptrs->var;
   if (SvIOK(var))
      sv_setiv(var, SvIVX(var)-ptrs->incr);
   else if (SvNOK(ptrs->var))
      sv_setnv(var, SvNVX(var)-ptrs->incr);
   else
      Perl_croak(aTHX_ "undoing local increment: variable is no more numerical");
}

void* do_local_push(pTHX_ SV* av, SV** src, int n, int side)
{
   PmSaveAlloc(local_incr_ptrs, ptrs);
   SV **dst, **src_end;
   ptrs->var=av;
   ptrs->incr=side*n;
   av_extend((AV*)av, AvFILLp(av)+n);
   if (side<0) {
      dst=AvARRAY(av);
      Move(dst, dst+n, AvFILLp(av)+1, SV*);
   } else {
      dst=AvARRAY(av)+AvFILLp(av)+1;
   }
   for (src_end=src+n; src<src_end; ++src, ++dst) {
      SV* d=*src;
      if (SvREADONLY(d) || !SvTEMP(d))
         *dst=newSVsv(d);
      else
         *dst=SvREFCNT_inc_simple_NN(d);
   }
   AvFILLp(av)+=n;
   PmSaveReturn();
}

void undo_local_push(pTHX_ void* p)
{
   PmSaveRestore(local_incr_ptrs, ptrs);
   SV* av=ptrs->var;
   I32 n=ptrs->incr;
   SV **e, **eend;
   if (n>0) {
      for (e=AvARRAY(av)+AvFILLp(av), eend=e-n; e>eend; --e) {
         SvREFCNT_dec(*e);
         *e=PmEmptyArraySlot;
      }
      AvFILLp(av)-=n;
   } else {
      for (eend=AvARRAY(av)-1, e=eend-n; e>eend; --e)
         SvREFCNT_dec(*e);
      AvFILLp(av)+=n;
      ++eend;
      Move(eend-n, eend, AvFILLp(av)+1, SV*);
      for (e=eend+AvFILLp(av)+1, eend=e-n; e<eend; ++e)
         *e=PmEmptyArraySlot;
   }
}

struct local_pop_ptrs {
   AV* av;
   SV* val;
};

void* do_local_pop(pTHX_ AV* av)
{
   PmSaveAlloc(local_pop_ptrs, ptrs);
   SvREFCNT_inc_simple_void_NN(av);
   ptrs->av=av;
   ptrs->val=av_pop(av);
   PmSaveReturn();
}

void undo_local_pop(pTHX_ void* p)
{
   PmSaveRestore(local_pop_ptrs, ptrs);
   AV* av=ptrs->av;
   av_push(av,ptrs->val);
   SvREFCNT_dec(av);
}

void* do_local_shift(pTHX_ AV* av)
{
   PmSaveAlloc(local_pop_ptrs, ptrs);
   SvREFCNT_inc_simple_void_NN(av);
   ptrs->av=av;
   ptrs->val=av_shift(av);
   PmSaveReturn();
}

void undo_local_shift(pTHX_ void* p)
{
   PmSaveRestore(local_pop_ptrs, ptrs);
   AV* av=ptrs->av;
   av_unshift(av,1);
   AvARRAY(av)[0]=ptrs->val;
   SvREFCNT_dec(av);
}

void* do_local_clip(pTHX_ AV* av, int store_incr)
{
   PmSaveAlloc(local_incr_ptrs, ptrs);
   SvREFCNT_inc_simple_void_NN(av);
   ptrs->var=(SV*)av;
   ptrs->incr=store_incr;
   PmSaveReturn();
}

void* do_local_clip_front(pTHX_ AV* av, int n)
{
   if (n>=0) {
      if (AvFILLp(av)+1 < n)
         Perl_croak(aTHX_ "local_clip_front: array has less than %d elements", n);
   } else {
      if (AvFILLp(av)+1 < -1-n)
         Perl_croak(aTHX_ "local_clip_front: array has less than %d elements", -n);
      n+=AvFILLp(av)+1;
   } 
   AvARRAY(av)+=n;
   AvFILLp(av)-=n;
   return do_local_clip(aTHX_ av, -n);
}

void* do_local_clip_back(pTHX_ AV* av, int n)
{
   int store_incr;
   if (n>=0) {
      if (AvFILLp(av)+1 < n)
         Perl_croak(aTHX_ "local_clip_back: array has less than %d elements", n);
      store_incr=AvFILLp(av)-n;
      AvFILLp(av)=n;
   } else {
      if (AvFILLp(av)+1 < -1-n)
         Perl_croak(aTHX_ "local_clip_back: array has less than %d elements", -n);
      store_incr=-1-n;
      AvFILLp(av)+=1+n;
   }
   return do_local_clip(aTHX_ av, store_incr);
}

void undo_local_clip(pTHX_ void* p)
{
   PmSaveRestore(local_incr_ptrs, ptrs);
   AV* av=(AV*)ptrs->var;
   if (ptrs->incr>=0) {
      AvFILLp(av)+=ptrs->incr;
   } else {
      AvARRAY(av)+=ptrs->incr;
      AvFILLp(av)-=ptrs->incr;
   }
   SvREFCNT_dec(av);
}

struct local_swap_ptrs {
   AV* var;
   I32 ix1, ix2;
};

void* do_local_swap(pTHX_ SV* var, I32 ix1, I32 ix2)
{
   PmSaveAlloc(local_swap_ptrs, ptrs);
   SV* tmp;
   AV* av=(AV*)SvREFCNT_inc_NN(SvRV(var));
   if (ix1<0) ix1+=AvFILL(av)+1;
   if (ix2<0) ix2+=AvFILL(av)+1;
   if (ix1>AvFILL(av) || ix2>AvFILL(av)) Perl_croak(aTHX_ "local_swap: indices out of range");
   ptrs->var=av;
   ptrs->ix1=ix1;
   ptrs->ix2=ix2;
   tmp=AvARRAY(av)[ix1];
   AvARRAY(av)[ix1]=AvARRAY(av)[ix2];
   AvARRAY(av)[ix2]=tmp;
   PmSaveReturn();
}

void undo_local_swap(pTHX_ void* p)
{
   PmSaveRestore(local_swap_ptrs, ptrs);
   AV* av=ptrs->var;
   SV* tmp=AvARRAY(av)[ptrs->ix1];
   AvARRAY(av)[ptrs->ix1]=AvARRAY(av)[ptrs->ix2];
   AvARRAY(av)[ptrs->ix2]=tmp;
   SvREFCNT_dec(av);
}

struct local_bless_ptrs {
   SV* var;
   HV* stash;
   I32 mg_flags;
};

void* do_local_bless(pTHX_ SV* ref, SV* pkg)
{
   PmSaveAlloc(local_bless_ptrs, ptrs);
   SV* var;
   HV* stash;
   if (!SvROK(ref) || (var=SvRV(ref), !SvOBJECT(var)))
      Perl_croak(aTHX_ "usage: local_bless(\\object, \"pkg\" || \\%%stash");
   if (SvPOK(pkg)) {
      if (!(stash=gv_stashsv(pkg, FALSE)))
        Perl_croak(aTHX_ "unknown package %.*s", (int)SvCUR(pkg), SvPVX(pkg));
   } else if (!SvROK(pkg) || (stash=(HV*)SvRV(pkg), SvTYPE(stash)!=SVt_PVHV)) {
      Perl_croak(aTHX_ "usage: local_bless(\\object, \"pkg\" || \\%%stash)");
   }
   ptrs->var=SvREFCNT_inc_simple_NN(var);
   ptrs->stash=(HV*)SvREFCNT_inc(SvSTASH(var));
   ptrs->mg_flags=SvFLAGS(var) & (SVs_GMG|SVs_SMG|SVs_RMG|SVf_AMAGIC);
   sv_bless(ref, stash);
   PmSaveReturn();
}

void undo_local_bless(pTHX_ void* p)
{
   PmSaveRestore(local_bless_ptrs, ptrs);
   SV* var=ptrs->var;
   HV* tmp_stash=SvSTASH(var);
   SvSTASH_set(var, ptrs->stash);
   SvFLAGS(var) &= ~(SVs_GMG|SVs_SMG|SVs_RMG|SVf_AMAGIC);
   SvFLAGS(var) |= ptrs->mg_flags;
   SvREFCNT_dec(var);
   SvREFCNT_dec(tmp_stash);
}

}

void
localize_scalar(pTHX_ SV* var)
{
   save_destructor_x(&undo_local_scalar, do_local_scalar(aTHX_ var, sv_mortalcopy(var)));
}

void
localize_array(pTHX_ SV* av, SV* ar_ref)
{
   assert(av && SvTYPE(av)==SVt_PVAV && SvROK(ar_ref) && SvTYPE(SvRV(ar_ref))==SVt_PVAV);
   save_destructor_x(&undo_local_var, do_local_var(aTHX_ av, SvRV(ar_ref)));
}

} } }

using namespace pm::perl::glue;

MODULE = Polymake::Scope                PACKAGE = Polymake::Scope

void
begin_locals(scope)
   SV *scope;
CODE:
LEAVE;
{
   save_destructor_x(&localize_marker, SvRV(scope));
}
ENTER;

void
end_locals(scope_ref)
   SV *scope_ref;
CODE:
LEAVE;
{
   ANY* mainstack=PL_savestack;
   I32 frame_top=PL_savestack_ix, frame_bottom=PL_scopestack[PL_scopestack_ix-1], f, to_save;
   AV* scope=(AV*)SvRV(scope_ref);

   // find the marker or resign
   for (f=frame_top-3; f >= frame_bottom; --f)
      if (mainstack[f].any_ptr == (void*)&localize_marker
          && f+2 < frame_top
          && mainstack[f+2].any_uv == SAVEt_DESTRUCTOR_X) {

         if (scope != (AV*)mainstack[f+1].any_ptr) break;

         to_save=frame_top-(f+3);
         if (to_save > 0) {
            SV* marker=AvARRAY(scope)[Scope_local_marker_index];
            sv_catpvn(marker, (char*)&(mainstack[f+3]), to_save*sizeof(ANY));
            PL_savestack_ix=f;  // pop the marker and the saved locals quickly
         }
         scope=Nullav;
         break;
      }
   if (scope != Nullav) Perl_croak(aTHX_ "Scope: begin-end mismatch");
}
ENTER;

void
unwind(marker)
   SV *marker;
CODE:
{
   I32 saved=SvCUR(marker)/sizeof(ANY);
   if (saved) {
      SSGROW(saved);
      Copy(SvPVX(marker), &(PL_savestack[PL_savestack_ix]), saved, ANY);
      PL_savestack_ix+=saved;
   }
   // LEAVE in pp_entersub will execute all actions
}

MODULE = Polymake::Scope                PACKAGE = Polymake

void
local_scalar(var, value)
   SV *var;
   SV *value;
PROTOTYPE: $$
PPCODE:
{
   if ( (isGV(var) ? !(var=GvSV(var))
                   : SvTYPE(var) >= SVt_PVAV) ||
        SvTYPE(value) >= SVt_PVAV )
      croak_xs_usage(cv, "*glob || $var, value");
   LEAVE;
   save_destructor_x(&undo_local_scalar, do_local_scalar(aTHX_ var, value));
   ENTER;
   ++SP;
}

void
local_save_scalar(var)
   SV *var;
PROTOTYPE: $
PPCODE:
{
   if ( isGV(var) ? !(var=GvSV(var))
                  : SvTYPE(var) >= SVt_PVAV )
      croak_xs_usage(cv, "*glob || $var");
   LEAVE;
   save_destructor_x(&undo_local_scalar, do_local_scalar(aTHX_ var, sv_mortalcopy(var)));
   ENTER;
   ++SP;
}

void
local_array(var, value)
   SV *var;
   SV *value;
PROTOTYPE: $$
PPCODE:
{
   if (SvROK(var) ? (var=SvRV(var), SvTYPE(var)==SVt_PVAV)
                  : isGV(var) && (var=(SV*)GvAV(var))) {
      if (SvROK(value) && (value=SvRV(value), SvTYPE(value)==SVt_PVAV)) {
         LEAVE;
         save_destructor_x(&undo_local_var, do_local_var(aTHX_ var, value));
         ENTER;
         XSRETURN(1);
      }
   }
   croak_xs_usage(cv, "*glob || \\@array, \\@array");
}

void
local_hash(var, value)
   SV *var;
   SV *value;
PROTOTYPE: $$
PPCODE:
{
   if (SvROK(var) ? (var=SvRV(var), SvTYPE(var)==SVt_PVHV)
                  : isGV(var) && (var=(SV*)GvHV(var))) {
      if (SvROK(value) && (value=SvRV(value), SvTYPE(value)==SVt_PVHV)) {
         LEAVE;
         save_destructor_x(&undo_local_var, do_local_var(aTHX_ var, value));
         ENTER;
         XSRETURN(1);
      }
   }
   croak_xs_usage(cv, "*glob || \\%%hash, \\%%hash");
}

void
local_sub(var, value)
   SV *var;
   SV *value;
PROTOTYPE: $$
PPCODE:
{
   if (SvROK(var) ? (var=SvRV(var), SvTYPE(var)==SVt_PVCV)
                  : isGV(var) && (var=(SV*)GvCV(var), TRUE)) {
      if (SvROK(value) && (value=SvRV(value), SvTYPE(value)==SVt_PVCV)) {
         if (var) {
            LEAVE;
            save_destructor_x(&undo_local_var, do_local_var(aTHX_ var, value));
            ENTER;
         }
         XSRETURN(1);
      }
   }
   croak_xs_usage(cv, "*glob || \\&sub, \\&sub");
}

void
local_incr(var, ...)
   SV* var;
PROTOTYPE: $;$
PPCODE:
{
   SV* incr= items==2 ? ST(1) : NULL;
   if ( items>2 ||
        (isGV(var) ? !(var=GvSV(var))
                   : SvTYPE(var) >= SVt_PVAV) ||
        (incr && SvTYPE(incr) >= SVt_PVAV) )
      croak_xs_usage(cv, "*glob || $var [, incr]");
   LEAVE;
   save_destructor_x(&undo_local_incr, do_local_incr(aTHX_ var, incr ? SvIV(incr) : 1));
   ENTER;
   ++SP;
}

void
local_push(avref, ...)
   SV* avref;
PROTOTYPE: $@
PPCODE:
{
   SV* av=NULL;
   if (isGV(avref)
       ? (av=(SV*)GvAV(avref)) == NULL
       : !SvROK(avref) || (av=SvRV(avref), SvTYPE(av) != SVt_PVAV || SvGMAGICAL(av)))
      croak_xs_usage(cv, "*glob || \\@array, data ...");
   if (items>=2) {
      LEAVE;
      save_destructor_x(&undo_local_push, do_local_push(aTHX_ av, &ST(1), items-1, 1));
      ENTER;
   }
   ++SP;
}

void
local_unshift(avref, ...)
   SV *avref;
PROTOTYPE: $@
PPCODE:
{
   SV* av=NULL;
   if (isGV(avref)
       ? (av=(SV*)GvAV(avref)) == NULL
       : !SvROK(avref) || (av=SvRV(avref), SvTYPE(av) != SVt_PVAV || SvGMAGICAL(av)))
      croak_xs_usage(cv, "*glob || \\@array, data ...");
   if (items>=2) {
      LEAVE;
      save_destructor_x(&undo_local_push, do_local_push(aTHX_ av, &ST(1), items-1, -1));
      ENTER;
   }
   ++SP;
}

void
local_pop(avref)
   SV* avref;
PROTOTYPE: $
PPCODE:
{
   AV* av=NULL;
   SV* ret=NULL;
   if (isGV(avref)
       ? (av=GvAV(avref)) == NULL
       : !SvROK(avref) || (av=(AV*)SvRV(avref), SvTYPE(av) != SVt_PVAV || SvGMAGICAL(av)))
      croak_xs_usage(cv, "*glob || \\@array");
   if (AvFILLp(av)<0)
      Perl_croak(aTHX_ "local_pop on an empty array");
   if (GIMME_V != G_VOID) {
      ret=AvARRAY(av)[AvFILLp(av)];
   }
   LEAVE;
   save_destructor_x(&undo_local_pop, do_local_pop(aTHX_ av));
   ENTER;
   if (ret) PUSHs(ret);
}

void
local_shift(avref)
   SV* avref;
PROTOTYPE: $
PPCODE:
{
   AV* av=NULL;
   SV* ret=NULL;
   if (isGV(avref)
       ? (av=GvAV(avref)) == NULL
       : !SvROK(avref) || (av=(AV*)SvRV(avref), SvTYPE(av) != SVt_PVAV || SvGMAGICAL(av)))
      croak_xs_usage(cv, "*glob || \\@array");
   if (AvFILLp(av)<0)
      Perl_croak(aTHX_ "local_shift on an empty array");
   if (GIMME_V != G_VOID) {
      ret=AvARRAY(av)[0];
   }
   LEAVE;
   save_destructor_x(&undo_local_shift, do_local_shift(aTHX_ av));
   ENTER;
   if (ret) PUSHs(ret);
}

void
local_clip_front(avref, n)
   SV* avref;
   I32 n;
PROTOTYPE: $$
PPCODE:
{
   AV* av=NULL;
   if (isGV(avref)
       ? (av=GvAV(avref)) == NULL
       : !SvROK(avref) || (av=(AV*)SvRV(avref), SvTYPE(av) != SVt_PVAV || SvGMAGICAL(av)))
      croak_xs_usage(cv, "*glob || \\@array, new_first_pos");
   if (n != 0) {
      LEAVE;
      save_destructor_x(&undo_local_clip, do_local_clip_front(aTHX_ av, n));
      ENTER;
   }
   ++SP;
}

void
local_clip_back(avref, n)
   SV* avref;
   I32 n;
PROTOTYPE: $$
PPCODE:
{
   AV* av=NULL;
   if (isGV(avref)
       ? !(av=GvAV(avref))
       : !SvROK(avref) || (av=(AV*)SvRV(avref), SvTYPE(av) != SVt_PVAV || SvGMAGICAL(av)))
      croak_xs_usage(cv, "*glob || \\@array, new_last_pos");
   if (n != -1) {
      LEAVE;
      save_destructor_x(&undo_local_clip, do_local_clip_back(aTHX_ av, n));
      ENTER;
   }
   ++SP;
}

void
local_swap(avref, ix1, ix2)
   SV* avref;
   I32 ix1;
   I32 ix2;
PROTOTYPE: $$$
PPCODE:
{
   LEAVE;
   save_destructor_x(&undo_local_swap, do_local_swap(aTHX_ avref, ix1, ix2));
   ENTER;
}

void
local_bless(ref, pkg)
   SV* ref;
   SV* pkg;
PROTOTYPE: $$
PPCODE:
{
   LEAVE;
   save_destructor_x(&undo_local_bless, do_local_bless(aTHX_ ref, pkg));
   ENTER;
   ++SP;
}

void
propagate_match()
PPCODE:
{
   PERL_CONTEXT *cx_bottom=cxstack, *cx=cx_bottom+cxstack_ix;
   while (cx >= cx_bottom) {
      if (CxTYPE(cx)==CXt_SUB) {
         cx->blk_oldpm=PL_curpm;
         if (!skip_debug_frame(aTHX_ cx)) break;
      }
      --cx;
   }
}


BOOT:
{
   Scope_local_marker_index=CvDEPTH(get_cv("Polymake::Scope::local_marker", FALSE));
   if (PL_DBgv) {
      CvNODEBUG_on(get_cv("Polymake::Scope::begin_locals", FALSE));
      CvNODEBUG_on(get_cv("Polymake::Scope::end_locals", FALSE));
      CvNODEBUG_on(get_cv("Polymake::Scope::unwind", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_scalar", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_save_scalar", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_array", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_hash", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_sub", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_incr", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_push", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_unshift", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_pop", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_shift", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_clip_front", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_clip_back", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_swap", FALSE));
      CvNODEBUG_on(get_cv("Polymake::local_bless", FALSE));
      CvNODEBUG_on(get_cv("Polymake::propagate_match", FALSE));
   }
}

=pod
// Local Variables:
// mode:C++
// c-basic-offset:3
// indent-tabs-mode:nil
// End:
=cut
