Generated by Cython 0.29.32

Yellow lines hint at Python interaction.
Click on a line that starts with a "+" to see the C code that Cython generated for it.

Raw output: _insert.c

+001: # encoding: utf-8
  __pyx_t_1 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 1, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 002: # cython: profile=False, boundscheck=False, wraparound=False
 003: 
 004: from .._bp cimport BP
+005: from bp._insert import _insert_setup, TreeNodeNoValidate
  __pyx_t_1 = PyList_New(2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_INCREF(__pyx_n_s_insert_setup);
  __Pyx_GIVEREF(__pyx_n_s_insert_setup);
  PyList_SET_ITEM(__pyx_t_1, 0, __pyx_n_s_insert_setup);
  __Pyx_INCREF(__pyx_n_s_TreeNodeNoValidate);
  __Pyx_GIVEREF(__pyx_n_s_TreeNodeNoValidate);
  PyList_SET_ITEM(__pyx_t_1, 1, __pyx_n_s_TreeNodeNoValidate);
  __pyx_t_2 = __Pyx_Import(__pyx_n_s_bp__insert, __pyx_t_1, -1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_insert_setup); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_insert_setup, __pyx_t_1) < 0) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_ImportFrom(__pyx_t_2, __pyx_n_s_TreeNodeNoValidate); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_TreeNodeNoValidate, __pyx_t_1) < 0) __PYX_ERR(0, 5, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+006: from .. import to_skbio_treenode
  __pyx_t_2 = PyList_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_INCREF(__pyx_n_s_to_skbio_treenode);
  __Pyx_GIVEREF(__pyx_n_s_to_skbio_treenode);
  PyList_SET_ITEM(__pyx_t_2, 0, __pyx_n_s_to_skbio_treenode);
  __pyx_t_1 = __Pyx_Import(__pyx_n_s__22, __pyx_t_2, 2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = __Pyx_ImportFrom(__pyx_t_1, __pyx_n_s_to_skbio_treenode); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_to_skbio_treenode, __pyx_t_2) < 0) __PYX_ERR(0, 6, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+007: import pandas as pd
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_pandas, 0, -1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 7, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_pd, __pyx_t_1) < 0) __PYX_ERR(0, 7, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+008: import json
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_json, 0, -1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 8, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_json, __pyx_t_1) < 0) __PYX_ERR(0, 8, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+009: import skbio
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_skbio, 0, -1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 9, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_skbio, __pyx_t_1) < 0) __PYX_ERR(0, 9, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 010: cimport cython
 011: 
 012: 
 013: # this method described here was derived from Genesis
 014: # https://github.com/lczech/genesis
 015: # The license for Genesis is included herein under
 016: # Genesis_LICENSE.txt
+017: def insert_multifurcating(object placements, BP bptree):
/* Python wrapper */
static PyObject *__pyx_pw_2bp_3GPL_7_insert_1insert_multifurcating(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
static char __pyx_doc_2bp_3GPL_7_insert_insert_multifurcating[] = "Update the backbone, with multifurcation for edges with multiple queries\n\n    Parameters\n    ----------\n    placements : pd.DataFrame\n        jplace data represented as a DataFrame\n    bptree : bp.BP\n        An instance of a BP tree, this is expected to contain edge numbers\n        and correspond to the backbone for the jplace data\n\n    Note\n    ----\n    This method was derived directly from the Genesis codebase, and is\n    therefore GPL.\n\n    Returns\n    -------\n    skbio.TreeNode\n        A tree with the fragments placed\n    ";
static PyMethodDef __pyx_mdef_2bp_3GPL_7_insert_1insert_multifurcating = {"insert_multifurcating", (PyCFunction)(void*)(PyCFunctionWithKeywords)__pyx_pw_2bp_3GPL_7_insert_1insert_multifurcating, METH_VARARGS|METH_KEYWORDS, __pyx_doc_2bp_3GPL_7_insert_insert_multifurcating};
static PyObject *__pyx_pw_2bp_3GPL_7_insert_1insert_multifurcating(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
  PyObject *__pyx_v_placements = 0;
  struct __pyx_obj_2bp_3_bp_BP *__pyx_v_bptree = 0;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("insert_multifurcating (wrapper)", 0);
  {
    static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_placements,&__pyx_n_s_bptree,0};
    PyObject* values[2] = {0,0};
    if (unlikely(__pyx_kwds)) {
      Py_ssize_t kw_args;
      const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
      switch (pos_args) {
        case  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
        CYTHON_FALLTHROUGH;
        case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
        CYTHON_FALLTHROUGH;
        case  0: break;
        default: goto __pyx_L5_argtuple_error;
      }
      kw_args = PyDict_Size(__pyx_kwds);
      switch (pos_args) {
        case  0:
        if (likely((values[0] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_placements)) != 0)) kw_args--;
        else goto __pyx_L5_argtuple_error;
        CYTHON_FALLTHROUGH;
        case  1:
        if (likely((values[1] = __Pyx_PyDict_GetItemStr(__pyx_kwds, __pyx_n_s_bptree)) != 0)) kw_args--;
        else {
          __Pyx_RaiseArgtupleInvalid("insert_multifurcating", 1, 2, 2, 1); __PYX_ERR(0, 17, __pyx_L3_error)
        }
      }
      if (unlikely(kw_args > 0)) {
        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "insert_multifurcating") < 0)) __PYX_ERR(0, 17, __pyx_L3_error)
      }
    } else if (PyTuple_GET_SIZE(__pyx_args) != 2) {
      goto __pyx_L5_argtuple_error;
    } else {
      values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
      values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
    }
    __pyx_v_placements = values[0];
    __pyx_v_bptree = ((struct __pyx_obj_2bp_3_bp_BP *)values[1]);
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L5_argtuple_error:;
  __Pyx_RaiseArgtupleInvalid("insert_multifurcating", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 17, __pyx_L3_error)
  __pyx_L3_error:;
  __Pyx_AddTraceback("bp.GPL._insert.insert_multifurcating", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  if (unlikely(!__Pyx_ArgTypeTest(((PyObject *)__pyx_v_bptree), __pyx_ptype_2bp_3_bp_BP, 1, "bptree", 0))) __PYX_ERR(0, 17, __pyx_L1_error)
  __pyx_r = __pyx_pf_2bp_3GPL_7_insert_insert_multifurcating(__pyx_self, __pyx_v_placements, __pyx_v_bptree);
  int __pyx_lineno = 0;
  const char *__pyx_filename = NULL;
  int __pyx_clineno = 0;

  /* function exit code */
  goto __pyx_L0;
  __pyx_L1_error:;
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

static PyObject *__pyx_pf_2bp_3GPL_7_insert_insert_multifurcating(CYTHON_UNUSED PyObject *__pyx_self, PyObject *__pyx_v_placements, struct __pyx_obj_2bp_3_bp_BP *__pyx_v_bptree) {
  PyObject *__pyx_v_sktree = NULL;
  PyObject *__pyx_v_node_lookup = NULL;
  PyObject *__pyx_v_new_parents = NULL;
  PyObject *__pyx_v_parent_idx = NULL;
  PyObject *__pyx_v_new_bridges = NULL;
  PyObject *__pyx_v_bridge_idx = NULL;
  PyObject *__pyx_v_edge = NULL;
  PyObject *__pyx_v_edge_grp = NULL;
  PyObject *__pyx_v_existing_node = NULL;
  PyObject *__pyx_v_current_parent = NULL;
  PyObject *__pyx_v_new_parent = NULL;
  PyObject *__pyx_v_min_frag = NULL;
  PyObject *__pyx_v_min_pendant = NULL;
  PyObject *__pyx_v_min_distal = NULL;
  PyObject *__pyx_v_new_node = NULL;
  PyObject *__pyx_v_bridge = NULL;
  PyObject *__pyx_v_avg_prox_len = NULL;
  Py_ssize_t __pyx_v_i;
  PyObject *__pyx_v_frag_row = NULL;
  PyObject *__pyx_v_frag_node = NULL;
  CYTHON_UNUSED PyObject *__pyx_v__ = NULL;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("insert_multifurcating", 0);
  __Pyx_INCREF(__pyx_v_placements);
/* … */
  /* function exit code */
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_XDECREF(__pyx_t_5);
  __Pyx_XDECREF(__pyx_t_6);
  __Pyx_AddTraceback("bp.GPL._insert.insert_multifurcating", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_XDECREF(__pyx_v_sktree);
  __Pyx_XDECREF(__pyx_v_node_lookup);
  __Pyx_XDECREF(__pyx_v_new_parents);
  __Pyx_XDECREF(__pyx_v_parent_idx);
  __Pyx_XDECREF(__pyx_v_new_bridges);
  __Pyx_XDECREF(__pyx_v_bridge_idx);
  __Pyx_XDECREF(__pyx_v_edge);
  __Pyx_XDECREF(__pyx_v_edge_grp);
  __Pyx_XDECREF(__pyx_v_existing_node);
  __Pyx_XDECREF(__pyx_v_current_parent);
  __Pyx_XDECREF(__pyx_v_new_parent);
  __Pyx_XDECREF(__pyx_v_min_frag);
  __Pyx_XDECREF(__pyx_v_min_pendant);
  __Pyx_XDECREF(__pyx_v_min_distal);
  __Pyx_XDECREF(__pyx_v_new_node);
  __Pyx_XDECREF(__pyx_v_bridge);
  __Pyx_XDECREF(__pyx_v_avg_prox_len);
  __Pyx_XDECREF(__pyx_v_frag_row);
  __Pyx_XDECREF(__pyx_v_frag_node);
  __Pyx_XDECREF(__pyx_v__);
  __Pyx_XDECREF(__pyx_v_placements);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
/* … */
  __pyx_tuple__24 = PyTuple_Pack(23, __pyx_n_s_placements, __pyx_n_s_bptree, __pyx_n_s_sktree, __pyx_n_s_node_lookup, __pyx_n_s_new_parents, __pyx_n_s_parent_idx, __pyx_n_s_new_bridges, __pyx_n_s_bridge_idx, __pyx_n_s_edge, __pyx_n_s_edge_grp, __pyx_n_s_existing_node, __pyx_n_s_current_parent, __pyx_n_s_new_parent, __pyx_n_s_min_frag, __pyx_n_s_min_pendant, __pyx_n_s_min_distal, __pyx_n_s_new_node, __pyx_n_s_bridge, __pyx_n_s_avg_prox_len, __pyx_n_s_i, __pyx_n_s_frag_row, __pyx_n_s_frag_node, __pyx_n_s__23); if (unlikely(!__pyx_tuple__24)) __PYX_ERR(0, 17, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_tuple__24);
  __Pyx_GIVEREF(__pyx_tuple__24);
/* … */
  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_2bp_3GPL_7_insert_1insert_multifurcating, NULL, __pyx_n_s_bp_GPL__insert); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 17, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_insert_multifurcating, __pyx_t_1) < 0) __PYX_ERR(0, 17, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_codeobj__25 = (PyObject*)__Pyx_PyCode_New(2, 0, 23, 0, CO_OPTIMIZED|CO_NEWLOCALS, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__24, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_bp_GPL__insert_pyx, __pyx_n_s_insert_multifurcating, 17, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__25)) __PYX_ERR(0, 17, __pyx_L1_error)
 018:     """Update the backbone, with multifurcation for edges with multiple queries
 019: 
 020:     Parameters
 021:     ----------
 022:     placements : pd.DataFrame
 023:         jplace data represented as a DataFrame
 024:     bptree : bp.BP
 025:         An instance of a BP tree, this is expected to contain edge numbers
 026:         and correspond to the backbone for the jplace data
 027: 
 028:     Note
 029:     ----
 030:     This method was derived directly from the Genesis codebase, and is
 031:     therefore GPL.
 032: 
 033:     Returns
 034:     -------
 035:     skbio.TreeNode
 036:         A tree with the fragments placed
 037:     """
 038:     # TODO: profile, type and re-profile
+039:     placements, sktree, node_lookup = \
  __Pyx_DECREF_SET(__pyx_v_placements, __pyx_t_2);
  __pyx_t_2 = 0;
  __pyx_v_sktree = __pyx_t_5;
  __pyx_t_5 = 0;
  __pyx_v_node_lookup = __pyx_t_3;
  __pyx_t_3 = 0;
+040:         _insert_setup(placements, bptree, 'multifurcating')
  __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_insert_setup); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 40, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_3 = NULL;
  __pyx_t_4 = 0;
  if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_2))) {
    __pyx_t_3 = PyMethod_GET_SELF(__pyx_t_2);
    if (likely(__pyx_t_3)) {
      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2);
      __Pyx_INCREF(__pyx_t_3);
      __Pyx_INCREF(function);
      __Pyx_DECREF_SET(__pyx_t_2, function);
      __pyx_t_4 = 1;
    }
  }
  #if CYTHON_FAST_PYCALL
  if (PyFunction_Check(__pyx_t_2)) {
    PyObject *__pyx_temp[4] = {__pyx_t_3, __pyx_v_placements, ((PyObject *)__pyx_v_bptree), __pyx_n_s_multifurcating};
    __pyx_t_1 = __Pyx_PyFunction_FastCall(__pyx_t_2, __pyx_temp+1-__pyx_t_4, 3+__pyx_t_4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 40, __pyx_L1_error)
    __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
    __Pyx_GOTREF(__pyx_t_1);
  } else
  #endif
  #if CYTHON_FAST_PYCCALL
  if (__Pyx_PyFastCFunction_Check(__pyx_t_2)) {
    PyObject *__pyx_temp[4] = {__pyx_t_3, __pyx_v_placements, ((PyObject *)__pyx_v_bptree), __pyx_n_s_multifurcating};
    __pyx_t_1 = __Pyx_PyCFunction_FastCall(__pyx_t_2, __pyx_temp+1-__pyx_t_4, 3+__pyx_t_4); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 40, __pyx_L1_error)
    __Pyx_XDECREF(__pyx_t_3); __pyx_t_3 = 0;
    __Pyx_GOTREF(__pyx_t_1);
  } else
  #endif
  {
    __pyx_t_5 = PyTuple_New(3+__pyx_t_4); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 40, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_5);
    if (__pyx_t_3) {
      __Pyx_GIVEREF(__pyx_t_3); PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_3); __pyx_t_3 = NULL;
    }
    __Pyx_INCREF(__pyx_v_placements);
    __Pyx_GIVEREF(__pyx_v_placements);
    PyTuple_SET_ITEM(__pyx_t_5, 0+__pyx_t_4, __pyx_v_placements);
    __Pyx_INCREF(((PyObject *)__pyx_v_bptree));
    __Pyx_GIVEREF(((PyObject *)__pyx_v_bptree));
    PyTuple_SET_ITEM(__pyx_t_5, 1+__pyx_t_4, ((PyObject *)__pyx_v_bptree));
    __Pyx_INCREF(__pyx_n_s_multifurcating);
    __Pyx_GIVEREF(__pyx_n_s_multifurcating);
    PyTuple_SET_ITEM(__pyx_t_5, 2+__pyx_t_4, __pyx_n_s_multifurcating);
    __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_t_5, NULL); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 40, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_1);
    __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  }
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
    PyObject* sequence = __pyx_t_1;
    Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
    if (unlikely(size != 3)) {
      if (size > 3) __Pyx_RaiseTooManyValuesError(3);
      else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
      __PYX_ERR(0, 39, __pyx_L1_error)
    }
    #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
    if (likely(PyTuple_CheckExact(sequence))) {
      __pyx_t_2 = PyTuple_GET_ITEM(sequence, 0); 
      __pyx_t_5 = PyTuple_GET_ITEM(sequence, 1); 
      __pyx_t_3 = PyTuple_GET_ITEM(sequence, 2); 
    } else {
      __pyx_t_2 = PyList_GET_ITEM(sequence, 0); 
      __pyx_t_5 = PyList_GET_ITEM(sequence, 1); 
      __pyx_t_3 = PyList_GET_ITEM(sequence, 2); 
    }
    __Pyx_INCREF(__pyx_t_2);
    __Pyx_INCREF(__pyx_t_5);
    __Pyx_INCREF(__pyx_t_3);
    #else
    __pyx_t_2 = PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 39, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_2);
    __pyx_t_5 = PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 39, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_5);
    __pyx_t_3 = PySequence_ITEM(sequence, 2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 39, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_3);
    #endif
    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  } else {
    Py_ssize_t index = -1;
    __pyx_t_6 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 39, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_6);
    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
    __pyx_t_7 = Py_TYPE(__pyx_t_6)->tp_iternext;
    index = 0; __pyx_t_2 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_2)) goto __pyx_L3_unpacking_failed;
    __Pyx_GOTREF(__pyx_t_2);
    index = 1; __pyx_t_5 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_5)) goto __pyx_L3_unpacking_failed;
    __Pyx_GOTREF(__pyx_t_5);
    index = 2; __pyx_t_3 = __pyx_t_7(__pyx_t_6); if (unlikely(!__pyx_t_3)) goto __pyx_L3_unpacking_failed;
    __Pyx_GOTREF(__pyx_t_3);
    if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_6), 3) < 0) __PYX_ERR(0, 39, __pyx_L1_error)
    __pyx_t_7 = NULL;
    __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
    goto __pyx_L4_unpacking_done;
    __pyx_L3_unpacking_failed:;
    __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
    __pyx_t_7 = NULL;
    if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
    __PYX_ERR(0, 39, __pyx_L1_error)
    __pyx_L4_unpacking_done:;
  }
 041: 
 042:     # it is much faster to bulk allocate than construct on the fly, so let's
 043:     # do that
+044:     new_parents = [TreeNodeNoValidate()
  __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 44, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
/* … */
    __Pyx_GetModuleGlobalName(__pyx_t_5, __pyx_n_s_TreeNodeNoValidate); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 44, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_5);
    __pyx_t_6 = NULL;
    if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_5))) {
      __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_5);
      if (likely(__pyx_t_6)) {
        PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_5);
        __Pyx_INCREF(__pyx_t_6);
        __Pyx_INCREF(function);
        __Pyx_DECREF_SET(__pyx_t_5, function);
      }
    }
    __pyx_t_2 = (__pyx_t_6) ? __Pyx_PyObject_CallOneArg(__pyx_t_5, __pyx_t_6) : __Pyx_PyObject_CallNoArg(__pyx_t_5);
    __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
    if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 44, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
    if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_2))) __PYX_ERR(0, 44, __pyx_L1_error)
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+045:                    for _ in range(len(placements['edge_num'].unique()))]
  __pyx_t_5 = __Pyx_PyObject_Dict_GetItem(__pyx_v_placements, __pyx_n_s_edge_num); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 45, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s_unique); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 45, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __pyx_t_5 = NULL;
  if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_2))) {
    __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_2);
    if (likely(__pyx_t_5)) {
      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2);
      __Pyx_INCREF(__pyx_t_5);
      __Pyx_INCREF(function);
      __Pyx_DECREF_SET(__pyx_t_2, function);
    }
  }
  __pyx_t_3 = (__pyx_t_5) ? __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_t_5) : __Pyx_PyObject_CallNoArg(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
  if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 45, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_8 = PyObject_Length(__pyx_t_3); if (unlikely(__pyx_t_8 == ((Py_ssize_t)-1))) __PYX_ERR(0, 45, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_t_3 = PyInt_FromSsize_t(__pyx_t_8); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 45, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_t_2 = __Pyx_PyObject_CallOneArg(__pyx_builtin_range, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 45, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  if (likely(PyList_CheckExact(__pyx_t_2)) || PyTuple_CheckExact(__pyx_t_2)) {
    __pyx_t_3 = __pyx_t_2; __Pyx_INCREF(__pyx_t_3); __pyx_t_8 = 0;
    __pyx_t_9 = NULL;
  } else {
    __pyx_t_8 = -1; __pyx_t_3 = PyObject_GetIter(__pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 45, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_3);
    __pyx_t_9 = Py_TYPE(__pyx_t_3)->tp_iternext; if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 45, __pyx_L1_error)
  }
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  for (;;) {
    if (likely(!__pyx_t_9)) {
      if (likely(PyList_CheckExact(__pyx_t_3))) {
        if (__pyx_t_8 >= PyList_GET_SIZE(__pyx_t_3)) break;
        #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
        __pyx_t_2 = PyList_GET_ITEM(__pyx_t_3, __pyx_t_8); __Pyx_INCREF(__pyx_t_2); __pyx_t_8++; if (unlikely(0 < 0)) __PYX_ERR(0, 45, __pyx_L1_error)
        #else
        __pyx_t_2 = PySequence_ITEM(__pyx_t_3, __pyx_t_8); __pyx_t_8++; if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 45, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_2);
        #endif
      } else {
        if (__pyx_t_8 >= PyTuple_GET_SIZE(__pyx_t_3)) break;
        #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
        __pyx_t_2 = PyTuple_GET_ITEM(__pyx_t_3, __pyx_t_8); __Pyx_INCREF(__pyx_t_2); __pyx_t_8++; if (unlikely(0 < 0)) __PYX_ERR(0, 45, __pyx_L1_error)
        #else
        __pyx_t_2 = PySequence_ITEM(__pyx_t_3, __pyx_t_8); __pyx_t_8++; if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 45, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_2);
        #endif
      }
    } else {
      __pyx_t_2 = __pyx_t_9(__pyx_t_3);
      if (unlikely(!__pyx_t_2)) {
        PyObject* exc_type = PyErr_Occurred();
        if (exc_type) {
          if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();
          else __PYX_ERR(0, 45, __pyx_L1_error)
        }
        break;
      }
      __Pyx_GOTREF(__pyx_t_2);
    }
    __Pyx_XDECREF_SET(__pyx_v__, __pyx_t_2);
    __pyx_t_2 = 0;
/* … */
  }
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_v_new_parents = ((PyObject*)__pyx_t_1);
  __pyx_t_1 = 0;
+046:     parent_idx = 0
  __Pyx_INCREF(__pyx_int_0);
  __pyx_v_parent_idx = __pyx_int_0;
 047: 
+048:     new_bridges = [TreeNodeNoValidate()
  __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 48, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
/* … */
    __Pyx_GetModuleGlobalName(__pyx_t_2, __pyx_n_s_TreeNodeNoValidate); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 48, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_2);
    __pyx_t_5 = NULL;
    if (CYTHON_UNPACK_METHODS && unlikely(PyMethod_Check(__pyx_t_2))) {
      __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_2);
      if (likely(__pyx_t_5)) {
        PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2);
        __Pyx_INCREF(__pyx_t_5);
        __Pyx_INCREF(function);
        __Pyx_DECREF_SET(__pyx_t_2, function);
      }
    }
    __pyx_t_6 = (__pyx_t_5) ? __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_t_5) : __Pyx_PyObject_CallNoArg(__pyx_t_2);
    __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
    if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 48, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_6);
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
    if (unlikely(__Pyx_ListComp_Append(__pyx_t_1, (PyObject*)__pyx_t_6))) __PYX_ERR(0, 48, __pyx_L1_error)
    __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+049:                    for _ in range(placements['edge_num'].duplicated().sum())]
  __pyx_t_5 = __Pyx_PyObject_Dict_GetItem(__pyx_v_placements, __pyx_n_s_edge_num); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 49, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s_duplicated); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 49, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_6);
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __pyx_t_5 = NULL;
  if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_6))) {
    __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_6);
    if (likely(__pyx_t_5)) {
      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_6);
      __Pyx_INCREF(__pyx_t_5);
      __Pyx_INCREF(function);
      __Pyx_DECREF_SET(__pyx_t_6, function);
    }
  }
  __pyx_t_2 = (__pyx_t_5) ? __Pyx_PyObject_CallOneArg(__pyx_t_6, __pyx_t_5) : __Pyx_PyObject_CallNoArg(__pyx_t_6);
  __Pyx_XDECREF(__pyx_t_5); __pyx_t_5 = 0;
  if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 49, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
  __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_sum); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 49, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_6);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = NULL;
  if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_6))) {
    __pyx_t_2 = PyMethod_GET_SELF(__pyx_t_6);
    if (likely(__pyx_t_2)) {
      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_6);
      __Pyx_INCREF(__pyx_t_2);
      __Pyx_INCREF(function);
      __Pyx_DECREF_SET(__pyx_t_6, function);
    }
  }
  __pyx_t_3 = (__pyx_t_2) ? __Pyx_PyObject_CallOneArg(__pyx_t_6, __pyx_t_2) : __Pyx_PyObject_CallNoArg(__pyx_t_6);
  __Pyx_XDECREF(__pyx_t_2); __pyx_t_2 = 0;
  if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 49, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
  __pyx_t_6 = __Pyx_PyObject_CallOneArg(__pyx_builtin_range, __pyx_t_3); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 49, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_6);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  if (likely(PyList_CheckExact(__pyx_t_6)) || PyTuple_CheckExact(__pyx_t_6)) {
    __pyx_t_3 = __pyx_t_6; __Pyx_INCREF(__pyx_t_3); __pyx_t_8 = 0;
    __pyx_t_9 = NULL;
  } else {
    __pyx_t_8 = -1; __pyx_t_3 = PyObject_GetIter(__pyx_t_6); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 49, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_3);
    __pyx_t_9 = Py_TYPE(__pyx_t_3)->tp_iternext; if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 49, __pyx_L1_error)
  }
  __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
  for (;;) {
    if (likely(!__pyx_t_9)) {
      if (likely(PyList_CheckExact(__pyx_t_3))) {
        if (__pyx_t_8 >= PyList_GET_SIZE(__pyx_t_3)) break;
        #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
        __pyx_t_6 = PyList_GET_ITEM(__pyx_t_3, __pyx_t_8); __Pyx_INCREF(__pyx_t_6); __pyx_t_8++; if (unlikely(0 < 0)) __PYX_ERR(0, 49, __pyx_L1_error)
        #else
        __pyx_t_6 = PySequence_ITEM(__pyx_t_3, __pyx_t_8); __pyx_t_8++; if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 49, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_6);
        #endif
      } else {
        if (__pyx_t_8 >= PyTuple_GET_SIZE(__pyx_t_3)) break;
        #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
        __pyx_t_6 = PyTuple_GET_ITEM(__pyx_t_3, __pyx_t_8); __Pyx_INCREF(__pyx_t_6); __pyx_t_8++; if (unlikely(0 < 0)) __PYX_ERR(0, 49, __pyx_L1_error)
        #else
        __pyx_t_6 = PySequence_ITEM(__pyx_t_3, __pyx_t_8); __pyx_t_8++; if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 49, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_6);
        #endif
      }
    } else {
      __pyx_t_6 = __pyx_t_9(__pyx_t_3);
      if (unlikely(!__pyx_t_6)) {
        PyObject* exc_type = PyErr_Occurred();
        if (exc_type) {
          if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();
          else __PYX_ERR(0, 49, __pyx_L1_error)
        }
        break;
      }
      __Pyx_GOTREF(__pyx_t_6);
    }
    __Pyx_XDECREF_SET(__pyx_v__, __pyx_t_6);
    __pyx_t_6 = 0;
/* … */
  }
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_v_new_bridges = ((PyObject*)__pyx_t_1);
  __pyx_t_1 = 0;
+050:     bridge_idx = 0
  __Pyx_INCREF(__pyx_int_0);
  __pyx_v_bridge_idx = __pyx_int_0;
 051: 
+052:     for edge, edge_grp in placements.groupby('edge_num'):
  __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_placements, __pyx_n_s_groupby); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 52, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_t_6 = NULL;
  if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_3))) {
    __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_3);
    if (likely(__pyx_t_6)) {
      PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_3);
      __Pyx_INCREF(__pyx_t_6);
      __Pyx_INCREF(function);
      __Pyx_DECREF_SET(__pyx_t_3, function);
    }
  }
  __pyx_t_1 = (__pyx_t_6) ? __Pyx_PyObject_Call2Args(__pyx_t_3, __pyx_t_6, __pyx_n_s_edge_num) : __Pyx_PyObject_CallOneArg(__pyx_t_3, __pyx_n_s_edge_num);
  __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
  if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 52, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  if (likely(PyList_CheckExact(__pyx_t_1)) || PyTuple_CheckExact(__pyx_t_1)) {
    __pyx_t_3 = __pyx_t_1; __Pyx_INCREF(__pyx_t_3); __pyx_t_8 = 0;
    __pyx_t_9 = NULL;
  } else {
    __pyx_t_8 = -1; __pyx_t_3 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 52, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_3);
    __pyx_t_9 = Py_TYPE(__pyx_t_3)->tp_iternext; if (unlikely(!__pyx_t_9)) __PYX_ERR(0, 52, __pyx_L1_error)
  }
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  for (;;) {
    if (likely(!__pyx_t_9)) {
      if (likely(PyList_CheckExact(__pyx_t_3))) {
        if (__pyx_t_8 >= PyList_GET_SIZE(__pyx_t_3)) break;
        #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
        __pyx_t_1 = PyList_GET_ITEM(__pyx_t_3, __pyx_t_8); __Pyx_INCREF(__pyx_t_1); __pyx_t_8++; if (unlikely(0 < 0)) __PYX_ERR(0, 52, __pyx_L1_error)
        #else
        __pyx_t_1 = PySequence_ITEM(__pyx_t_3, __pyx_t_8); __pyx_t_8++; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 52, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_1);
        #endif
      } else {
        if (__pyx_t_8 >= PyTuple_GET_SIZE(__pyx_t_3)) break;
        #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
        __pyx_t_1 = PyTuple_GET_ITEM(__pyx_t_3, __pyx_t_8); __Pyx_INCREF(__pyx_t_1); __pyx_t_8++; if (unlikely(0 < 0)) __PYX_ERR(0, 52, __pyx_L1_error)
        #else
        __pyx_t_1 = PySequence_ITEM(__pyx_t_3, __pyx_t_8); __pyx_t_8++; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 52, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_1);
        #endif
      }
    } else {
      __pyx_t_1 = __pyx_t_9(__pyx_t_3);
      if (unlikely(!__pyx_t_1)) {
        PyObject* exc_type = PyErr_Occurred();
        if (exc_type) {
          if (likely(__Pyx_PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();
          else __PYX_ERR(0, 52, __pyx_L1_error)
        }
        break;
      }
      __Pyx_GOTREF(__pyx_t_1);
    }
    if ((likely(PyTuple_CheckExact(__pyx_t_1))) || (PyList_CheckExact(__pyx_t_1))) {
      PyObject* sequence = __pyx_t_1;
      Py_ssize_t size = __Pyx_PySequence_SIZE(sequence);
      if (unlikely(size != 2)) {
        if (size > 2) __Pyx_RaiseTooManyValuesError(2);
        else if (size >= 0) __Pyx_RaiseNeedMoreValuesError(size);
        __PYX_ERR(0, 52, __pyx_L1_error)
      }
      #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS
      if (likely(PyTuple_CheckExact(sequence))) {
        __pyx_t_6 = PyTuple_GET_ITEM(sequence, 0); 
        __pyx_t_2 = PyTuple_GET_ITEM(sequence, 1); 
      } else {
        __pyx_t_6 = PyList_GET_ITEM(sequence, 0); 
        __pyx_t_2 = PyList_GET_ITEM(sequence, 1); 
      }
      __Pyx_INCREF(__pyx_t_6);
      __Pyx_INCREF(__pyx_t_2);
      #else
      __pyx_t_6 = PySequence_ITEM(sequence, 0); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 52, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_6);
      __pyx_t_2 = PySequence_ITEM(sequence, 1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 52, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_2);
      #endif
      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
    } else {
      Py_ssize_t index = -1;
      __pyx_t_5 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 52, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_5);
      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
      __pyx_t_7 = Py_TYPE(__pyx_t_5)->tp_iternext;
      index = 0; __pyx_t_6 = __pyx_t_7(__pyx_t_5); if (unlikely(!__pyx_t_6)) goto __pyx_L11_unpacking_failed;
      __Pyx_GOTREF(__pyx_t_6);
      index = 1; __pyx_t_2 = __pyx_t_7(__pyx_t_5); if (unlikely(!__pyx_t_2)) goto __pyx_L11_unpacking_failed;
      __Pyx_GOTREF(__pyx_t_2);
      if (__Pyx_IternextUnpackEndCheck(__pyx_t_7(__pyx_t_5), 2) < 0) __PYX_ERR(0, 52, __pyx_L1_error)
      __pyx_t_7 = NULL;
      __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
      goto __pyx_L12_unpacking_done;
      __pyx_L11_unpacking_failed:;
      __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
      __pyx_t_7 = NULL;
      if (__Pyx_IterFinish() == 0) __Pyx_RaiseNeedMoreValuesError(index);
      __PYX_ERR(0, 52, __pyx_L1_error)
      __pyx_L12_unpacking_done:;
    }
    __Pyx_XDECREF_SET(__pyx_v_edge, __pyx_t_6);
    __pyx_t_6 = 0;
    __Pyx_XDECREF_SET(__pyx_v_edge_grp, __pyx_t_2);
    __pyx_t_2 = 0;
/* … */
  }
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
 053:         # update topology
+054:         existing_node = node_lookup[edge]
    __pyx_t_1 = __Pyx_PyObject_GetItem(__pyx_v_node_lookup, __pyx_v_edge); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 54, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_1);
    __Pyx_XDECREF_SET(__pyx_v_existing_node, __pyx_t_1);
    __pyx_t_1 = 0;
+055:         current_parent = existing_node.parent
    __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_existing_node, __pyx_n_s_parent); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 55, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_1);
    __Pyx_XDECREF_SET(__pyx_v_current_parent, __pyx_t_1);
    __pyx_t_1 = 0;
+056:         current_parent.remove(existing_node)
    __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_v_current_parent, __pyx_n_s_remove); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 56, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_2);
    __pyx_t_6 = NULL;
    if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_2))) {
      __pyx_t_6 = PyMethod_GET_SELF(__pyx_t_2);
      if (likely(__pyx_t_6)) {
        PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_2);
        __Pyx_INCREF(__pyx_t_6);
        __Pyx_INCREF(function);
        __Pyx_DECREF_SET(__pyx_t_2, function);
      }
    }
    __pyx_t_1 = (__pyx_t_6) ? __Pyx_PyObject_Call2Args(__pyx_t_2, __pyx_t_6, __pyx_v_existing_node) : __Pyx_PyObject_CallOneArg(__pyx_t_2, __pyx_v_existing_node);
    __Pyx_XDECREF(__pyx_t_6); __pyx_t_6 = 0;
    if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 56, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_1);
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 057: 
 058:         # get a pre-allocated node
+059:         new_parent = new_parents[parent_idx]
    __pyx_t_1 = __Pyx_PyObject_GetItem(__pyx_v_new_parents, __pyx_v_parent_idx); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 59, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_1);
    __Pyx_XDECREF_SET(__pyx_v_new_parent, __pyx_t_1);
    __pyx_t_1 = 0;
+060:         parent_idx += 1  # make sure we update our offset
    __pyx_t_1 = __Pyx_PyInt_AddObjC(__pyx_v_parent_idx, __pyx_int_1, 1, 1, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 60, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_1);
    __Pyx_DECREF_SET(__pyx_v_parent_idx, __pyx_t_1);
    __pyx_t_1 = 0;
 061: 
 062:         # gather detail on our minimal placement
+063:         min_frag = edge_grp.iloc[0]
    __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_edge_grp, __pyx_n_s_iloc); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 63, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_1);
    __pyx_t_2 = __Pyx_GetItemInt(__pyx_t_1, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 63, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
    __Pyx_XDECREF_SET(__pyx_v_min_frag, __pyx_t_2);
    __pyx_t_2 = 0;
+064:         min_pendant = min_frag['pendant_length']
    __pyx_t_2 = __Pyx_PyObject_Dict_GetItem(__pyx_v_min_frag, __pyx_n_s_pendant_length); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 64, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_XDECREF_SET(__pyx_v_min_pendant, __pyx_t_2);
    __pyx_t_2 = 0;
+065:         min_distal = min_frag['distal_length']
    __pyx_t_2 = __Pyx_PyObject_Dict_GetItem(__pyx_v_min_frag, __pyx_n_s_distal_length); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 65, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_XDECREF_SET(__pyx_v_min_distal, __pyx_t_2);
    __pyx_t_2 = 0;
+066:         new_node = min_frag['node']
    __pyx_t_2 = __Pyx_PyObject_Dict_GetItem(__pyx_v_min_frag, __pyx_n_s_node); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 66, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_XDECREF_SET(__pyx_v_new_node, __pyx_t_2);
    __pyx_t_2 = 0;
 067: 
+068:         if len(edge_grp) > 1:
    __pyx_t_10 = PyObject_Length(__pyx_v_edge_grp); if (unlikely(__pyx_t_10 == ((Py_ssize_t)-1))) __PYX_ERR(0, 68, __pyx_L1_error)
    __pyx_t_11 = ((__pyx_t_10 > 1) != 0);
    if (__pyx_t_11) {
/* … */
      goto __pyx_L13;
    }
 069:             # if we have multiple placements, we construct a node that contains
 070:             # all of the placements, and place this under a "bridge" such that
 071:             # it is a sister to the existing node.
 072: 
 073:             # derived from
 074:             # https://github.com/lczech/genesis/blob/98c064d8e3e2efaa97da33c9263f6fef3724f0a5/lib/genesis/placement/function/tree.cpp#L295
 075: 
 076:             # update topology
+077:             bridge = new_bridges[bridge_idx]
      __pyx_t_2 = __Pyx_PyObject_GetItem(__pyx_v_new_bridges, __pyx_v_bridge_idx); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 77, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_2);
      __Pyx_XDECREF_SET(__pyx_v_bridge, __pyx_t_2);
      __pyx_t_2 = 0;
+078:             bridge_idx += 1
      __pyx_t_2 = __Pyx_PyInt_AddObjC(__pyx_v_bridge_idx, __pyx_int_1, 1, 1, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 78, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_2);
      __Pyx_DECREF_SET(__pyx_v_bridge_idx, __pyx_t_2);
      __pyx_t_2 = 0;
+079:             bridge.append(existing_node)
      __pyx_t_12 = __Pyx_PyObject_Append(__pyx_v_bridge, __pyx_v_existing_node); if (unlikely(__pyx_t_12 == ((int)-1))) __PYX_ERR(0, 79, __pyx_L1_error)
+080:             bridge.append(new_parent)
      __pyx_t_12 = __Pyx_PyObject_Append(__pyx_v_bridge, __pyx_v_new_parent); if (unlikely(__pyx_t_12 == ((int)-1))) __PYX_ERR(0, 80, __pyx_L1_error)
+081:             current_parent.append(bridge)
      __pyx_t_12 = __Pyx_PyObject_Append(__pyx_v_current_parent, __pyx_v_bridge); if (unlikely(__pyx_t_12 == ((int)-1))) __PYX_ERR(0, 81, __pyx_L1_error)
+082:             new_parent.append(new_node)
      __pyx_t_12 = __Pyx_PyObject_Append(__pyx_v_new_parent, __pyx_v_new_node); if (unlikely(__pyx_t_12 == ((int)-1))) __PYX_ERR(0, 82, __pyx_L1_error)
 083: 
 084:             # Gappa uses the average distal for joining the node encompassing
 085:             # placements and existing back to the tree. As we are subtracting
 086:             # against the existing node, it is possible to introduce a negative
 087:             # branch length, so we enforce a minimum of 0.0
+088:             avg_prox_len = edge_grp['distal_length'].mean()
      __pyx_t_1 = __Pyx_PyObject_Dict_GetItem(__pyx_v_edge_grp, __pyx_n_s_distal_length); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 88, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_1);
      __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_mean); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 88, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_6);
      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
      __pyx_t_1 = NULL;
      if (CYTHON_UNPACK_METHODS && likely(PyMethod_Check(__pyx_t_6))) {
        __pyx_t_1 = PyMethod_GET_SELF(__pyx_t_6);
        if (likely(__pyx_t_1)) {
          PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_6);
          __Pyx_INCREF(__pyx_t_1);
          __Pyx_INCREF(function);
          __Pyx_DECREF_SET(__pyx_t_6, function);
        }
      }
      __pyx_t_2 = (__pyx_t_1) ? __Pyx_PyObject_CallOneArg(__pyx_t_6, __pyx_t_1) : __Pyx_PyObject_CallNoArg(__pyx_t_6);
      __Pyx_XDECREF(__pyx_t_1); __pyx_t_1 = 0;
      if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 88, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_2);
      __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
      __Pyx_XDECREF_SET(__pyx_v_avg_prox_len, __pyx_t_2);
      __pyx_t_2 = 0;
+089:             bridge.length = max(0.0, existing_node.length - avg_prox_len)
      __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_v_existing_node, __pyx_n_s_length); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 89, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_2);
      __pyx_t_6 = PyNumber_Subtract(__pyx_t_2, __pyx_v_avg_prox_len); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 89, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_6);
      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
      __pyx_t_13 = 0.0;
      __pyx_t_1 = PyFloat_FromDouble(__pyx_t_13); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 89, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_1);
      __pyx_t_5 = PyObject_RichCompare(__pyx_t_6, __pyx_t_1, Py_GT); __Pyx_XGOTREF(__pyx_t_5); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 89, __pyx_L1_error)
      __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
      __pyx_t_11 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_11 < 0)) __PYX_ERR(0, 89, __pyx_L1_error)
      __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
      if (__pyx_t_11) {
        __Pyx_INCREF(__pyx_t_6);
        __pyx_t_2 = __pyx_t_6;
      } else {
        __pyx_t_5 = PyFloat_FromDouble(__pyx_t_13); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 89, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_5);
        __pyx_t_2 = __pyx_t_5;
        __pyx_t_5 = 0;
      }
      __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
      __pyx_t_6 = __pyx_t_2;
      __Pyx_INCREF(__pyx_t_6);
      __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
      if (__Pyx_PyObject_SetAttrStr(__pyx_v_bridge, __pyx_n_s_length, __pyx_t_6) < 0) __PYX_ERR(0, 89, __pyx_L1_error)
      __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
 090: 
 091:             # update edges. the first node we place has a length of zero as
 092:             # its parent accounts for its pendant
+093:             existing_node.length = avg_prox_len
      if (__Pyx_PyObject_SetAttrStr(__pyx_v_existing_node, __pyx_n_s_length, __pyx_v_avg_prox_len) < 0) __PYX_ERR(0, 93, __pyx_L1_error)
+094:             new_parent.length = min_pendant
      if (__Pyx_PyObject_SetAttrStr(__pyx_v_new_parent, __pyx_n_s_length, __pyx_v_min_pendant) < 0) __PYX_ERR(0, 94, __pyx_L1_error)
+095:             new_node.length = 0.0
      if (__Pyx_PyObject_SetAttrStr(__pyx_v_new_node, __pyx_n_s_length, __pyx_float_0_0) < 0) __PYX_ERR(0, 95, __pyx_L1_error)
 096: 
+097:             for i in range(1, len(edge_grp)):
      __pyx_t_10 = PyObject_Length(__pyx_v_edge_grp); if (unlikely(__pyx_t_10 == ((Py_ssize_t)-1))) __PYX_ERR(0, 97, __pyx_L1_error)
      __pyx_t_14 = __pyx_t_10;
      for (__pyx_t_15 = 1; __pyx_t_15 < __pyx_t_14; __pyx_t_15+=1) {
        __pyx_v_i = __pyx_t_15;
 098:                 # gather placement detail
+099:                 frag_row = edge_grp.iloc[i]
        __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_v_edge_grp, __pyx_n_s_iloc); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 99, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_6);
        __pyx_t_2 = __Pyx_GetItemInt(__pyx_t_6, __pyx_v_i, Py_ssize_t, 1, PyInt_FromSsize_t, 0, 0, 0); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 99, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_2);
        __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
        __Pyx_XDECREF_SET(__pyx_v_frag_row, __pyx_t_2);
        __pyx_t_2 = 0;
+100:                 frag_node = frag_row['node']
        __pyx_t_2 = __Pyx_PyObject_Dict_GetItem(__pyx_v_frag_row, __pyx_n_s_node); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 100, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_2);
        __Pyx_XDECREF_SET(__pyx_v_frag_node, __pyx_t_2);
        __pyx_t_2 = 0;
 101: 
 102:                 # update topology
+103:                 new_parent.append(frag_node)
        __pyx_t_12 = __Pyx_PyObject_Append(__pyx_v_new_parent, __pyx_v_frag_node); if (unlikely(__pyx_t_12 == ((int)-1))) __PYX_ERR(0, 103, __pyx_L1_error)
 104: 
 105:                 # update the branch length. Note that "frag_node" has its
 106:                 # length first set to its pendant during the preallocation
 107:                 # step. we subtract the parent pendant to avoid counting
 108:                 # extra edge length. This should never be < 0 as the data
 109:                 # are presorted by pendant length, so the fragment evaluated
 110:                 # here is assured to have a > length than the pendant used
 111:                 # with the parent.
+112:                 frag_node.length = frag_node.length - new_parent.length
        __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_v_frag_node, __pyx_n_s_length); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 112, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_2);
        __pyx_t_6 = __Pyx_PyObject_GetAttrStr(__pyx_v_new_parent, __pyx_n_s_length); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 112, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_6);
        __pyx_t_5 = PyNumber_Subtract(__pyx_t_2, __pyx_t_6); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 112, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_5);
        __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
        __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
        if (__Pyx_PyObject_SetAttrStr(__pyx_v_frag_node, __pyx_n_s_length, __pyx_t_5) < 0) __PYX_ERR(0, 112, __pyx_L1_error)
        __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
      }
 113:         else:
 114:             # if we only have a single placement, we place the fragment as a
 115:             # sister to the existing node.
 116: 
 117:             # update topology
+118:             current_parent.append(new_parent)
    /*else*/ {
      __pyx_t_12 = __Pyx_PyObject_Append(__pyx_v_current_parent, __pyx_v_new_parent); if (unlikely(__pyx_t_12 == ((int)-1))) __PYX_ERR(0, 118, __pyx_L1_error)
+119:             new_parent.append(existing_node)
      __pyx_t_12 = __Pyx_PyObject_Append(__pyx_v_new_parent, __pyx_v_existing_node); if (unlikely(__pyx_t_12 == ((int)-1))) __PYX_ERR(0, 119, __pyx_L1_error)
+120:             new_parent.append(new_node)
      __pyx_t_12 = __Pyx_PyObject_Append(__pyx_v_new_parent, __pyx_v_new_node); if (unlikely(__pyx_t_12 == ((int)-1))) __PYX_ERR(0, 120, __pyx_L1_error)
 121: 
 122:             # update branch lengths
+123:             new_node.length = min_pendant
      if (__Pyx_PyObject_SetAttrStr(__pyx_v_new_node, __pyx_n_s_length, __pyx_v_min_pendant) < 0) __PYX_ERR(0, 123, __pyx_L1_error)
+124:             new_parent.length = existing_node.length - min_distal
      __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_v_existing_node, __pyx_n_s_length); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 124, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_5);
      __pyx_t_6 = PyNumber_Subtract(__pyx_t_5, __pyx_v_min_distal); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 124, __pyx_L1_error)
      __Pyx_GOTREF(__pyx_t_6);
      __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
      if (__Pyx_PyObject_SetAttrStr(__pyx_v_new_parent, __pyx_n_s_length, __pyx_t_6) < 0) __PYX_ERR(0, 124, __pyx_L1_error)
      __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+125:             existing_node.length = min_distal
      if (__Pyx_PyObject_SetAttrStr(__pyx_v_existing_node, __pyx_n_s_length, __pyx_v_min_distal) < 0) __PYX_ERR(0, 125, __pyx_L1_error)
    }
    __pyx_L13:;
 126: 
+127:     return sktree
  __Pyx_XDECREF(__pyx_r);
  __Pyx_INCREF(__pyx_v_sktree);
  __pyx_r = __pyx_v_sktree;
  goto __pyx_L0;