[med-svn] [python-skbio] 01/03: New upstream version 0.5.1
Kevin Murray
daube-guest at moszumanska.debian.org
Tue Nov 15 08:04:55 UTC 2016
This is an automated email from the git hooks/post-receive script.
daube-guest pushed a commit to branch master
in repository python-skbio.
commit 1ee78f3c3dcd25e274442250f9b2353d5116b4d1
Author: Kevin Murray <spam at kdmurray.id.au>
Date: Tue Nov 15 18:56:28 2016 +1100
New upstream version 0.5.1
---
CHANGELOG.md | 39 +-
checklist.py | 26 +-
ci/matplotlibrc | 1 +
doc/source/index.rst | 1 +
doc/source/metadata.rst | 1 +
licenses/bx_python.txt | 20 +
setup.py | 15 +-
skbio/__init__.py | 5 +-
skbio/alignment/_pairwise.py | 2 +-
skbio/diversity/__init__.py | 5 +-
skbio/diversity/_block.py | 319 +
skbio/diversity/_driver.py | 2 +-
skbio/diversity/tests/test_block.py | 207 +
skbio/io/__init__.py | 1 +
skbio/io/format/_blast.py | 29 +-
skbio/io/format/_sequence_feature_vocabulary.py | 351 +
skbio/io/format/genbank.py | 557 +-
skbio/io/format/tests/data/genbank_multi_records | 8 +-
skbio/io/format/tests/data/genbank_single_record | 6 +-
skbio/io/format/tests/test_fasta.py | 5 +-
skbio/io/format/tests/test_genbank.py | 305 +-
.../tests/test_sequence_feature_vocabulary.py | 102 +
skbio/metadata/__init__.py | 14 +
skbio/metadata/_intersection.c | 10555 +++++++++++++++++++
skbio/metadata/_intersection.pyx | 529 +
skbio/metadata/_interval.py | 978 ++
skbio/metadata/_mixin.py | 135 +-
skbio/metadata/_repr.py | 21 +-
skbio/metadata/_testing.py | 315 +
skbio/metadata/tests/test_intersection.py | 241 +
skbio/metadata/tests/test_interval.py | 718 ++
skbio/metadata/tests/test_mixin.py | 42 +-
skbio/sequence/_dna.py | 14 +-
skbio/sequence/_grammared_sequence.py | 9 +-
skbio/sequence/_nucleotide_mixin.py | 17 +-
skbio/sequence/_protein.py | 4 +
skbio/sequence/_rna.py | 9 +
skbio/sequence/_sequence.py | 237 +-
skbio/sequence/tests/test_dna.py | 10 +-
skbio/sequence/tests/test_grammared_sequence.py | 9 +-
skbio/sequence/tests/test_nucleotide_sequences.py | 35 +-
skbio/sequence/tests/test_rna.py | 10 +-
skbio/sequence/tests/test_sequence.py | 325 +-
skbio/stats/composition.py | 47 +-
skbio/stats/distance/_base.py | 132 +-
skbio/stats/distance/tests/test_base.py | 203 +-
skbio/stats/power.py | 6 +-
skbio/stats/tests/test_composition.py | 72 +
skbio/stats/tests/test_power.py | 15 +-
skbio/tree/_tree.py | 44 +-
skbio/tree/tests/test_tree.py | 26 +
skbio/util/__init__.py | 9 +-
skbio/util/_decorator.py | 23 +-
skbio/util/_misc.py | 196 +-
skbio/util/tests/test_misc.py | 82 +-
55 files changed, 15899 insertions(+), 1190 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a9f74af..7191ba8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,42 @@
# scikit-bio changelog
+## Version 0.5.1 (2016-11-12)
+
+### Features
+* Added `IntervalMetadata` and `Interval` classes in `skbio.metadata` to store, query, and manipulate information of a sub-region of a sequence. ([#1414](https://github.com/biocore/scikit-bio/issues/1414))
+* `Sequence` and its child classes (including `GrammaredSequence`, `RNA`, `DNA`, `Protein`) now accept `IntervalMetadata` in their constructor API. Some of their relevant methods are also updated accordingly. ([#1430](https://github.com/biocore/scikit-bio/pull/1430))
+* GenBank parser now reads and writes `Sequence` or its subclass objects with `IntervalMetadata`. ([#1440](https://github.com/biocore/scikit-bio/pull/1440))
+* `DissimilarityMatrix` now has a new constructor method called `from_iterable`. ([#1343](https://github.com/biocore/scikit-bio/issues/1343)).
+* `DissimilarityMatrix` now allows non-hollow matrices. ([#1343](https://github.com/biocore/scikit-bio/issues/1343)).
+* `DistanceMatrix.from_iterable` now accepts a `validate=True` parameter. ([#1343](https://github.com/biocore/scikit-bio/issues/1343)).
+* ``DistanceMatrix`` now has a new method called ``to_series`` to create a ``pandas.Series`` from a ``DistanceMatrix`` ([#1397](https://github.com/biocore/scikit-bio/issues/1397)).
+* Added parallel beta diversity calculation support via `skbio.diversity.block_beta_diversity`. The issue and idea is discussed in ([#1181](https://github.com/biocore/scikit-bio/issues/1181), while the actual code changes are in [#1352](https://github.com/biocore/scikit-bio/pull/1352)).
+
+### Backward-incompatible changes [stable]
+* The constructor API for `Sequence` and its child classes (including `GrammaredSequence`, `RNA`, `DNA`, `Protein`) are changed from `(sequence, metadata=None, positional_metadata=None, lowercase=False)` to `(sequence, metadata=None, positional_metadata=None, interval_metadata=None, lowercase=False)`
+
+ The changes are made to allow these classes to adopt `IntervalMetadata` object for interval features on the sequence. The `interval_metadata` parameter is added imediately after `positional_metadata` instead of appended to the end, because it is more natural and logical and, more importantly, because it is unlikely in practice to break user code. A user's code would break only if they had supplied `metadata`, `postional_metadata`, and `lowercase` parameters positionally. In the unlikel [...]
+
+### Backward-incompatible changes [experimental]
+* Modifying basis handling in `skbio.stats.composition.ilr_inv` prior to checking for orthogonality. Now the basis is strictly assumed to be in the Aitchison simplex.
+* `DistanceMatrix.from_iterable` default behavior is now to validate matrix by computing all pairwise distances. Pass `validate=False` to get the previous behavior (no validation, but faster execution).([#1343](https://github.com/biocore/scikit-bio/issues/1343)).
+* GenBank I/O now parses sequence features into the attribute of `interval_metadata` instead of `positiona_metadata`. And the key of `FEATURES` is removed from `metadata` attribute.
+
+### Performance enhancements
+* `TreeNode.shear` was rewritten for approximately a 25% performance increase. ([#1399](https://github.com/biocore/scikit-bio/pull/1399))
+* The `IntervalMetadata` allows dramatic decrease in memory usage in reading GenBank files of feature rich sequences. ([#1159](https://github.com/biocore/scikit-bio/issues/1159))
+
+### Bug fixes
+
+* `skbio.tree.TreeNode.prune` and implicitly `skbio.tree.TreeNode.shear` were not handling a situation in which a parent was validly removed during pruning operations as may happen if the resulting subtree does not include the root. Previously, an `AttributeError` would raise as `parent` would be `None` in this situation.
+* numpy linking was fixed for installation under El Capitan.
+* A bug was introduced in #1398 into `TreeNode.prune` and fixed in #1416 in which, under the special case of a single descendent existing from the root, the resulting children parent references were not updated. The cause of the bug was a call made to `self.children.extend` as opposed to `self.extend` where the former is a `list.extend` without knowledge of the tree, while the latter is `TreeNode.extend` which is able to adjust references to `self.parent`.
+
+### Miscellaneous
+
+* Removed deprecated functions from `skbio.util`: `is_casava_v180_or_later`, `remove_files`, and `create_dir`.
+* Removed deprecated `skbio.Sequence.copy` method.
+
## Version 0.5.0 (2016-06-14)
**IMPORTANT**: scikit-bio is no longer compatible with Python 2. scikit-bio is compatible with Python 3.4 and later.
@@ -16,7 +53,7 @@
* `TemporaryFile` and `NamedTemporaryFile` are now supported IO sources for `skbio.io` and related functionality. ([#1291](https://github.com/biocore/scikit-bio/issues/1291))
* Added `tree_node_class=TreeNode` parameter to `skbio.tree.majority_rule` to support returning consensus trees of type `TreeNode` (the default) or a type that has the same interface as `TreeNode` (e.g. `TreeNode` subclasses) ([#1193](https://github.com/biocore/scikit-bio/pull/1193))
* `TreeNode.from_linkage_matrix` and `TreeNode.from_taxonomy` now support constructing `TreeNode` subclasses. `TreeNode.bifurcate` now supports `TreeNode` subclasses ([#1193](https://github.com/biocore/scikit-bio/pull/1193))
-* The `ignore_metadata` keyword has been added to `TablueMSA.iter_positions` to improve performance when metadata is not necessary.
+* The `ignore_metadata` keyword has been added to `TabularMSA.iter_positions` to improve performance when metadata is not necessary.
* Pairwise aligners in `skbio.alignment` now propagate per-sequence `metadata` objects (this does not include `positional_metadata`).
### Backward-incompatible changes [stable]
diff --git a/checklist.py b/checklist.py
index 3daa4cd..e79ace5 100755
--- a/checklist.py
+++ b/checklist.py
@@ -15,6 +15,7 @@ import subprocess
import sys
import ast
import tokenize
+import warnings
import dateutil.parser
@@ -23,6 +24,11 @@ if sys.version_info.major != 3:
"running Python %d." % sys.version_info.major)
+class ChecklistWarning(Warning):
+ """General warning class for warnings raised by checklist.py."""
+ pass
+
+
def main():
"""Go on a power trip by nitpicking the scikit-bio repo.
@@ -154,6 +160,12 @@ class CopyrightHeadersValidator(RepoValidator):
See the current standard for scikit-bio's copyright headers at
``http://scikit-bio.org/docs/latest/development/new_module.html``
+ Individual files are ignored if the first line in the file is exactly:
+
+ # checklist.py:CopyrightHeadersValidator IGNORE
+
+ If a file is ignored, a ``ChecklistWarning`` is raised.
+
Parameters
----------
skip_dirs : iterable of str, optional
@@ -190,8 +202,20 @@ class CopyrightHeadersValidator(RepoValidator):
for _file in files:
if not _file.endswith('.py'):
continue
+
pos = 0
- f = open(os.path.join(root, _file))
+ filepath = os.path.join(root, _file)
+ f = open(filepath)
+
+ first_line = f.readline().rstrip('\n')
+ if first_line == '# checklist.py:CopyrightHeadersValidator IGNORE':
+ warnings.warn(
+ "File %s has IGNORE directive. Ignoring scikit-bio "
+ "copyright header validation." % filepath,
+ ChecklistWarning)
+ continue
+
+ f.seek(0)
tokens = list(tokenize.generate_tokens(f.readline))
# A module docstring is fully described using just two tokens: the
diff --git a/ci/matplotlibrc b/ci/matplotlibrc
new file mode 100644
index 0000000..88a8365
--- /dev/null
+++ b/ci/matplotlibrc
@@ -0,0 +1 @@
+backend: Agg
diff --git a/doc/source/index.rst b/doc/source/index.rst
index f076669..31c9102 100644
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -18,6 +18,7 @@ API Reference
workflow
diversity
stats
+ metadata
util
User Documentation
diff --git a/doc/source/metadata.rst b/doc/source/metadata.rst
new file mode 100644
index 0000000..7fa2516
--- /dev/null
+++ b/doc/source/metadata.rst
@@ -0,0 +1 @@
+.. automodule:: skbio.metadata
diff --git a/licenses/bx_python.txt b/licenses/bx_python.txt
new file mode 100644
index 0000000..3014db2
--- /dev/null
+++ b/licenses/bx_python.txt
@@ -0,0 +1,20 @@
+Copyright (c) 2005-2015 The Pennsylvania State University
+Copyright (c) 2013-2015 The Johns Hopkins University
+
+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.
diff --git a/setup.py b/setup.py
index e2c5aa6..366e058 100644
--- a/setup.py
+++ b/setup.py
@@ -64,6 +64,8 @@ ext = '.pyx' if USE_CYTHON else '.c'
# details. This acts as a workaround until the next Python 3 release -- thanks
# Wolfgang Maier (wolma) for the workaround!
ssw_extra_compile_args = ['-Wno-error=declaration-after-statement']
+if sys.platform == 'win32':
+ ssw_extra_compile_args = []
# Users with i686 architectures have reported that adding this flag allows
# SSW to be compiled. See https://github.com/biocore/scikit-bio/issues/409 and
@@ -72,14 +74,19 @@ if platform.machine() == 'i686':
ssw_extra_compile_args.append('-msse2')
extensions = [
+ Extension("skbio.metadata._intersection",
+ ["skbio/metadata/_intersection" + ext]),
Extension("skbio.stats.__subsample",
- ["skbio/stats/__subsample" + ext]),
+ ["skbio/stats/__subsample" + ext],
+ include_dirs=[np.get_include()]),
Extension("skbio.alignment._ssw_wrapper",
["skbio/alignment/_ssw_wrapper" + ext,
"skbio/alignment/_lib/ssw.c"],
- extra_compile_args=ssw_extra_compile_args),
+ extra_compile_args=ssw_extra_compile_args,
+ include_dirs=[np.get_include()]),
Extension("skbio.diversity._phylogenetic",
- ["skbio/diversity/_phylogenetic" + ext])
+ ["skbio/diversity/_phylogenetic" + ext],
+ include_dirs=[np.get_include()])
]
if USE_CYTHON:
@@ -88,7 +95,7 @@ if USE_CYTHON:
setup(name='scikit-bio',
version=version,
- license='BSD',
+ license='BSD-3-Clause',
description=description,
long_description=long_description,
author="scikit-bio development team",
diff --git a/skbio/__init__.py b/skbio/__init__.py
index b2e7a78..4910cbc 100644
--- a/skbio/__init__.py
+++ b/skbio/__init__.py
@@ -18,14 +18,15 @@ from skbio.alignment import local_pairwise_align_ssw, TabularMSA
from skbio.tree import TreeNode, nj
from skbio.io import read, write
from skbio.stats.ordination import OrdinationResults
-
+import skbio.diversity # noqa
+import skbio.stats.evolve # noqa
__all__ = ['Sequence', 'DNA', 'RNA', 'Protein', 'GeneticCode',
'DistanceMatrix', 'local_pairwise_align_ssw', 'TabularMSA',
'TreeNode', 'nj', 'read', 'write', 'OrdinationResults']
__credits__ = "https://github.com/biocore/scikit-bio/graphs/contributors"
-__version__ = "0.5.0"
+__version__ = "0.5.1"
mottos = [
# 03/15/2014
diff --git a/skbio/alignment/_pairwise.py b/skbio/alignment/_pairwise.py
index c2d7e24..fb88015 100644
--- a/skbio/alignment/_pairwise.py
+++ b/skbio/alignment/_pairwise.py
@@ -735,7 +735,7 @@ def local_pairwise_align_ssw(sequence1, sequence2, **kwargs):
return msa, alignment.optimal_alignment_score, start_end
- at deprecated(as_of="0.4.0", until="0.5.1",
+ at deprecated(as_of="0.4.0", until="0.5.2",
reason="Will be replaced by a SubstitutionMatrix class. To track "
"progress, see [#161]"
"(https://github.com/biocore/scikit-bio/issues/161).")
diff --git a/skbio/diversity/__init__.py b/skbio/diversity/__init__.py
index 52b3e4a..bd42788 100644
--- a/skbio/diversity/__init__.py
+++ b/skbio/diversity/__init__.py
@@ -163,6 +163,7 @@ Functions
alpha_diversity
beta_diversity
partial_beta_diversity
+ block_beta_diversity
get_alpha_diversity_metrics
get_beta_diversity_metrics
@@ -402,8 +403,10 @@ from skbio.util import TestRunner
from ._driver import (alpha_diversity, beta_diversity, partial_beta_diversity,
get_alpha_diversity_metrics, get_beta_diversity_metrics)
+from ._block import block_beta_diversity
__all__ = ["alpha_diversity", "beta_diversity", "get_alpha_diversity_metrics",
- "get_beta_diversity_metrics", "partial_beta_diversity"]
+ "get_beta_diversity_metrics", "partial_beta_diversity",
+ "block_beta_diversity"]
test = TestRunner(__file__).test
diff --git a/skbio/diversity/_block.py b/skbio/diversity/_block.py
new file mode 100644
index 0000000..381b370
--- /dev/null
+++ b/skbio/diversity/_block.py
@@ -0,0 +1,319 @@
+# ----------------------------------------------------------------------------
+# Copyright (c) 2013--, scikit-bio development team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+# ----------------------------------------------------------------------------
+
+import numpy as np
+
+from skbio.util._decorator import experimental
+from skbio.diversity._driver import partial_beta_diversity
+from skbio.stats.distance import DistanceMatrix
+from skbio.diversity._util import _validate_counts_matrix
+
+
+def _generate_id_blocks(ids, k=64):
+ """Generate blocks of IDs that map into a DistanceMatrix
+
+ Parameters
+ ----------
+ ids : Iterable object
+ An iterable of IDs of whatever type.
+ k : int, optional
+ The size of a block to generate IDs for, defaults to 64.
+
+ Notes
+ -----
+ This method is intended to facilitate partial beta diversity calculations.
+ Blocks of IDs are generated from the upper triangle of the subsequent
+ distance matrix. For instance, given the following distance matrix with
+ IDs {A, B, C, D, E}:
+
+ A B C D E
+ A 0 # # # #
+ B # 0 # # #
+ C # # 0 # #
+ D # # # 0 #
+ E # # # # 0
+
+ The goal of this method is to generate tuples of IDs of at most size k over
+ the upper triangle which correspond to blocks of the matrix to compute. IDs
+ are remapped as well into integers to facilitate downstream indexing.
+ Given k=3, the following ID tuples would be generated:
+
+ ((0, 1, 2), (0, 1, 2))
+ ((0, 1, 2), (3, 4))
+ ((3, 4), (3, 4))
+
+ This method is not responsible for describing which specific pairs of IDs
+ are to be computed, only the subset of the matrix of interest.
+
+ Returns
+ -------
+ tuple of 1D np.array
+ Index 0 contains the row IDs, and index 1 contains the column IDs
+ """
+ n = len(ids)
+ ids_idx = np.arange(n)
+
+ for row_start in range(0, n, k):
+ for col_start in range(row_start, n, k):
+ row_ids = ids_idx[row_start:row_start + k]
+ col_ids = ids_idx[col_start:col_start + k]
+
+ yield (row_ids, col_ids)
+
+
+def _block_party(counts=None, row_ids=None, col_ids=None, **kwargs):
+ """Subset counts to relevant rows and columns
+
+ Parameters
+ ----------
+ counts : 2D array_like of ints or floats
+ Matrix containing count/abundance data where each row contains counts
+ of OTUs in a given sample.
+ row_ids : 1D np.ndarray of int
+ Block row IDs to keep in the counts matrix.
+ col_ids : 1D np.ndarray of int
+ Block column IDs to keep in the counts matrix. Note, these correspond
+ to rows in the counts matrix, but columns in a subsequent distance
+ matrix.
+ kwargs : dict
+ Keyword arguments containing information about the block to compute.
+
+ Returns
+ -------
+ dict
+ kwargs that describe the block to compute. A filtered ``counts`` matrix
+ is stored in kwargs. If applicable, a filtered ``tree`` and ``otu_ids``
+ are also stored.
+ """
+ ids_to_keep = np.unique(np.hstack([row_ids, col_ids]))
+
+ # create a view of the relevant samples
+ counts_block = counts[ids_to_keep]
+
+ # remove from the block any empty observations
+ # NOTE: this will perform an implicit copy
+ nonzero_cols = (counts_block != 0).any(axis=0)
+ counts_block = counts_block[:, nonzero_cols]
+
+ kwargs['counts'] = counts_block
+ kwargs['ids'] = ids_to_keep
+
+ if 'tree' in kwargs and 'otu_ids' in kwargs:
+ kwargs['otu_ids'] = np.asarray(kwargs['otu_ids'])[nonzero_cols]
+ kwargs['tree'] = kwargs['tree'].shear(kwargs['otu_ids'])
+
+ return kwargs
+
+
+def _pairs_to_compute(rids, cids):
+ """Determine the pairs of samples to compute distances between
+
+ Parameters
+ ----------
+ rids : Iterable
+ The row IDs in the partial pairwise computation.
+ cids : Iterable
+ The column IDs in the partial pairwise computation.
+
+ Raises
+ ------
+ ValueError
+ When determining ID pairs for blocks that fall outside of the diagonal
+ of the resulting distance matrix, if a pair corresponds to the lower
+ triangle, complain loudly.
+
+ Returns
+ -------
+ list of tuple
+ The ID pairs to compute distances between.
+ """
+ # if identical, gather the upper triangle
+ if len(rids) == len(cids) and (rids == cids).all():
+ return [(i, j) for idx, i in enumerate(rids) for j in rids[idx+1:]]
+
+ # otherwise, grab pairwise combinations disregarding the diagonal
+ else:
+ if set(rids).intersection(set(cids)):
+ raise ValueError("Attempting to compute a lower triangle")
+ return [(i, j) for i in rids for j in cids if i != j]
+
+
+def _block_kwargs(**kwargs):
+ """Construct arguments describing a block to compute
+
+ Returns
+ -------
+ dict
+ The parameters for the block of the distance matrix to compute.
+ """
+ valid_block_keys = {'counts', 'ids', 'tree', 'otu_ids', 'metric',
+ 'id_pairs', 'validate'}
+ for row_ids, col_ids in _generate_id_blocks(kwargs['ids'], kwargs['k']):
+ id_pairs = _pairs_to_compute(row_ids, col_ids)
+ if id_pairs:
+ kw = {k: v for k, v in kwargs.items() if k in valid_block_keys}
+ kw['id_pairs'] = id_pairs
+ kw['row_ids'] = row_ids
+ kw['col_ids'] = col_ids
+
+ yield kw
+
+
+def _block_compute(**kwargs):
+ """Compute a block within the resulting distance matrix
+
+ Notes
+ -----
+ This method encapsulates the two expensive operations to perform for each
+ block, namely, the "shearing" of the phylogenetic tree to correspond to
+ only the OTUs of interest, and the actual beta diversity calculations.
+
+ Returns
+ -------
+ DistanceMatrix
+ """
+ block_kw = _block_party(**kwargs)
+
+ return partial_beta_diversity(**block_kw)
+
+
+def _map(func, kw_gen):
+ """Map a function over arguments
+
+ Note
+ ----
+ builtin map does not allow for mapping with kwargs.
+
+ Parallel uses of block decomposition will likely replace this method with
+ one which can distribute compute.
+ """
+ for kwargs in kw_gen:
+ yield func(**kwargs)
+
+
+def _reduce(blocks):
+ """Reduce an iterable of partial distance matrices into a full matrix
+
+ Note, the reduce doesn't actually care about what pairs are computed
+ so if a distance between pairs exists multiple times, it'll get
+ added. as such, this reduction is only safe to perform if by
+ the block_beta_diversity method which assures that distances are not
+ computed multiple times.
+ """
+ all_blocks = list(blocks)
+
+ # Determine the maximum integer ID observed in the blocks. There exists a
+ # 1-1 mapping between the integer ID and a sample ID. We increment by 1
+ # as the integer ID space begins with zero, and we'll be using this value
+ # to determine the size of the resulting full distance matrix.
+ n_ids = max(map(lambda x: max(x.ids), all_blocks)) + 1
+
+ mat = np.zeros((n_ids, n_ids), dtype=float)
+
+ # TODO: something smarter.
+ for block in all_blocks:
+ n_blk_ids = len(block.ids)
+
+ # get the corresponding coordinates in the master matrix
+ master_idx = [(i, j) for row, i in enumerate(block.ids)
+ for j in block.ids[row+1:]]
+
+ # get the corresponding coordinates within the current block
+ block_idx = [(i, j) for row, i in enumerate(range(n_blk_ids))
+ for j in range(row+1, n_blk_ids)]
+
+ for (m_i, m_j), (b_i, b_j) in zip(master_idx, block_idx):
+ mat[m_i, m_j] += block.data[b_i, b_j]
+
+ return DistanceMatrix(mat + mat.T, list(range(n_ids)))
+
+
+ at experimental(as_of="0.5.1")
+def block_beta_diversity(metric, counts, ids, validate=True, k=64,
+ reduce_f=None, map_f=None, **kwargs):
+ """Perform a block-decomposition beta diversity calculation
+
+ Parameters
+ ----------
+ metric : str or callable
+ The pairwise distance function to apply. If ``metric`` is a string, it
+ must be resolvable by scikit-bio (e.g., UniFrac methods), or must be
+ callable.
+ counts : 2D array_like of ints or floats
+ Matrix containing count/abundance data where each row contains counts
+ of OTUs in a given sample.
+ ids : iterable of strs
+ Identifiers for each sample in ``counts``.
+ validate : bool, optional
+ See ``skbio.diversity.beta_diversity`` for details.
+ reduce_f : function, optional
+ A method to reduce `PartialDistanceMatrix` objects into a single
+ `DistanceMatrix`. The expected signature is:
+
+ `f(Iterable of DistanceMatrix) -> DistanceMatrix`
+
+ Note, this is the reduce within a map/reduce.
+ map_f: function, optional
+ A method that accepts a `_block_compute`. The expected signature is:
+
+ `f(**kwargs) -> DistanceMatrix`
+
+ NOTE: ipyparallel's `map_async` will not work here as we need to be
+ able to pass around `**kwargs``.
+ k : int, optional
+ The blocksize used when computing distances
+ kwargs : kwargs, optional
+ Metric-specific parameters.
+
+ Returns
+ -------
+ DistanceMatrix
+ A distance matrix relating all samples represented by counts to each
+ other.
+
+ Note
+ ----
+ This method is designed to facilitate computing beta diversity in parallel.
+ In general, if you are processing a few hundred samples or less, then it is
+ likely the case that `skbio.diversity.beta_diversity` will be faster. The
+ original need which motivated the development of this method was processing
+ the Earth Microbiome Project [1]_ dataset which at the time spanned over
+ 25,000 samples and 7.5 million open reference OTUs.
+
+ See Also
+ --------
+ skbio.diversity.beta_diversity
+ skbio.diversity.partial_beta_diversity
+
+ References
+ ----------
+ .. [1] http://www.earthmicrobiome.org/
+ """
+ if validate:
+ counts = _validate_counts_matrix(counts, ids=ids)
+
+ if reduce_f is None:
+ reduce_f = _reduce
+
+ if map_f is None:
+ map_f = _map
+
+ # The block method uses numeric IDs to take advantage of fancy indexing
+ # with numpy.
+ tmp_ids = np.arange(len(counts))
+ kwargs['ids'] = tmp_ids
+
+ kwargs['metric'] = metric
+ kwargs['counts'] = counts
+ kwargs['k'] = k
+ kwargs['validate'] = False # we've already validated if necessary
+
+ dm = reduce_f(map_f(_block_compute, _block_kwargs(**kwargs)))
+ dm.ids = ids
+
+ return dm
diff --git a/skbio/diversity/_driver.py b/skbio/diversity/_driver.py
index 9821485..42997c1 100644
--- a/skbio/diversity/_driver.py
+++ b/skbio/diversity/_driver.py
@@ -182,7 +182,7 @@ def alpha_diversity(metric, counts, ids=None, validate=True, **kwargs):
return pd.Series(results, index=ids)
- at deprecated(as_of='0.5.0', until='0.5.1',
+ at deprecated(as_of='0.5.0', until='0.5.2',
reason=('The return type is unstable. Developer caution is '
'advised. The resulting DistanceMatrix object will '
'include zeros when distance has not been calculated, and '
diff --git a/skbio/diversity/tests/test_block.py b/skbio/diversity/tests/test_block.py
new file mode 100644
index 0000000..bd0909e
--- /dev/null
+++ b/skbio/diversity/tests/test_block.py
@@ -0,0 +1,207 @@
+# ----------------------------------------------------------------------------
+# Copyright (c) 2013--, scikit-bio development team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+# ----------------------------------------------------------------------------
+
+from unittest import TestCase, main
+
+import numpy as np
+import numpy.testing as npt
+
+from skbio import TreeNode, DistanceMatrix
+from skbio.diversity import beta_diversity, block_beta_diversity
+from skbio.diversity._block import (_block_party, _generate_id_blocks,
+ _pairs_to_compute, _block_compute,
+ _block_kwargs, _map, _reduce)
+
+
+class ParallelBetaDiversity(TestCase):
+ def setUp(self):
+ self.table1 = [[1, 5],
+ [2, 3],
+ [0, 1]]
+ self.sids1 = list('ABC')
+ self.tree1 = TreeNode.read([
+ '((O1:0.25, O2:0.50):0.25, O3:0.75)root;'])
+ self.oids1 = ['O1', 'O2']
+
+ def test_block_kwargs(self):
+ kws = {'ids': [1, 2, 3, 4, 5], 'foo': 'bar', 'k': 2}
+ exp = [{'row_ids': np.array((0, 1)),
+ 'col_ids': np.array((0, 1)),
+ 'id_pairs': [(0, 1)],
+ 'ids': [1, 2, 3, 4, 5]},
+
+ {'row_ids': np.array((0, 1)),
+ 'col_ids': np.array((2, 3)),
+ 'id_pairs': [(0, 2), (0, 3), (1, 2), (1, 3)],
+ 'ids': [1, 2, 3, 4, 5]},
+
+ {'row_ids': np.array((0, 1)),
+ 'col_ids': np.array((4,)),
+ 'id_pairs': [(0, 4), (1, 4)],
+ 'ids': [1, 2, 3, 4, 5]},
+
+ {'row_ids': np.array((2, 3)),
+ 'col_ids': np.array((2, 3)),
+ 'id_pairs': [(2, 3), ],
+ 'ids': [1, 2, 3, 4, 5]},
+
+ {'row_ids': np.array((2, 3)),
+ 'col_ids': np.array((4,)),
+ 'id_pairs': [(2, 4), (3, 4)],
+ 'ids': [1, 2, 3, 4, 5]}]
+
+ obs = list(_block_kwargs(**kws))
+ npt.assert_equal(obs, exp)
+
+ def test_block_compute(self):
+ def mock_metric(u, v):
+ return (u + v).sum()
+
+ counts = np.array([[0, 1, 2, 3, 4, 5],
+ [1, 2, 3, 4, 5, 0],
+ [2, 3, 4, 5, 0, 1],
+ [10, 2, 3, 6, 8, 2],
+ [9, 9, 2, 2, 3, 4]])
+
+ kwargs = {'metric': mock_metric,
+ 'counts': counts,
+ 'row_ids': np.array((2, 3)),
+ 'col_ids': np.array((4, )),
+ 'id_pairs': [(2, 4), (3, 4)],
+ 'ids': [1, 2, 3, 4, 5]}
+
+ exp = DistanceMatrix(np.array([[0, 0, 44],
+ [0, 0, 60],
+ [44, 60, 0]]), (2, 3, 4))
+
+ obs = _block_compute(**kwargs)
+ npt.assert_equal(obs.data, exp.data)
+ self.assertEqual(obs.ids, exp.ids)
+
+ def test_map(self):
+ def func(a, b, c=5):
+ return a + b + c
+
+ kwargs = [{'a': 0, 'b': 1, 'c': 0},
+ {'a': 2, 'b': 3}]
+ exp = [1, 10]
+ obs = list(_map(func, kwargs))
+ self.assertEqual(obs, exp)
+
+ def test_reduce(self):
+ dm1 = DistanceMatrix(np.array([[0, 0, 44],
+ [0, 0, 60],
+ [44, 60, 0]]), (2, 3, 4))
+ dm2 = DistanceMatrix(np.array([[0, 123],
+ [123, 0]]), (1, 5))
+ dm3 = DistanceMatrix(np.array([[0, 1, 2, 3],
+ [1, 0, 4, 5],
+ [2, 4, 0, 6],
+ [3, 5, 6, 0]]), (0, 3, 4, 5))
+ exp = DistanceMatrix(np.array([[0, 0, 0, 1, 2, 3],
+ [0, 0, 0, 0, 0, 123],
+ [0, 0, 0, 0, 44, 0],
+ [1, 0, 0, 0, 64, 5],
+ [2, 0, 44, 64, 0, 6],
+ [3, 123, 0, 5, 6, 0]]), list(range(6)))
+
+ obs = _reduce([dm1, dm2, dm3])
+ npt.assert_equal(obs.data, exp.data)
+ self.assertEqual(obs.ids, exp.ids)
+
+ def test_block_beta_diversity(self):
+ exp = beta_diversity('unweighted_unifrac', self.table1, self.sids1,
+ tree=self.tree1, otu_ids=self.oids1)
+ obs = block_beta_diversity('unweighted_unifrac', self.table1,
+ self.sids1, otu_ids=self.oids1,
+ tree=self.tree1, k=2)
+ npt.assert_equal(obs.data, exp.data)
+ self.assertEqual(obs.ids, exp.ids)
+
+ def test_generate_id_blocks(self):
+ ids = [1, 2, 3, 4, 5]
+ exp = [(np.array((0, 1)), np.array((0, 1))),
+ (np.array((0, 1)), np.array((2, 3))),
+ (np.array((0, 1)), np.array((4,))),
+ (np.array((2, 3)), np.array((2, 3))),
+ (np.array((2, 3)), np.array((4,))),
+ (np.array((4,)), np.array((4,)))]
+
+ obs = list(_generate_id_blocks(ids, 2))
+ npt.assert_equal(obs, exp)
+
+ def test_block_party_notree(self):
+ counts = np.arange(15).reshape(5, 3)
+ exp = [{'counts': np.array([[0, 1, 2], [3, 4, 5]]),
+ 'ids': np.array([0, 1])},
+ {'counts': np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8],
+ [9, 10, 11]]),
+ 'ids': np.array([0, 1, 2, 3])},
+ {'counts': np.array([[0, 1, 2], [3, 4, 5], [12, 13, 14]]),
+ 'ids': np.array([0, 1, 4])},
+ {'counts': np.array([[6, 7, 8], [9, 10, 11]]),
+ 'ids': np.array([2, 3])},
+ {'counts': np.array([[6, 7, 8], [9, 10, 11], [12, 13, 14]]),
+ 'ids': np.array([2, 3, 4])},
+ {'counts': np.array([[12, 13, 14]]), 'ids': np.array([4])}]
+ obs = [_block_party(counts, rids, cids) for rids, cids in
+ _generate_id_blocks(list(range(5)), 2)]
+ npt.assert_equal(obs, exp)
+
+ def test_block_party_tree(self):
+ counts = np.array([[1, 1, 1],
+ [1, 0, 1],
+ [1, 0, 1],
+ [0, 0, 1],
+ [0, 1, 1]])
+ tree = TreeNode.read(['(a:1,b:2,c:3);'])
+ otu_ids = ['a', 'b', 'c']
+
+ kw = {'tree': tree, 'otu_ids': otu_ids}
+ kw_no_a = {'tree': tree.shear(['b', 'c']), 'otu_ids': ['b', 'c']}
+ kw_no_b = {'tree': tree.shear(['a', 'c']), 'otu_ids': ['a', 'c']}
+
+ # python >= 3.5 supports {foo: bar, **baz}
+ exp = [dict(counts=np.array([[1, 1, 1], [1, 0, 1]]), **kw),
+ dict(counts=np.array([[1, 1, 1], [1, 0, 1], [1, 0, 1],
+ [0, 0, 1]]), **kw),
+ dict(counts=np.array([[1, 1, 1], [1, 0, 1], [0, 1, 1]]), **kw),
+ dict(counts=np.array([[1, 1], [0, 1]]), **kw_no_b),
+ dict(counts=np.array([[1, 0, 1], [0, 0, 1], [0, 1, 1]]), **kw),
+ dict(counts=np.array([[1, 1]]), **kw_no_a)]
+
+ obs = [_block_party(counts, rids, cids, **kw) for rids, cids in
+ _generate_id_blocks(list(range(5)), 2)]
+
+ for okw, ekw in zip(obs, exp):
+ npt.assert_equal(okw['counts'], ekw['counts'])
+ npt.assert_equal(okw['otu_ids'], ekw['otu_ids'])
+ self.assertEqual(str(okw['tree']), str(ekw['tree']))
+
+ def test_pairs_to_compute_rids_are_cids(self):
+ rids = np.array([0, 1, 2, 10])
+ cids = rids
+ exp = [(0, 1), (0, 2), (0, 10), (1, 2), (1, 10), (2, 10)]
+ self.assertEqual(_pairs_to_compute(rids, cids), exp)
+
+ def test_pairs_to_compute_rids_are_not_cids(self):
+ rids = np.array([0, 1, 2])
+ cids = np.array([3, 4, 5])
+ exp = [(0, 3), (0, 4), (0, 5), (1, 3), (1, 4), (1, 5), (2, 3), (2, 4),
+ (2, 5)]
+ self.assertEqual(_pairs_to_compute(rids, cids), exp)
+
+ def test_pairs_to_compute_rids_overlap_cids(self):
+ rids = np.array([0, 1, 2])
+ cids = np.array([0, 10, 20])
+ with self.assertRaises(ValueError):
+ _pairs_to_compute(rids, cids)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/skbio/io/__init__.py b/skbio/io/__init__.py
index 069c316..5ef33ac 100644
--- a/skbio/io/__init__.py
+++ b/skbio/io/__init__.py
@@ -26,6 +26,7 @@ see the associated documentation.
phylip
qseq
stockholm
+ genbank
.. currentmodule:: skbio.io.registry
diff --git a/skbio/io/format/_blast.py b/skbio/io/format/_blast.py
index a1c4d65..67b116c 100644
--- a/skbio/io/format/_blast.py
+++ b/skbio/io/format/_blast.py
@@ -7,6 +7,7 @@
# ----------------------------------------------------------------------------
import functools
+import contextlib
import pandas as pd
@@ -32,10 +33,28 @@ def _parse_blast_data(fh, columns, error, error_message, comment=None,
read_csv = functools.partial(pd.read_csv, na_values='N/A', sep='\t',
header=None, keep_default_na=False,
comment=comment, skiprows=skiprows)
- lineone = read_csv(fh, nrows=1)
- if len(lineone.columns) != len(columns):
- raise error(error_message % (len(columns), len(lineone.columns)))
+ # HACK for https://github.com/pandas-dev/pandas/issues/14418
+ # this avoids closing the `fh`, whose lifetime isn't the responsibility
+ # of this parser
+ with _noop_close(fh) as fh:
- fh.seek(0)
- return read_csv(fh, names=columns, dtype=_possible_columns)
+ lineone = read_csv(fh, nrows=1)
+
+ if len(lineone.columns) != len(columns):
+ raise error(error_message % (len(columns), len(lineone.columns)))
+
+ fh.seek(0)
+
+ return read_csv(fh, names=columns, dtype=_possible_columns)
+
+
+# HACK for https://github.com/pandas-dev/pandas/issues/14418
+ at contextlib.contextmanager
+def _noop_close(fh):
+ backup = fh.close
+ fh.close = lambda: None
+ try:
+ yield fh
+ finally:
+ fh.close = backup
diff --git a/skbio/io/format/_sequence_feature_vocabulary.py b/skbio/io/format/_sequence_feature_vocabulary.py
new file mode 100644
index 0000000..235cbb4
--- /dev/null
+++ b/skbio/io/format/_sequence_feature_vocabulary.py
@@ -0,0 +1,351 @@
+# ----------------------------------------------------------------------------
+# Copyright (c) 2013--, scikit-bio development team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+# ----------------------------------------------------------------------------
+
+import re
+
+from skbio.metadata import IntervalMetadata
+from skbio.io.format._base import _line_generator
+from skbio.io import FileFormatError
+
+
+def _vocabulary_change(format='insdc', read_in=True):
+ '''Return a dict that converts between memory and output vocabulary.'''
+ convert = {'phase': {'insdc': 'codon_start'},
+ 'source': {'insdc': 'inference'},
+ 'db_xref': {'gff3': 'Dbxref'},
+ 'note': {'gff3': 'Note'}}
+ if read_in:
+ return {v[format]: k for k, v in convert.items() if format in v}
+ else:
+ return {k: v[format] for k, v in convert.items() if format in v}
+
+
+def _vocabulary_skip(format='insdc'):
+ '''Return a list of vocabularies that should be skipped when auto
+ output to disk for the specified format.
+
+ '''
+ skip = {'type': ('insdc', 'gff3'),
+ 'ID': ('insdc'),
+ 'translation': ('gff3'),
+ 'strand': ('insdc')}
+ return [k for k, v in skip.items() if format in v]
+
+
+def _yield_section(is_another_section, **kwargs):
+ '''Returns function that returns successive sections from file.
+
+ Parameters
+ ----------
+ is_another_section : callable
+ It takes a string as input and return a boolean indicating
+ a new section starts.
+ kwargs : dict, optional
+ Keyword arguments will be passed to `_line_generator`.
+
+ Returns
+ -------
+ function
+ A function accept a list of lines as input and return
+ a generator to yield section one by one.
+ '''
+ def parser(lines):
+ curr = []
+ for line in _line_generator(lines, **kwargs):
+ # if we find another, return the previous section
+ if is_another_section(line):
+ if curr:
+ yield curr
+ curr = []
+ curr.append(line)
+ # don't forget to return the last section in the file
+ if curr:
+ yield curr
+ return parser
+
+
+def _parse_section_default(
+ lines, label_delimiter=None, join_delimiter=' ', return_label=False):
+ '''Parse sections in default way.
+
+ Do 2 things:
+ 1. split first line with label_delimiter for label
+ 2. join all the lines into one str with join_delimiter.
+ '''
+ data = []
+ label = None
+ line = lines[0]
+
+ items = line.split(label_delimiter, 1)
+
+ if len(items) == 2:
+ label, section = items
+ else:
+ label = items[0]
+ section = ""
+ data.append(section)
+
+ data.extend(lines[1:])
+ data = join_delimiter.join(i.strip() for i in data)
+ if return_label:
+ return label, data
+ else:
+ return data
+
+
+def _serialize_section_default(header, obj, indent=12):
+ return '{header:<{indent}}{obj}\n'.format(
+ header=header, obj=obj, indent=indent)
+
+
+def _parse_feature_table(lines, length):
+ '''parse DDBJ/ENA/GenBank Feature Table.'''
+ imd = IntervalMetadata(length)
+ # skip the 1st FEATURES line
+ if lines[0].startswith('FEATURES'):
+ lines = lines[1:]
+ # magic number 21: the lines following header of each feature
+ # are indented with 21 spaces.
+ feature_indent = ' ' * 21
+ section_splitter = _yield_section(
+ lambda x: not x.startswith(feature_indent),
+ skip_blanks=True, strip=False)
+
+ for section in section_splitter(lines):
+ _parse_single_feature(section, imd)
+ return imd
+
+
+def _parse_single_feature(lines, imd):
+ '''Parse a feature.
+
+ Parse a feature and add it to ``IntervalMetadata`` object.
+
+ Parameters
+ ----------
+ imd : IntervalMetadata
+ '''
+ voca_change = _vocabulary_change('insdc')
+
+ # each component of a feature starts with '/', except the 1st
+ # component of location.
+ section_splitter = _yield_section(
+ lambda x: x.startswith('/'), strip=True)
+ section_iter = section_splitter(lines)
+
+ # 1st section is location
+ section = next(section_iter)
+ feature_type, feature_loc = _parse_section_default(
+ section, join_delimiter='', return_label=True)
+
+ metadata = {'type': feature_type, '__location': feature_loc}
+
+ intvl = imd.add(*_parse_loc_str(feature_loc))
+
+ for section in section_iter:
+ # following sections are Qualifiers
+ k, v = _parse_section_default(
+ section, label_delimiter='=',
+ join_delimiter=' ', return_label=True)
+ # 1st char is '/'
+ k = k[1:]
+ if k in voca_change:
+ k = voca_change[k]
+
+ if k == 'phase':
+ v = int(v) - 1
+
+ # some Qualifiers can appear multiple times
+ if k in metadata:
+ if not isinstance(metadata[k], list):
+ metadata[k] = [metadata[k]]
+ metadata[k].append(v)
+ else:
+ metadata[k] = v
+
+ intvl.metadata.update(metadata)
+
+
+def _parse_loc_str(loc_str):
+ '''Parse location string.
+
+ .. warning: This converts coordinates to 0-based from 1-based
+ GenBank coordinate system.
+
+ The location descriptor can be one of the following [1]_:
+ (a) a single base number. e.g. 467
+ (b) a site between two indicated adjoining bases. e.g. 123^124
+ (c) a single base chosen from within a specified range of bases (not
+ allowed for new entries). e.g. 102.110
+ (d) the base numbers delimiting a sequence span. e.g.340..565
+ (e) a remote entry identifier followed by a local location
+ descriptor (i.e., a-d). e.g. J00194.1:100..202
+
+ Notes
+ -----
+ This does not fully handle (e) case. It will discard the remote
+ entry part and only keep the local part. When it parses locations
+ across strand (e.g. "complement(123..145),200..209"), it will
+ record all the span parts but will record strand as negative.
+
+ References
+ ----------
+ .. [1] http://www.insdc.org/files/feature_table.html#3.4
+
+ '''
+ # define the tokens
+ operators = ['join', 'complement', 'order']
+ LPAREN = r'(?P<LPAREN>\()'
+ RPAREN = r'(?P<RPAREN>\))'
+ COMMA = r'(?P<COMMA>,)'
+ WS = r'(?P<WS>\s+)'
+ a = r'(?P<A>\d+)'
+ b = r'(?P<B>\d+\^\d+)'
+ c = r'(?P<C>\d+\.\d+)'
+ d = r'(?P<D><?\d+\.\.>?\d+)'
+ e_left = r'(?P<EL><?[a-zA-Z_0-9\.]+:\d+\.\.>?\d+)'
+ e_right = r'(?P<ER><?\d+\.\.>?[a-zA-Z_0-9\.]+:\d+)'
+ illegal = r'(?P<ILLEGAL>.+)'
+ # The order of tokens in the master regular expression also
+ # matters. When matching, re tries to match pattens in the order
+ # specified. Thus, if a pattern happens to be a substring of a
+ # longer pattern, you need to make sure the longer pattern goes
+ # first.
+ master_pat = re.compile('|'.join(
+ operators + [WS, LPAREN, RPAREN, COMMA,
+ b, c, d, e_left, e_right, a,
+ illegal]))
+
+ scanner = master_pat.scanner(loc_str)
+
+ bounds = []
+ fuzzy = []
+
+ metadata = {'strand': '+'}
+
+ for m in iter(scanner.match, None):
+ p, v = m.lastgroup, m.group()
+ if v == 'complement':
+ metadata['strand'] = '-'
+ elif p == 'A':
+ start = int(v)
+ bounds.append((start-1, start))
+ fuzzy.append((False, False))
+ elif p == 'B':
+ start, end = v.split('^')
+ start = int(start)
+ bounds.append((start-1, start))
+ fuzzy.append((False, False))
+ elif p == 'C' or p == 'D':
+ if p == 'C':
+ start, end = v.split('.')
+ else:
+ start, end = v.split('..')
+ fuzzy_s = fuzzy_e = False
+ if start.startswith('<'):
+ start = start[1:]
+ fuzzy_s = True
+ if end.startswith('>'):
+ end = end[1:]
+ fuzzy_e = True
+ bounds.append((int(start)-1, int(end)))
+ fuzzy.append((fuzzy_s, fuzzy_e))
+ elif p == 'ILLEGAL':
+ raise FileFormatError(
+ 'Could not parse location string: "%s"' % loc_str)
+
+ return bounds, fuzzy, metadata
+
+
+def _serialize_feature_table(intervals, indent=21):
+ '''
+ Parameters
+ ----------
+ intervals : list of ``Interval``
+ '''
+ for intvl in intervals:
+ yield _serialize_single_feature(intvl, indent)
+
+
+def _serialize_single_feature(intvl, indent=21):
+ '''
+ Parameters
+ ----------
+ intvl : Interval
+ '''
+ # there are 5 spaces before Feature Key starts.
+ padding = ' ' * 5
+ qualifiers = []
+ md = intvl.metadata
+ voca_skip = _vocabulary_skip('insdc')
+ voca_change = _vocabulary_change('insdc', read_in=False)
+ # sort it so the output order is deterministic
+ for k in sorted(md):
+ if k.startswith('__') or k in voca_skip:
+ continue
+ v = md[k]
+ if k == 'phase':
+ v = str(v + 1)
+ if k in voca_change:
+ k = voca_change[k]
+ if isinstance(v, list):
+ for vi in v:
+ qualifiers.append(_serialize_qualifier(k, vi))
+ else:
+ qualifiers.append(_serialize_qualifier(k, v))
+
+ if '__location' in md:
+ loc = md['__location']
+ else:
+ loc = _serialize_location(intvl)
+ # the qualifiers start at column 22
+ qualifiers = [' ' * indent + i for i in qualifiers]
+ return '{header:<{indent}}{loc}\n{qualifiers}\n'.format(
+ header=padding + md['type'],
+ loc=loc,
+ indent=indent,
+ qualifiers='\n'.join(qualifiers))
+
+
+def _serialize_location(intvl):
+ loc = []
+ for bound, fuzzy in zip(intvl.bounds, intvl.fuzzy):
+ start, end = bound
+ start += 1
+ if start == end:
+ s = str(start)
+ elif fuzzy[0] and fuzzy[1]:
+ s = '<%d..>%d' % (start, end)
+ elif fuzzy[0] and not fuzzy[1]:
+ s = '<%d..%d' % (start, end)
+ elif not fuzzy[0] and fuzzy[1]:
+ s = '%d..>%d' % (start, end)
+ else:
+ s = '%d..%d' % (start, end)
+ loc.append(s)
+ if len(loc) > 1:
+ loc_str = 'join({})'.format(','.join(loc))
+ else:
+ loc_str = loc[0]
+ if intvl.metadata.get('strand') == '-':
+ loc_str = 'complement({})'.format(loc_str)
+ return loc_str
+
+
+def _serialize_qualifier(key, value):
+ '''Serialize a Qualifier in a feature.
+
+ Parameters
+ ----------
+ value : int, str
+ '''
+ # if value is empty
+ if not value:
+ return '/%s' % key
+
+ return '/{k}={v}'.format(k=key, v=value)
diff --git a/skbio/io/format/genbank.py b/skbio/io/format/genbank.py
index 11f77c6..030d576 100644
--- a/skbio/io/format/genbank.py
+++ b/skbio/io/format/genbank.py
@@ -4,18 +4,16 @@ GenBank format (:mod:`skbio.io.format.genbank`)
.. currentmodule:: skbio.io.format.genbank
-GenBank format (GenBank Flat File Format) stores sequence and its annotation
-together. The start of the annotation section is marked by a line beginning
-with the word "LOCUS". The start of sequence section is marked by a line
-beginning with the word "ORIGIN" and the end of the section is marked by a line
-with only "//".
+GenBank format (GenBank Flat File Format) stores sequence and its
+annotation together. The start of the annotation section is marked by
+a line beginning with the word "LOCUS". The start of sequence section
+is marked by a line beginning with the word "ORIGIN" and the end of
+the section is marked by a line with only "//".
-The GenBank file usually ends with .gb or sometimes .gbk. The GenBank format
-for protein has been renamed to GenPept. The GenBank (for nucleotide) and
-Genpept are essentially the same format.
-
-An example of a GenBank file can be see here:
-<http://www.ncbi.nlm.nih.gov/Sitemap/samplerecord.html>
+The GenBank file usually ends with .gb or sometimes .gbk. The GenBank
+format for protein has been renamed to GenPept. The GenBank (for
+nucleotide) and Genpept are essentially the same format. An example of
+a GenBank file can be seen here [1]_.
Format Support
--------------
@@ -39,47 +37,115 @@ Format Specification
--------------------
**State: Experimental as of 0.4.1.**
-The International Nucleotide Sequence Database Collaboration (INSDC)
-foundational initiative between the DDBJ, EMBL, and GenBank
-(http://www.insdc.org/). These organisations all use the
-same "Feature Table" layout in their plain text flat file formats.
-
-However, the header and sequence sections of an EMBL file are very
-different in layout to those produced by GenBank/DDBJ.
-
-Feature Table Documentation:
-http://www.insdc.org/files/feature_table.html
-ftp://ftp.ncbi.nih.gov/genbank/docs/FTv10_3.html
-
-The sequence in the ``'ORIGIN'`` section is always in lowercase for the
-GenBank files downloaded from NCBI. For the RNA molecules, ``'t'`` (thymine),
-instead of ``'u'`` (uracil) is used in the sequence. All GenBank writers
-follow these conventions while writing GenBank files.
-
-All the sections before ``'FEATURES'`` will be read into ``metadata`` of
-``Sequence`` or its sub-class. The header and its content of a section
-is stored as a pair of key and value in ``metadata``. For the ``'REFERENCE'``
+Sections before ``FEATURES``
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+All the sections before ``FEATURES`` will be read into the attribute
+of ``metadata``. The header and its content of a section is stored as
+a pair of key and value in ``metadata``. For the ``REFERENCE``
section, its value is stored as a list, as there are often multiple
reference sections in one GenBank record.
-The information of the ``'FEATURES'`` is stored in both ``metadata`` and
-``positional_metadata`` of ``Sequence`` or its sub-class. For each feature,
-its location is stored as boolean column in ``positional_metadata``; other
-qualifiers are stored as a ``dict`` in the ``list`` of
-``metadata['FEATURES']``. In the ``dict`` of qualifiers, there are a few
-extra keys, which end with ``'_'``, including:
+``FEATURES`` section
+^^^^^^^^^^^^^^^^^^^^
+The International Nucleotide Sequence Database Collaboration (INSDC
+[2]_) is a joint effort among the DDBJ, EMBL, and GenBank. These
+organisations all use the same "Feature Table" layout in their plain
+text flat file formats, which are documented in detail [3]_. The
+feature keys and their qualifiers are also described in this webpage
+[4]_.
+
+The ``FEATURES`` section will be stored in ``interval_metadata`` of
+``Sequence`` or its sub-class. Each sub-section is stored as an
+``Interval`` object in ``interval_metadata``. Each ``Interval`` object
+has ``metadata`` keeping the information of this feature in the
+sub-section.
+
+To normalize the vocabulary between multiple formats (currently only
+the INSDC Feature Table and GFF3) to store metadata of interval
+features, we rename some terms in some formats to the same common name
+when parsing them into memory, as described in this table:
+
++-----------+-----------+-----------+---------+------------------------------+
+|INSDC |GFF3 |Key stored |Value |Description |
+|feature |columns or | |type | |
+|table |attributes | |stored | |
++===========+===========+===========+=========+==============================+
+|inference |source |source |str |the algorithm or experiment |
+| |(column 2) | | |used to generate this feature |
++-----------+-----------+-----------+---------+------------------------------+
+|feature key|type |type |str |the type of the feature |
+| |(column 3) | | | |
++-----------+-----------+-----------+---------+------------------------------+
+|N/A |score |score |float |the score of the feature |
+| |(column 6) | | | |
++-----------+-----------+-----------+---------+------------------------------+
+|N/A |strand |strand |str |the strand of the feature. + |
+| |(column 7) | | |for positive strand, - for |
+| | | | |minus strand, and . for |
+| | | | |features that are not |
+| | | | |stranded. In addition, ? can |
+| | | | |be used for features whose |
+| | | | |strandedness is relevant, but |
+| | | | |unknown. |
++-----------+-----------+-----------+---------+------------------------------+
+|codon_start|phase |phase |int |the offset at which the first |
+| |(column 8) | | |complete codon of a coding |
+| | | | |feature can be found, relative|
+| | | | |to the first base of that |
+| | | | |feature. It is 0, 1, or 2 in |
+| | | | |GFF3 or 1, 2, or 3 in GenBank.|
+| | | | |The stored value is 0, 1, or |
+| | | | |2, following in GFF3 format. |
++-----------+-----------+-----------+---------+------------------------------+
+|db_xref |Dbxref |db_xref |list of |A database cross reference |
+| | | |str | |
++-----------+-----------+-----------+---------+------------------------------+
+|N/A |ID |ID |str |feature ID |
++-----------+-----------+-----------+---------+------------------------------+
+|note |Note |note |str |any comment or additional |
+| | | | |information |
++-----------+-----------+-----------+---------+------------------------------+
+|translation|N/A |translation|str |the protein sequence for CDS |
+| | | | |features |
++-----------+-----------+-----------+---------+------------------------------+
+
+``Location`` string
++++++++++++++++++++
+There are 5 types of location descriptors defined in Feature
+Table. This explains how they will be parsed into the bounds of
+``Interval`` object (note it converts the 1-based coordinate to
+0-based):
+
+ 1. a single base number. e.g. 67. This is parsed to ``(66, 67)``.
+
+ 2. a site between two neighboring bases. e.g. 67^68. This
+ is parsed to ``(66, 67)``.
+
+ 3. a single base from inside a range. e.g. 67.89. This is parsed to
+ ``(66, 89)``.
+
+ 4. a pair of base numbers defining a sequence span. e.g. 67..89. This
+ is parsed to ``(66, 89)``.
+
+ 5. a remote sequence identifier followed by a location descriptor
+ defined above. e.g. J00123.1:67..89. This will be discarded
+ because it is not on the current sequence. When it is combined
+ with local descriptor like J00123.1:67..89,200..209, the
+ local part will be kept to be ``(199, 209)``.
+
+
+.. note:: The Location string is fully stored in ``Interval.metadata``
+ with key ``__location``. The key starting with ``__`` is "private"
+ and should be modified with care.
+
+
+``ORIGIN`` section
+^^^^^^^^^^^^^^^^^^
+The sequence in the ``ORIGIN`` section is always in lowercase for
+the GenBank files downloaded from NCBI. For the RNA molecules, ``t``
+(thymine), instead of ``u`` (uracil) is used in the sequence. All
+GenBank writers follow these conventions while writing GenBank files.
- 1. ``'index_'``: the column index to the ``positional_metadata``,
-where the location of the current feature is stored.
-
- 2. ``'left_partial_'``: whether the exact lower boundary point of the
-feature is unknown.
-
- 3. ``'right_partial_'``: whether the exact upper boundary point of the
-feature is unknown.
-
- 4. ``'type_'``: the molecular type of the feature. Its value is from the
-header of the feature.
Format Parameters
-----------------
@@ -90,12 +156,12 @@ The ``constructor`` parameter can be used with the ``Sequence`` generator
to specify the in-memory type of each GenBank record that is parsed.
``constructor`` should be ``Sequence`` or a sub-class of ``Sequence``.
It is also detected by the unit label on the LOCUS line. For example, if it
-is ``'bp'``, it will be read into ``DNA``; if it is ``'aa'``, it will be read
+is ``bp``, it will be read into ``DNA``; if it is ``aa``, it will be read
into ``Protein``. Otherwise, it will be read into ``Sequence``. This default
behavior is overridden by setting ``constructor``.
``lowercase`` is another parameter available for all GenBank readers.
-By default, it is set to ``True`` to read in the ``'ORIGIN'`` sequence
+By default, it is set to ``True`` to read in the ``ORIGIN`` sequence
as lowercase letters. This parameter is passed to ``Sequence`` or
its sub-class constructor.
@@ -108,57 +174,42 @@ Examples
Reading and Writing GenBank Files
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Suppose we have the following GenBank file [example modified from [1_]::
-
- LOCUS 3K1V_A 34 bp RNA linear SYN 10-OCT-2012
- DEFINITION Chain A, Structure Of A Mutant Class-I Preq1.
- ACCESSION 3K1V_A
- VERSION 3K1V_A GI:260656459
- KEYWORDS .
- SOURCE synthetic construct
- ORGANISM synthetic construct
- other sequences; artificial sequences.
- REFERENCE 1 (bases 1 to 34)
- AUTHORS Klein,D.J., Edwards,T.E. and Ferre-D'Amare,A.R.
- TITLE Cocrystal structure of a class I preQ1 riboswitch
- JOURNAL Nat. Struct. Mol. Biol. 16 (3), 343-344 (2009)
- PUBMED 19234468
- COMMENT SEQRES.
- FEATURES Location/Qualifiers
- source 1..34
- /organism="synthetic construct"
- /mol_type="other RNA"
- /db_xref="taxon:32630"
- ORIGIN
- 1 agaggttcta gcacatccct ctataaaaaa ctaa
- //
-
->>> gb = ['LOCUS 3K1V_A 34 bp RNA linear SYN 10-OCT-2012\\n',
-... 'DEFINITION Chain A, Structure Of A Mutant Class-I Preq1.\\n',
-... 'ACCESSION 3K1V_A\\n',
-... 'VERSION 3K1V_A GI:260656459\\n',
-... 'KEYWORDS .\\n',
-... 'SOURCE synthetic construct\\n',
-... ' ORGANISM synthetic construct\\n',
-... ' other sequences; artificial sequences.\\n',
-... 'REFERENCE 1 (bases 1 to 34)\\n',
-... " AUTHORS Klein,D.J., Edwards,T.E. and Ferre-D'Amare,A.R.\\n",
-... ' TITLE Cocrystal structure of a class I preQ1 riboswitch\\n',
-... ' JOURNAL Nat. Struct. Mol. Biol. 16 (3), 343-344 (2009)\\n',
-... ' PUBMED 19234468\\n',
-... 'COMMENT SEQRES.\\n',
-... 'FEATURES Location/Qualifiers\\n',
-... ' source 1..34\\n',
-... ' /organism="synthetic construct"\\n',
-... ' /mol_type="other RNA"\\n',
-... ' /db_xref="taxon:32630"\\n',
-... 'ORIGIN\\n',
-... ' 1 agaggttcta gcacatccct ctataaaaaa ctaa\\n',
-... '//\\n']
+Suppose we have the following GenBank file example modified from [5]_:
+
+>>> gb_str = '''
+... LOCUS 3K1V_A 34 bp RNA linear SYN 10-OCT-2012
+... DEFINITION Chain A, Structure Of A Mutant Class-I Preq1.
+... ACCESSION 3K1V_A
+... VERSION 3K1V_A GI:260656459
+... KEYWORDS .
+... SOURCE synthetic construct
+... ORGANISM synthetic construct
+... other sequences; artificial sequences.
+... REFERENCE 1 (bases 1 to 34)
+... AUTHORS Klein,D.J., Edwards,T.E. and Ferre-D'Amare,A.R.
+... TITLE Cocrystal structure of a class I preQ1 riboswitch
+... JOURNAL Nat. Struct. Mol. Biol. 16 (3), 343-344 (2009)
+... PUBMED 19234468
+... COMMENT SEQRES.
+... FEATURES Location/Qualifiers
+... source 1..34
+... /organism="synthetic construct"
+... /mol_type="other RNA"
+... /db_xref="taxon:32630"
+... misc_binding 1..30
+... /note="Preq1 riboswitch"
+... /bound_moiety="preQ1"
+... ORIGIN
+... 1 agaggttcta gcacatccct ctataaaaaa ctaa
+... //
+... '''
+
Now we can read it as ``DNA`` object:
+>>> import io
>>> from skbio import DNA, RNA, Sequence
+>>> gb = io.StringIO(gb_str)
>>> dna_seq = DNA.read(gb)
>>> dna_seq
DNA
@@ -167,14 +218,13 @@ Metadata:
'ACCESSION': '3K1V_A'
'COMMENT': 'SEQRES.'
'DEFINITION': 'Chain A, Structure Of A Mutant Class-I Preq1.'
- 'FEATURES': <class 'list'>
'KEYWORDS': '.'
'LOCUS': <class 'dict'>
'REFERENCE': <class 'list'>
'SOURCE': <class 'dict'>
'VERSION': '3K1V_A GI:260656459'
-Positional metadata:
- 0: <dtype: bool>
+Interval metadata:
+ 2 interval features
Stats:
length: 34
has gaps: False
@@ -185,10 +235,11 @@ Stats:
0 AGAGGTTCTA GCACATCCCT CTATAAAAAA CTAA
-Since this is a riboswitch molecule, we may want to read it as ``RNA``.
-As the GenBank file usually have ``'t'`` instead of ``'u'`` in the
-sequence, we can read it as ``RNA`` by converting ``'t'`` to ``'u'``:
+Since this is a riboswitch molecule, we may want to read it as
+``RNA``. As the GenBank file usually have ``t`` instead of ``u`` in
+the sequence, we can read it as ``RNA`` by converting ``t`` to ``u``:
+>>> gb = io.StringIO(gb_str)
>>> rna_seq = RNA.read(gb)
>>> rna_seq
RNA
@@ -197,14 +248,13 @@ Metadata:
'ACCESSION': '3K1V_A'
'COMMENT': 'SEQRES.'
'DEFINITION': 'Chain A, Structure Of A Mutant Class-I Preq1.'
- 'FEATURES': <class 'list'>
'KEYWORDS': '.'
'LOCUS': <class 'dict'>
'REFERENCE': <class 'list'>
'SOURCE': <class 'dict'>
'VERSION': '3K1V_A GI:260656459'
-Positional metadata:
- 0: <dtype: bool>
+Interval metadata:
+ 2 interval features
Stats:
length: 34
has gaps: False
@@ -217,8 +267,7 @@ Stats:
>>> rna_seq == dna_seq.transcribe()
True
->>> from io import StringIO
->>> with StringIO() as fh:
+>>> with io.StringIO() as fh:
... print(dna_seq.write(fh, format='genbank').getvalue())
LOCUS 3K1V_A 34 bp RNA linear SYN 10-OCT-2012
DEFINITION Chain A, Structure Of A Mutant Class-I Preq1.
@@ -235,10 +284,13 @@ REFERENCE 1 (bases 1 to 34)
PUBMED 19234468
COMMENT SEQRES.
FEATURES Location/Qualifiers
- source 1..34
+ source 1..34
/db_xref="taxon:32630"
/mol_type="other RNA"
/organism="synthetic construct"
+ misc_binding 1..30
+ /bound_moiety="preQ1"
+ /note="Preq1 riboswitch"
ORIGIN
1 agaggttcta gcacatccct ctataaaaaa ctaa
//
@@ -246,7 +298,11 @@ ORIGIN
References
----------
-.. [1_] http://www.ncbi.nlm.nih.gov/nuccore/3K1V_A
+.. [1] http://www.ncbi.nlm.nih.gov/Sitemap/samplerecord.html
+.. [2] http://www.insdc.org/
+.. [3] http://www.insdc.org/files/feature_table.html
+.. [4] http://www.ebi.ac.uk/ena/WebFeat/
+.. [5] http://www.ncbi.nlm.nih.gov/nuccore/3K1V_A
"""
@@ -259,8 +315,6 @@ References
# ----------------------------------------------------------------------------
import re
-import numpy as np
-import pandas as pd
from functools import partial
from skbio.io import create_format, GenBankFormatError
@@ -268,6 +322,9 @@ from skbio.io.format._base import (
_get_nth_sequence, _line_generator, _too_many_blanks)
from skbio.util._misc import chunk_str
from skbio.sequence import Sequence, DNA, RNA, Protein
+from skbio.io.format._sequence_feature_vocabulary import (
+ _yield_section, _parse_section_default, _serialize_section_default,
+ _parse_feature_table, _serialize_feature_table)
genbank = create_format('genbank')
@@ -364,7 +421,7 @@ def _protein_to_genbank(obj, fh):
def _construct(record, constructor=None, **kwargs):
'''Construct the object of Sequence, DNA, RNA, or Protein.
'''
- seq, md, pmd = record
+ seq, md, imd = record
if 'lowercase' not in kwargs:
kwargs['lowercase'] = True
if constructor is None:
@@ -377,10 +434,10 @@ def _construct(record, constructor=None, **kwargs):
if constructor == RNA:
return DNA(
- seq, metadata=md, positional_metadata=pmd, **kwargs).transcribe()
+ seq, metadata=md, interval_metadata=imd, **kwargs).transcribe()
else:
return constructor(
- seq, metadata=md, positional_metadata=pmd, **kwargs)
+ seq, metadata=md, interval_metadata=imd, **kwargs)
def _parse_genbanks(fh):
@@ -395,7 +452,7 @@ def _parse_genbanks(fh):
def _parse_single_genbank(chunks):
metadata = {}
- positional_metadata = None
+ interval_metadata = None
sequence = ''
# each section starts with a HEADER without indent.
section_splitter = _yield_section(
@@ -422,11 +479,10 @@ def _parse_single_genbank(chunks):
elif header == 'ORIGIN':
sequence = parsed
elif header == 'FEATURES':
- metadata[header] = parsed[0]
- positional_metadata = pd.concat(parsed[1], axis=1)
+ interval_metadata = parsed
else:
metadata[header] = parsed
- return sequence, metadata, positional_metadata
+ return sequence, metadata, interval_metadata
def _serialize_single_genbank(obj, fh):
@@ -435,12 +491,18 @@ def _serialize_single_genbank(obj, fh):
Always write it in NCBI canonical way:
1. sequence in lowercase
2. 'u' as 't' even in RNA molecules.
+
+ Parameters
+ ----------
+ obj : Sequence or its child class
+
'''
+ # write out the headers
md = obj.metadata
for header in _HEADERS:
+ serializer = _SERIALIZER_TABLE.get(
+ header, _serialize_section_default)
if header in md:
- serializer = _SERIALIZER_TABLE.get(
- header, _serialize_section_default)
out = serializer(header, md[header])
# test if 'out' is a iterator.
# cf. Effective Python Item 17
@@ -449,6 +511,16 @@ def _serialize_single_genbank(obj, fh):
fh.write(s)
else:
fh.write(out)
+ if header == 'FEATURES':
+ if obj.has_interval_metadata():
+ # magic number 21: the amount of indentation before
+ # feature table starts as defined by INSDC
+ indent = 21
+ fh.write('{header:<{indent}}Location/Qualifiers\n'.format(
+ header=header, indent=indent))
+ for s in serializer(obj.interval_metadata._intervals, indent):
+ fh.write(s)
+ # write out the sequence
# always write RNA seq as DNA
if isinstance(obj, RNA):
obj = obj.reverse_transcribe()
@@ -533,7 +605,7 @@ def _parse_reference(lines):
skip_blanks=True, strip=False)
for section in section_splitter(lines):
label, data = _parse_section_default(
- section, join_delimitor=' ', return_label=True)
+ section, join_delimiter=' ', return_label=True)
res[label] = data
return res
@@ -595,175 +667,6 @@ def _serialize_source(header, obj, indent=12):
return s
-def _parse_features(lines, length):
- '''Parse FEATURES field.
- '''
- features = []
- positional_metadata = []
- # skip the 1st FEATURES line
- if lines[0].startswith('FEATURES'):
- lines = lines[1:]
- # magic number 20: the lines following header of each feature
- # are at least indented with 20 spaces.
- feature_indent = ' ' * 20
- section_splitter = _yield_section(
- lambda x: not x.startswith(feature_indent),
- skip_blanks=True, strip=False)
- for i, section in enumerate(section_splitter(lines)):
- # print(i) ; continue
- feature, pmd = _parse_single_feature(section, length, i)
- features.append(feature)
- positional_metadata.append(pmd)
- return features, positional_metadata
-
-
-def _serialize_features(header, obj, indent=21):
- first = True
- for feature in obj:
- if first:
- first = False
- yield '{header:<{indent}}Location/Qualifiers\n{feat}'.format(
- header=header, indent=indent,
- feat=_serialize_single_feature(feature, indent))
- else:
- yield _serialize_single_feature(feature, indent)
-
-
-def _parse_single_feature(lines, length, index):
- '''Parse a feature.
-
- Returns
- -------
- tuple
- Tuple of a dict of `metadata` and a pandas.Series of
- `positional_metadata` for the feature.
-
- '''
- feature = {}
- feature['index_'] = index
- # each component of a feature starts with '/', except the 1st
- # component of location.
- section_splitter = _yield_section(
- lambda x: x.startswith('/'), strip=True)
- first = True
- for section in section_splitter(lines):
- if first:
- # first section is the Location string
- first = False
- type, location = _parse_section_default(
- section, join_delimitor='', return_label=True)
- feature['type_'] = type
- feature['location'] = location
- loc, loc_pmd = _parse_loc_str(location, length)
- feature.update(loc)
- else:
- # following sections are Qualifiers
- k, v = _parse_section_default(
- section, label_delimitor='=',
- join_delimitor=' ', return_label=True)
- k = k[1:]
-
- # some Qualifiers can appear multiple times
- if k in feature:
- if not isinstance(feature[k], list):
- feature[k] = [feature[k]]
- feature[k].append(v)
- else:
- feature[k] = v
- return feature, loc_pmd
-
-
-def _serialize_single_feature(obj, indent=21):
- padding = ' ' * 8
- qualifiers = []
- for k in sorted(obj):
- if k.endswith('_') or k in ('location', 'type'):
- continue
- v = obj[k]
- if isinstance(v, list):
- for vi in v:
- qualifiers.append(_serialize_qualifier(k, vi))
- else:
- qualifiers.append(_serialize_qualifier(k, v))
-
- qualifiers = [' ' * indent + i for i in qualifiers]
- return '{header:>{indent}}{loc}\n{qualifiers}\n'.format(
- header=obj['type_'] + padding, loc=obj['location'],
- indent=indent, qualifiers='\n'.join(qualifiers))
-
-
-def _serialize_qualifier(key, value):
- '''Serialize a Qualifier in a feature.
-
- Parameters
- ----------
- value : int, str
- '''
- # if value is empty
- if not value:
- return '/%s' % key
-
- return '/{k}={v}'.format(k=key, v=value)
-
-
-def _parse_loc_str(loc_str, length):
- '''Parse location string.
-
- Warning: This converts coordinates to 0-based from 1-based as
- in GenBank format.
-
- The location descriptor can be one of the following:
- (a) a single base number. e.g. 467
- (b) a site between two indicated adjoining bases. e.g. 123^124
- (c) a single base chosen from within a specified range of bases (not
- allowed for new entries). e.g. 102.110
- (d) the base numbers delimiting a sequence span. e.g.340..565
- (e) a remote entry identifier followed by a local location
- descriptor (i.e., a-d). e.g. J00194.1:100..202
-
- TODO:
- handle (b), (c), (e) cases correctly
- '''
- pmd = np.zeros(length, dtype=bool)
- res = {'rc_': False,
- 'left_partial_': False,
- 'right_partial_': False}
- items = re.split('[(),]+', loc_str)
- operators = ['join', 'complement', 'order']
- if 'complement' in items:
- res['rc_'] = True
- for i in items:
- i = i.strip()
- if i in operators or not i:
- continue
- elif ':' in i: # (e)
- index = []
- elif '..' in i: # (d)
- beg, end = i.split('..')
- if beg.startswith('<'):
- beg = beg[1:]
- res['left_partial_'] = True
- if end.startswith('>'):
- end = end[1:]
- res['right_partial_'] = True
- beg = int(beg)
- end = int(end)
- index = range(beg-1, end)
- elif '.' in i: # (c)
- index = []
- elif i.isdigit(): # (a)
- index = int(i) - 1
- elif '^' in i: # (b)
- index = []
- else:
- raise GenBankFormatError(
- 'Could not parse location string: "%s"' %
- loc_str)
- pmd[index] = True
-
- return res, pd.Series(pmd)
-
-
def _parse_origin(lines):
'''Parse the ORIGIN section for sequence.
'''
@@ -797,79 +700,11 @@ def _serialize_origin(seq, indent=9):
yield s
-def _parse_section_default(
- lines, label_delimitor=None, join_delimitor=' ', return_label=False):
- '''Parse sections in default way.
-
- Do 2 things:
- 1. split first line with label_delimitor for label
- 2. join all the lines into one str with join_delimitor.
- '''
- data = []
- first = True
- label = None
- for line in lines:
- if first:
- items = line.split(label_delimitor, 1)
-
- if len(items) == 2:
- label, section = items
- else:
- label = items[0]
- section = ""
- data.append(section)
- first = False
- else:
- data.append(line)
- data = join_delimitor.join(i.strip() for i in data)
- if return_label:
- return label, data
- else:
- return data
-
-
-def _serialize_section_default(header, obj, indent=12):
- return '{header:<{indent}}{obj}\n'.format(
- header=header, obj=obj, indent=indent)
-
-
-def _yield_section(is_another_section, **kwargs):
- '''Returns function that returns successive sections from file.
-
- Parameters
- ----------
- is_another_section : callable
- It takes a string as input and return a boolean indicating
- a new section starts.
- kwargs : dict, optional
- Keyword arguments will be passed to `_line_generator`.
-
- Returns
- -------
- function
- A function accept a list of lines as input and return
- a generator to yield section one by one.
- '''
- def parser(lines):
- curr = []
- for line in _line_generator(lines, **kwargs):
- # if we find another, return the previous section
- if is_another_section(line):
- if curr:
- yield curr
- curr = []
- curr.append(line)
- # don't forget to return the last section in the file
- if curr:
- yield curr
- return parser
-
-
_PARSER_TABLE = {
'LOCUS': _parse_locus,
'SOURCE': _parse_source,
'REFERENCE': _parse_reference,
- 'FEATURES': _parse_features,
+ 'FEATURES': _parse_feature_table,
'ORIGIN': _parse_origin}
@@ -877,4 +712,4 @@ _SERIALIZER_TABLE = {
'LOCUS': _serialize_locus,
'SOURCE': _serialize_source,
'REFERENCE': _serialize_reference,
- 'FEATURES': _serialize_features}
+ 'FEATURES': _serialize_feature_table}
diff --git a/skbio/io/format/tests/data/genbank_multi_records b/skbio/io/format/tests/data/genbank_multi_records
index a4d9808..26b9771 100644
--- a/skbio/io/format/tests/data/genbank_multi_records
+++ b/skbio/io/format/tests/data/genbank_multi_records
@@ -20,9 +20,9 @@ REFERENCE 1 (residues 1 to 9)
PUBMED 7764422
COMMENT Method: direct peptide sequencing.
FEATURES Location/Qualifiers
- source 1..9
+ source 1..9
/organism="Bacteria"
- Protein 1..>9
+ Protein 1..>9
/product="L-carnitine amidase"
ORIGIN
1 gsreildfk
@@ -36,10 +36,10 @@ SOURCE uncultured Xylanimonas sp.
ORGANISM uncultured Xylanimonas sp.
Bacteria; Actinobacteria; Micrococcales; Promicromonosporaceae; Xylanimonas; environmental samples.
FEATURES Location/Qualifiers
- source 1..9
+ source 1..9
/country="Brazil: Parana, Paranavai"
/environmental_sample
- rRNA complement(<2..>8)
+ rRNA complement(<2..>8)
/product="16S ribosomal RNA"
ORIGIN
1 catgcaggc
diff --git a/skbio/io/format/tests/data/genbank_single_record b/skbio/io/format/tests/data/genbank_single_record
index cf1bf02..104c785 100644
--- a/skbio/io/format/tests/data/genbank_single_record
+++ b/skbio/io/format/tests/data/genbank_single_record
@@ -8,16 +8,16 @@ SOURCE Escherichia coli
Bacteria; Proteobacteria; Gammaproteobacteria; Enterobacteriales; Enterobacteriaceae; Escherichia.
COMMENT Original source text: E.coli, cDNA to mRNA.
FEATURES Location/Qualifiers
- source 1..63
+ source 1..63
/db_xref="taxon:562"
/mol_type="mRNA"
/organism="Escherichia coli"
- CDS 1..>63
- /codon_start=1
+ CDS 1..>63
/db_xref="GI:145230"
/db_xref="taxon:562"
/db_xref="taxon:561"
/note="alkaline phosphatase signal peptide"
+ /codon_start=1
/protein_id="AAA23431.1"
/transl_table=11
/translation="MKQSTIALAVLPLLFTPVTKA"
diff --git a/skbio/io/format/tests/test_fasta.py b/skbio/io/format/tests/test_fasta.py
index 38b1a0b..fe1e1c3 100644
--- a/skbio/io/format/tests/test_fasta.py
+++ b/skbio/io/format/tests/test_fasta.py
@@ -6,6 +6,7 @@
# The full license is in the file COPYING.txt, distributed with this software.
# ----------------------------------------------------------------------------
+import copy
import io
import string
from unittest import TestCase, main
@@ -475,7 +476,7 @@ class ReaderTests(TestCase):
obs = list(_fasta_to_generator(fasta_fp, **kwargs))
self.assertEqual(len(obs), len(exp))
for o, e in zip(obs, exp):
- e = e.copy()
+ e = copy.copy(e)
del e.positional_metadata['quality']
self.assertEqual(o, e)
@@ -640,7 +641,7 @@ class ReaderTests(TestCase):
self.assertEqual(len(obs), len(exp))
for o, e in zip(obs, exp):
- e = e.copy()
+ e = copy.copy(e)
del e.positional_metadata['quality']
self.assertEqual(o, e)
diff --git a/skbio/io/format/tests/test_genbank.py b/skbio/io/format/tests/test_genbank.py
index e3eeb14..fb9b869 100644
--- a/skbio/io/format/tests/test_genbank.py
+++ b/skbio/io/format/tests/test_genbank.py
@@ -7,12 +7,10 @@
# ----------------------------------------------------------------------------
import io
-import numpy as np
-import pandas as pd
-import numpy.testing as npt
from unittest import TestCase, main
from skbio import Protein, DNA, RNA, Sequence
+from skbio.metadata import IntervalMetadata
from skbio.util import get_data_path
from skbio.io import GenBankFormatError
from skbio.io.format.genbank import (
@@ -20,7 +18,6 @@ from skbio.io.format.genbank import (
_genbank_to_generator, _genbank_to_sequence,
_genbank_to_dna, _genbank_to_rna, _genbank_to_protein,
_parse_locus, _parse_reference,
- _parse_loc_str, _parse_section_default,
_generator_to_genbank, _sequence_to_genbank,
_protein_to_genbank, _rna_to_genbank, _dna_to_genbank,
_serialize_locus)
@@ -87,33 +84,31 @@ class GenBankIOTests(TestCase):
Protein)
self.single_rna_fp = get_data_path('genbank_single_record')
+ imd = IntervalMetadata(63)
+ imd.add([(0, 63)],
+ [(False, False)],
+ {'db_xref': '"taxon:562"',
+ 'mol_type': '"mRNA"',
+ 'organism': '"Escherichia coli"',
+ 'type': 'source',
+ 'strand': '+',
+ '__location': '1..63'})
+ imd.add([(0, 63)],
+ [(False, True)],
+ {'phase': 0,
+ 'db_xref': ['"GI:145230"', '"taxon:562"', '"taxon:561"'],
+ '__location': '1..>63',
+ 'strand': '+',
+ 'note': '"alkaline phosphatase signal peptide"',
+ 'protein_id': '"AAA23431.1"',
+ 'transl_table': '11',
+ 'translation': '"MKQSTIALAVLPLLFTPVTKA"',
+ 'type': 'CDS'})
self.single_rna = (
'gugaaacaaagcacuauugcacuggcugucuuaccguuacuguuuaccccugugacaaaagcc',
{'ACCESSION': 'M14399',
'COMMENT': 'Original source text: E.coli, cDNA to mRNA.',
'DEFINITION': "alkaline phosphatase signal mRNA, 5' end.",
- 'FEATURES': [{'db_xref': '"taxon:562"',
- 'index_': 0,
- 'left_partial_': False,
- 'location': '1..63',
- 'mol_type': '"mRNA"',
- 'organism': '"Escherichia coli"',
- 'rc_': False,
- 'right_partial_': False,
- 'type_': 'source'},
- {'codon_start': '1',
- 'db_xref': [
- '"GI:145230"', '"taxon:562"', '"taxon:561"'],
- 'index_': 1,
- 'left_partial_': False,
- 'location': '1..>63',
- 'note': '"alkaline phosphatase signal peptide"',
- 'protein_id': '"AAA23431.1"',
- 'rc_': False,
- 'right_partial_': True,
- 'transl_table': '11',
- 'translation': '"MKQSTIALAVLPLLFTPVTKA"',
- 'type_': 'CDS'}],
'KEYWORDS': 'alkaline phosphatase; signal peptide.',
'LOCUS': {'date': '26-APR-1993',
'division': 'BCT',
@@ -127,8 +122,7 @@ class GenBankIOTests(TestCase):
'Gammaproteobacteria; Enterobacteriales; '
'Enterobacteriaceae; Escherichia.'},
'VERSION': 'M14399.1 GI:145229'},
- pd.DataFrame({0: np.ones(63, dtype=bool),
- 1: np.ones(63, dtype=bool)}),
+ imd,
RNA)
# test:
@@ -137,26 +131,36 @@ class GenBankIOTests(TestCase):
# 3. DNA, RNA, Protein type
# 4. variation of formats
self.multi_fp = get_data_path('genbank_multi_records')
+ imd_pro = IntervalMetadata(9)
+ imd_pro.add([(0, 9)], [(False, False)],
+ {'organism': '"Bacteria"',
+ 'type': 'source',
+ 'strand': '+',
+ '__location': '1..9'},)
+ imd_pro.add([(0, 9)], [(False, True)],
+ {'__location': '1..>9',
+ 'product': '"L-carnitine amidase"',
+ 'strand': '+',
+ 'type': 'Protein'})
+ imd_dna = IntervalMetadata(9)
+ imd_dna.add([(0, 9)], [(False, False)],
+ {'country': '"Brazil: Parana, Paranavai"',
+ 'type': 'source',
+ 'strand': '+',
+ '__location': '1..9',
+ 'environmental_sample': ''})
+ imd_dna.add([(1, 8)], [(True, True)],
+ {'__location': 'complement(<2..>8)',
+ 'product': '"16S ribosomal RNA"',
+ 'strand': '-',
+ 'type': 'rRNA'})
+
self.multi = (
('gsreildfk',
{'ACCESSION': 'AAB29917',
'COMMENT': 'Method: direct peptide sequencing.',
'DBSOURCE': 'accession AAB29917.1',
'DEFINITION': 'L-carnitine amidase {N-terminal}',
- 'FEATURES': [{'index_': 0,
- 'left_partial_': False,
- 'location': '1..9',
- 'organism': '"Bacteria"',
- 'rc_': False,
- 'right_partial_': False,
- 'type_': 'source'},
- {'index_': 1,
- 'left_partial_': False,
- 'location': '1..>9',
- 'product': '"L-carnitine amidase"',
- 'rc_': False,
- 'right_partial_': True,
- 'type_': 'Protein'}],
'KEYWORDS': '.',
'LOCUS': {'date': '23-SEP-1994',
'division': 'BCT',
@@ -179,28 +183,12 @@ class GenBankIOTests(TestCase):
'SOURCE': {'ORGANISM': 'Bacteria',
'taxonomy': 'Unclassified.'},
'VERSION': 'AAB29917.1 GI:545426'},
- pd.DataFrame({0: np.ones(9, dtype=bool),
- 1: np.ones(9, dtype=bool)}),
+ imd_pro,
Protein),
('catgcaggc',
{'ACCESSION': 'HQ018078',
'DEFINITION': 'Uncultured Xylanimonas sp.16S, partial',
- 'FEATURES': [{'country': '"Brazil: Parana, Paranavai"',
- 'environmental_sample': '',
- 'index_': 0,
- 'left_partial_': False,
- 'location': '1..9',
- 'rc_': False,
- 'right_partial_': False,
- 'type_': 'source'},
- {'index_': 1,
- 'left_partial_': True,
- 'location': 'complement(<2..>8)',
- 'product': '"16S ribosomal RNA"',
- 'rc_': True,
- 'right_partial_': True,
- 'type_': 'rRNA'}],
'KEYWORDS': 'ENV.',
'LOCUS': {'date': '29-AUG-2010',
'division': 'ENV',
@@ -214,8 +202,7 @@ class GenBankIOTests(TestCase):
'Micrococcales; Promicromonosporaceae; '
'Xylanimonas; environmental samples.'},
'VERSION': 'HQ018078.1 GI:304421728'},
- pd.DataFrame({0: [True] * 9,
- 1: [False] + [True] * 7 + [False]}),
+ imd_dna,
DNA))
@@ -257,79 +244,6 @@ REFERENCE 1 (bases 1 to 154478)
'Could not parse the LOCUS line:.*'):
_parse_locus(line)
- def test_parse_section_default(self):
- lines = [
- ['FOO blah blah',
- ' blah'],
- ['FOO=blah',
- ' blah'],
- ['FOO']]
- kwargs = [{'join_delimitor': '=', 'return_label': False},
- {'label_delimitor': '=', 'join_delimitor': '',
- 'return_label': True},
- {'label_delimitor': '=', 'join_delimitor': '=',
- 'return_label': True}]
- expects = ['blah blah=blah',
- ('FOO', 'blahblah'),
- ('FOO', '')]
- for i, j, k in zip(lines, kwargs, expects):
- self.assertEqual(k, _parse_section_default(i, **j))
-
- def test_parse_loc_str(self):
- length = 12
-
- examples = [
- '',
- '9', # a single base in the presented sequence
- '3..8',
- '<3..8',
- '1..>8',
- 'complement(3..8)',
- 'complement(join(3..5,7..9))',
- 'join(3..5,7..9)',
- 'J00194.1:1..9',
- '1.9',
- '1^9']
-
- expects = [
- ({'right_partial_': False, 'left_partial_': False, 'rc_': False},
- np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], dtype=bool)),
- ({'right_partial_': False, 'left_partial_': False, 'rc_': False},
- np.array([0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], dtype=bool)),
- ({'right_partial_': False, 'left_partial_': False, 'rc_': False},
- np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], dtype=bool)),
- ({'right_partial_': False, 'left_partial_': True, 'rc_': False},
- np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], dtype=bool)),
- ({'right_partial_': True, 'left_partial_': False, 'rc_': False},
- np.array([1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], dtype=bool)),
- ({'right_partial_': False, 'left_partial_': False, 'rc_': True},
- np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], dtype=bool)),
- ({'right_partial_': False, 'left_partial_': False, 'rc_': True},
- np.array([0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0], dtype=bool)),
- ({'right_partial_': False, 'left_partial_': False, 'rc_': False},
- np.array([0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0], dtype=bool)),
- ({'right_partial_': False, 'left_partial_': False, 'rc_': False},
- np.zeros(length, dtype=bool)),
- ({'right_partial_': False, 'left_partial_': False, 'rc_': False},
- np.zeros(length, dtype=bool)),
- ({'right_partial_': False, 'left_partial_': False, 'rc_': False},
- np.zeros(length, dtype=bool))]
- for example, expect in zip(examples, expects):
- parsed = _parse_loc_str(example, length)
- self.assertDictEqual(parsed[0], expect[0])
- npt.assert_equal(parsed[1], expect[1])
-
- def test_parse_loc_str_invalid(self):
- length = 12
- examples = [
- 'abc',
- '3-8']
- for example in examples:
- with self.assertRaisesRegex(GenBankFormatError,
- 'Could not parse location string: '
- '"%s"' % example):
- _parse_loc_str(example, length)
-
def test_genbank_to_generator_single(self):
# test single record and uppercase sequence
for c in [Sequence, Protein]:
@@ -341,23 +255,24 @@ REFERENCE 1 (bases 1 to 154478)
def test_genbank_to_generator(self):
for i, obs in enumerate(_genbank_to_generator(self.multi_fp)):
- seq, md, pmd, constructor = self.multi[i]
+ seq, md, imd, constructor = self.multi[i]
exp = constructor(seq, metadata=md, lowercase=True,
- positional_metadata=pmd)
+ interval_metadata=imd)
self.assertEqual(exp, obs)
def test_genbank_to_sequence(self):
for i, exp in enumerate(self.multi):
obs = _genbank_to_sequence(self.multi_fp, seq_num=i+1)
exp = Sequence(exp[0], metadata=exp[1], lowercase=True,
- positional_metadata=exp[2])
+ interval_metadata=exp[2])
self.assertEqual(exp, obs)
def test_genbank_to_rna(self):
- seq, md, pmd, constructor = self.single_rna
+ seq, md, imd, constructor = self.single_rna
obs = _genbank_to_rna(self.single_rna_fp)
exp = constructor(seq, metadata=md,
- lowercase=True, positional_metadata=pmd)
+ lowercase=True, interval_metadata=imd)
+
self.assertEqual(exp, obs)
def test_genbank_to_dna(self):
@@ -365,7 +280,8 @@ REFERENCE 1 (bases 1 to 154478)
exp = self.multi[i]
obs = _genbank_to_dna(self.multi_fp, seq_num=i+1)
exp = DNA(exp[0], metadata=exp[1], lowercase=True,
- positional_metadata=exp[2])
+ interval_metadata=exp[2])
+
self.assertEqual(exp, obs)
def test_genbank_to_protein(self):
@@ -373,7 +289,7 @@ REFERENCE 1 (bases 1 to 154478)
exp = self.multi[i]
obs = _genbank_to_protein(self.multi_fp, seq_num=i+1)
exp = Protein(exp[0], metadata=exp[1],
- lowercase=True, positional_metadata=exp[2])
+ lowercase=True, interval_metadata=exp[2])
self.assertEqual(exp, obs)
@@ -384,54 +300,52 @@ class WriterTests(GenBankIOTests):
_serialize_locus('LOCUS', parsed), serialized[0] + '\n')
def test_generator_to_genbank(self):
- seq, md, pmd, constructor = self.single
- obj = constructor(seq, md, pmd)
- fh = io.StringIO()
- _generator_to_genbank([obj], fh)
- obs = fh.getvalue()
- fh.close()
-
- with io.open(self.single_lower_fp) as fh:
+ seq, md, imd, constructor = self.single
+ obj = constructor(seq, md, interval_metadata=imd)
+ with io.StringIO() as fh:
+ _generator_to_genbank([obj], fh)
+ obs = fh.getvalue()
+
+ with open(self.single_lower_fp) as fh:
exp = fh.read()
self.assertEqual(obs, exp)
def test_sequence_to_genbank(self):
- fh = io.StringIO()
- for i, (seq, md, pmd, constructor) in enumerate(self.multi):
- obj = Sequence(seq, md, pmd, lowercase=True)
- _sequence_to_genbank(obj, fh)
- obs = fh.getvalue()
- fh.close()
-
- with io.open(self.multi_fp) as fh:
+ with io.StringIO() as fh:
+ for i, (seq, md, imd, constructor) in enumerate(self.multi):
+ obj = Sequence(seq, md, interval_metadata=imd, lowercase=True)
+ _sequence_to_genbank(obj, fh)
+ obs = fh.getvalue()
+
+ with open(self.multi_fp) as fh:
exp = fh.read()
+
self.assertEqual(obs, exp)
def test_dna_protein_to_genbank(self):
writers = [_protein_to_genbank,
_dna_to_genbank]
- fh = io.StringIO()
- for i, (seq, md, pmd, constructor) in enumerate(self.multi):
- obj = constructor(seq, md, pmd, lowercase=True)
- writers[i](obj, fh)
- obs = fh.getvalue()
- fh.close()
-
- with io.open(self.multi_fp) as fh:
+ with io.StringIO() as fh:
+ for i, (seq, md, imd, constructor) in enumerate(self.multi):
+ obj = constructor(
+ seq, md, interval_metadata=imd, lowercase=True)
+ writers[i](obj, fh)
+ obs = fh.getvalue()
+
+ with open(self.multi_fp) as fh:
exp = fh.read()
self.assertEqual(obs, exp)
def test_rna_to_genbank(self):
- fh = io.StringIO()
- seq, md, pmd, constructor = self.single_rna
- obj = constructor(seq, md, pmd, lowercase=True)
- _rna_to_genbank(obj, fh)
- obs = fh.getvalue()
- fh.close()
-
- with io.open(self.single_rna_fp) as fh:
+ with io.StringIO() as fh:
+ seq, md, imd, constructor = self.single_rna
+ obj = constructor(seq, md, interval_metadata=imd, lowercase=True)
+ _rna_to_genbank(obj, fh)
+ obs = fh.getvalue()
+
+ with open(self.single_rna_fp) as fh:
exp = fh.read()
self.assertEqual(obs, exp)
@@ -439,56 +353,51 @@ class WriterTests(GenBankIOTests):
class RoundtripTests(GenBankIOTests):
def test_roundtrip_generator(self):
- fh = io.StringIO()
- _generator_to_genbank(_genbank_to_generator(self.multi_fp), fh)
- obs = fh.getvalue()
- fh.close()
+ with io.StringIO() as fh:
+ _generator_to_genbank(_genbank_to_generator(self.multi_fp), fh)
+ obs = fh.getvalue()
- with io.open(self.multi_fp) as fh:
+ with open(self.multi_fp) as fh:
exp = fh.read()
self.assertEqual(obs, exp)
def test_roundtrip_rna(self):
- fh = io.StringIO()
- _rna_to_genbank(_genbank_to_rna(self.single_rna_fp), fh)
- obs = fh.getvalue()
- fh.close()
+ with io.StringIO() as fh:
+ _rna_to_genbank(_genbank_to_rna(self.single_rna_fp), fh)
+ obs = fh.getvalue()
- with io.open(self.single_rna_fp) as fh:
+ with open(self.single_rna_fp) as fh:
exp = fh.read()
self.assertEqual(obs, exp)
def test_roundtrip_dna(self):
- fh = io.StringIO()
- _dna_to_genbank(_genbank_to_dna(self.single_rna_fp), fh)
- obs = fh.getvalue()
- fh.close()
+ with io.StringIO() as fh:
+ _dna_to_genbank(_genbank_to_dna(self.single_rna_fp), fh)
+ obs = fh.getvalue()
- with io.open(self.single_rna_fp) as fh:
+ with open(self.single_rna_fp) as fh:
exp = fh.read()
self.assertEqual(obs, exp)
def test_roundtrip_protein(self):
- fh = io.StringIO()
- _protein_to_genbank(_genbank_to_protein(self.single_lower_fp), fh)
- obs = fh.getvalue()
- fh.close()
+ with io.StringIO() as fh:
+ _protein_to_genbank(_genbank_to_protein(self.single_lower_fp), fh)
+ obs = fh.getvalue()
- with io.open(self.single_lower_fp) as fh:
+ with open(self.single_lower_fp) as fh:
exp = fh.read()
self.assertEqual(obs, exp)
def test_roundtrip_sequence(self):
- fh = io.StringIO()
- _sequence_to_genbank(_genbank_to_sequence(self.single_rna_fp), fh)
- obs = fh.getvalue()
- fh.close()
+ with io.StringIO() as fh:
+ _sequence_to_genbank(_genbank_to_sequence(self.single_rna_fp), fh)
+ obs = fh.getvalue()
- with io.open(self.single_rna_fp) as fh:
+ with open(self.single_rna_fp) as fh:
exp = fh.read()
self.assertEqual(obs, exp)
diff --git a/skbio/io/format/tests/test_sequence_feature_vocabulary.py b/skbio/io/format/tests/test_sequence_feature_vocabulary.py
new file mode 100644
index 0000000..42fa063
--- /dev/null
+++ b/skbio/io/format/tests/test_sequence_feature_vocabulary.py
@@ -0,0 +1,102 @@
+# ----------------------------------------------------------------------------
+# Copyright (c) 2013--, scikit-bio development team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+# ----------------------------------------------------------------------------
+
+from unittest import TestCase, main
+
+from skbio.io.format._sequence_feature_vocabulary import (
+ _parse_loc_str, _parse_section_default, _serialize_location)
+from skbio.io import FileFormatError
+from skbio.metadata import IntervalMetadata
+
+
+class Tests(TestCase):
+ def test_parse_section_default(self):
+ lines = [
+ ['FOO blah blah',
+ ' blah'],
+ ['FOO=blah',
+ ' blah'],
+ ['FOO']]
+ kwargs = [{'join_delimiter': '=', 'return_label': False},
+ {'label_delimiter': '=', 'join_delimiter': '',
+ 'return_label': True},
+ {'label_delimiter': '=', 'join_delimiter': '=',
+ 'return_label': True}]
+ expects = ['blah blah=blah',
+ ('FOO', 'blahblah'),
+ ('FOO', '')]
+ for i, j, k in zip(lines, kwargs, expects):
+ self.assertEqual(k, _parse_section_default(i, **j))
+
+ def test_parse_loc_str(self):
+ examples = [
+ '9', # a single base in the presented sequence
+ '3..8',
+ '<3..8',
+ '3..>8',
+ 'complement(3..>8)',
+ 'complement(join(3..>5,<7..9))',
+ 'join(J00194.1:1..9,3..8)',
+ 'join(3..8,J00194.1:1..9)',
+ '1.9',
+ '1^2']
+
+ expects = [
+ ([(8, 9)], [(False, False)], {'strand': '+'}),
+ ([(2, 8)], [(False, False)], {'strand': '+'}),
+ ([(2, 8)], [(True, False)], {'strand': '+'}),
+ ([(2, 8)], [(False, True)], {'strand': '+'}),
+ ([(2, 8)], [(False, True)], {'strand': '-'}),
+ ([(2, 5), (6, 9)], [(False, True), (True, False)],
+ {'strand': '-'}),
+ ([(2, 8)], [(False, False)], {'strand': '+'}),
+ ([(2, 8)], [(False, False)], {'strand': '+'}),
+ ([(0, 9)], [(False, False)], {'strand': '+'}),
+ ([(0, 1)], [(False, False)], {'strand': '+'})]
+
+ for example, expect in zip(examples, expects):
+ parsed = _parse_loc_str(example)
+ self.assertEqual(parsed, expect)
+
+ def test_parse_loc_str_invalid(self):
+ examples = [
+ 'abc',
+ '3-8']
+ for example in examples:
+ with self.assertRaisesRegex(FileFormatError,
+ 'Could not parse location string: '
+ '"%s"' % example):
+ _parse_loc_str(example)
+
+ def test_serialize_location(self):
+ imd = IntervalMetadata(9)
+ i1 = imd.add([(0, 1)])
+ self.assertEqual(_serialize_location(i1), '1')
+
+ i2 = imd.add([(0, 2)], [(True, True)])
+ self.assertEqual(_serialize_location(i2), '<1..>2')
+
+ i3 = imd.add([(0, 2)], [(False, True)])
+ self.assertEqual(_serialize_location(i3), '1..>2')
+
+ i4 = imd.add([(0, 2)], [(True, False)])
+ self.assertEqual(_serialize_location(i4), '<1..2')
+
+ i5 = imd.add([(0, 2), (3, 9)], metadata={'strand': '-'})
+ self.assertEqual(_serialize_location(i5),
+ 'complement(join(1..2,4..9))')
+
+ i6 = imd.add([(0, 2), (3, 9)],
+ [(True, False), (False, True)],
+ metadata={'strand': '-'})
+ self.assertEqual(_serialize_location(i6),
+ 'complement(join(<1..2,4..>9))')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/skbio/metadata/__init__.py b/skbio/metadata/__init__.py
index 6db261d..624babd 100644
--- a/skbio/metadata/__init__.py
+++ b/skbio/metadata/__init__.py
@@ -5,6 +5,15 @@ Metadata (:mod:`skbio.metadata`)
.. currentmodule:: skbio.metadata
This module provides classes for storing and working with metadata.
+
+Classes
+-------
+
+.. autosummary::
+ :toctree: generated/
+
+ Interval
+ IntervalMetadata
"""
# ----------------------------------------------------------------------------
@@ -16,4 +25,9 @@ This module provides classes for storing and working with metadata.
# ----------------------------------------------------------------------------
from skbio.util import TestRunner
+
+from ._interval import Interval, IntervalMetadata
+
+__all__ = ['Interval', 'IntervalMetadata']
+
test = TestRunner(__file__).test
diff --git a/skbio/metadata/_intersection.c b/skbio/metadata/_intersection.c
new file mode 100644
index 0000000..c99f34a
--- /dev/null
+++ b/skbio/metadata/_intersection.c
@@ -0,0 +1,10555 @@
+/* Generated by Cython 0.24.1 */
+
+#define PY_SSIZE_T_CLEAN
+#include "Python.h"
+#ifndef Py_PYTHON_H
+ #error Python headers needed to compile C extensions, please install development version of Python.
+#elif PY_VERSION_HEX < 0x02060000 || (0x03000000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x03020000)
+ #error Cython requires Python 2.6+ or Python 3.2+.
+#else
+#define CYTHON_ABI "0_24_1"
+#include <stddef.h>
+#ifndef offsetof
+ #define offsetof(type, member) ( (size_t) & ((type*)0) -> member )
+#endif
+#if !defined(WIN32) && !defined(MS_WINDOWS)
+ #ifndef __stdcall
+ #define __stdcall
+ #endif
+ #ifndef __cdecl
+ #define __cdecl
+ #endif
+ #ifndef __fastcall
+ #define __fastcall
+ #endif
+#endif
+#ifndef DL_IMPORT
+ #define DL_IMPORT(t) t
+#endif
+#ifndef DL_EXPORT
+ #define DL_EXPORT(t) t
+#endif
+#ifndef PY_LONG_LONG
+ #define PY_LONG_LONG LONG_LONG
+#endif
+#ifndef Py_HUGE_VAL
+ #define Py_HUGE_VAL HUGE_VAL
+#endif
+#ifdef PYPY_VERSION
+ #define CYTHON_COMPILING_IN_PYPY 1
+ #define CYTHON_COMPILING_IN_CPYTHON 0
+#else
+ #define CYTHON_COMPILING_IN_PYPY 0
+ #define CYTHON_COMPILING_IN_CPYTHON 1
+#endif
+#if !defined(CYTHON_USE_PYLONG_INTERNALS) && CYTHON_COMPILING_IN_CPYTHON && PY_VERSION_HEX >= 0x02070000
+ #define CYTHON_USE_PYLONG_INTERNALS 1
+#endif
+#if CYTHON_USE_PYLONG_INTERNALS
+ #include "longintrepr.h"
+ #undef SHIFT
+ #undef BASE
+ #undef MASK
+#endif
+#if CYTHON_COMPILING_IN_PYPY && PY_VERSION_HEX < 0x02070600 && !defined(Py_OptimizeFlag)
+ #define Py_OptimizeFlag 0
+#endif
+#define __PYX_BUILD_PY_SSIZE_T "n"
+#define CYTHON_FORMAT_SSIZE_T "z"
+#if PY_MAJOR_VERSION < 3
+ #define __Pyx_BUILTIN_MODULE_NAME "__builtin__"
+ #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\
+ PyCode_New(a+k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
+ #define __Pyx_DefaultClassType PyClass_Type
+#else
+ #define __Pyx_BUILTIN_MODULE_NAME "builtins"
+ #define __Pyx_PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)\
+ PyCode_New(a, k, l, s, f, code, c, n, v, fv, cell, fn, name, fline, lnos)
+ #define __Pyx_DefaultClassType PyType_Type
+#endif
+#ifndef Py_TPFLAGS_CHECKTYPES
+ #define Py_TPFLAGS_CHECKTYPES 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_INDEX
+ #define Py_TPFLAGS_HAVE_INDEX 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_NEWBUFFER
+ #define Py_TPFLAGS_HAVE_NEWBUFFER 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_FINALIZE
+ #define Py_TPFLAGS_HAVE_FINALIZE 0
+#endif
+#if PY_VERSION_HEX > 0x03030000 && defined(PyUnicode_KIND)
+ #define CYTHON_PEP393_ENABLED 1
+ #define __Pyx_PyUnicode_READY(op) (likely(PyUnicode_IS_READY(op)) ?\
+ 0 : _PyUnicode_Ready((PyObject *)(op)))
+ #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_LENGTH(u)
+ #define __Pyx_PyUnicode_READ_CHAR(u, i) PyUnicode_READ_CHAR(u, i)
+ #define __Pyx_PyUnicode_KIND(u) PyUnicode_KIND(u)
+ #define __Pyx_PyUnicode_DATA(u) PyUnicode_DATA(u)
+ #define __Pyx_PyUnicode_READ(k, d, i) PyUnicode_READ(k, d, i)
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != (likely(PyUnicode_IS_READY(u)) ? PyUnicode_GET_LENGTH(u) : PyUnicode_GET_SIZE(u)))
+#else
+ #define CYTHON_PEP393_ENABLED 0
+ #define __Pyx_PyUnicode_READY(op) (0)
+ #define __Pyx_PyUnicode_GET_LENGTH(u) PyUnicode_GET_SIZE(u)
+ #define __Pyx_PyUnicode_READ_CHAR(u, i) ((Py_UCS4)(PyUnicode_AS_UNICODE(u)[i]))
+ #define __Pyx_PyUnicode_KIND(u) (sizeof(Py_UNICODE))
+ #define __Pyx_PyUnicode_DATA(u) ((void*)PyUnicode_AS_UNICODE(u))
+ #define __Pyx_PyUnicode_READ(k, d, i) ((void)(k), (Py_UCS4)(((Py_UNICODE*)d)[i]))
+ #define __Pyx_PyUnicode_IS_TRUE(u) (0 != PyUnicode_GET_SIZE(u))
+#endif
+#if CYTHON_COMPILING_IN_PYPY
+ #define __Pyx_PyUnicode_Concat(a, b) PyNumber_Add(a, b)
+ #define __Pyx_PyUnicode_ConcatSafe(a, b) PyNumber_Add(a, b)
+#else
+ #define __Pyx_PyUnicode_Concat(a, b) PyUnicode_Concat(a, b)
+ #define __Pyx_PyUnicode_ConcatSafe(a, b) ((unlikely((a) == Py_None) || unlikely((b) == Py_None)) ?\
+ PyNumber_Add(a, b) : __Pyx_PyUnicode_Concat(a, b))
+#endif
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyUnicode_Contains)
+ #define PyUnicode_Contains(u, s) PySequence_Contains(u, s)
+#endif
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyByteArray_Check)
+ #define PyByteArray_Check(obj) PyObject_TypeCheck(obj, &PyByteArray_Type)
+#endif
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Format)
+ #define PyObject_Format(obj, fmt) PyObject_CallMethod(obj, "__format__", "O", fmt)
+#endif
+#if CYTHON_COMPILING_IN_PYPY && !defined(PyObject_Malloc)
+ #define PyObject_Malloc(s) PyMem_Malloc(s)
+ #define PyObject_Free(p) PyMem_Free(p)
+ #define PyObject_Realloc(p) PyMem_Realloc(p)
+#endif
+#define __Pyx_PyString_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : __Pyx_PyString_Format(a, b))
+#define __Pyx_PyUnicode_FormatSafe(a, b) ((unlikely((a) == Py_None)) ? PyNumber_Remainder(a, b) : PyUnicode_Format(a, b))
+#if PY_MAJOR_VERSION >= 3
+ #define __Pyx_PyString_Format(a, b) PyUnicode_Format(a, b)
+#else
+ #define __Pyx_PyString_Format(a, b) PyString_Format(a, b)
+#endif
+#if PY_MAJOR_VERSION < 3 && !defined(PyObject_ASCII)
+ #define PyObject_ASCII(o) PyObject_Repr(o)
+#endif
+#if PY_MAJOR_VERSION >= 3
+ #define PyBaseString_Type PyUnicode_Type
+ #define PyStringObject PyUnicodeObject
+ #define PyString_Type PyUnicode_Type
+ #define PyString_Check PyUnicode_Check
+ #define PyString_CheckExact PyUnicode_CheckExact
+#endif
+#if PY_MAJOR_VERSION >= 3
+ #define __Pyx_PyBaseString_Check(obj) PyUnicode_Check(obj)
+ #define __Pyx_PyBaseString_CheckExact(obj) PyUnicode_CheckExact(obj)
+#else
+ #define __Pyx_PyBaseString_Check(obj) (PyString_Check(obj) || PyUnicode_Check(obj))
+ #define __Pyx_PyBaseString_CheckExact(obj) (PyString_CheckExact(obj) || PyUnicode_CheckExact(obj))
+#endif
+#ifndef PySet_CheckExact
+ #define PySet_CheckExact(obj) (Py_TYPE(obj) == &PySet_Type)
+#endif
+#define __Pyx_TypeCheck(obj, type) PyObject_TypeCheck(obj, (PyTypeObject *)type)
+#if PY_MAJOR_VERSION >= 3
+ #define PyIntObject PyLongObject
+ #define PyInt_Type PyLong_Type
+ #define PyInt_Check(op) PyLong_Check(op)
+ #define PyInt_CheckExact(op) PyLong_CheckExact(op)
+ #define PyInt_FromString PyLong_FromString
+ #define PyInt_FromUnicode PyLong_FromUnicode
+ #define PyInt_FromLong PyLong_FromLong
+ #define PyInt_FromSize_t PyLong_FromSize_t
+ #define PyInt_FromSsize_t PyLong_FromSsize_t
+ #define PyInt_AsLong PyLong_AsLong
+ #define PyInt_AS_LONG PyLong_AS_LONG
+ #define PyInt_AsSsize_t PyLong_AsSsize_t
+ #define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask
+ #define PyInt_AsUnsignedLongLongMask PyLong_AsUnsignedLongLongMask
+ #define PyNumber_Int PyNumber_Long
+#endif
+#if PY_MAJOR_VERSION >= 3
+ #define PyBoolObject PyLongObject
+#endif
+#if PY_MAJOR_VERSION >= 3 && CYTHON_COMPILING_IN_PYPY
+ #ifndef PyUnicode_InternFromString
+ #define PyUnicode_InternFromString(s) PyUnicode_FromString(s)
+ #endif
+#endif
+#if PY_VERSION_HEX < 0x030200A4
+ typedef long Py_hash_t;
+ #define __Pyx_PyInt_FromHash_t PyInt_FromLong
+ #define __Pyx_PyInt_AsHash_t PyInt_AsLong
+#else
+ #define __Pyx_PyInt_FromHash_t PyInt_FromSsize_t
+ #define __Pyx_PyInt_AsHash_t PyInt_AsSsize_t
+#endif
+#if PY_MAJOR_VERSION >= 3
+ #define __Pyx_PyMethod_New(func, self, klass) ((self) ? PyMethod_New(func, self) : PyInstanceMethod_New(func))
+#else
+ #define __Pyx_PyMethod_New(func, self, klass) PyMethod_New(func, self, klass)
+#endif
+#if PY_VERSION_HEX >= 0x030500B1
+#define __Pyx_PyAsyncMethodsStruct PyAsyncMethods
+#define __Pyx_PyType_AsAsync(obj) (Py_TYPE(obj)->tp_as_async)
+#elif CYTHON_COMPILING_IN_CPYTHON && PY_MAJOR_VERSION >= 3
+typedef struct {
+ unaryfunc am_await;
+ unaryfunc am_aiter;
+ unaryfunc am_anext;
+} __Pyx_PyAsyncMethodsStruct;
+#define __Pyx_PyType_AsAsync(obj) ((__Pyx_PyAsyncMethodsStruct*) (Py_TYPE(obj)->tp_reserved))
+#else
+#define __Pyx_PyType_AsAsync(obj) NULL
+#endif
+#ifndef CYTHON_RESTRICT
+ #if defined(__GNUC__)
+ #define CYTHON_RESTRICT __restrict__
+ #elif defined(_MSC_VER) && _MSC_VER >= 1400
+ #define CYTHON_RESTRICT __restrict
+ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define CYTHON_RESTRICT restrict
+ #else
+ #define CYTHON_RESTRICT
+ #endif
+#endif
+#define __Pyx_void_to_None(void_result) ((void)(void_result), Py_INCREF(Py_None), Py_None)
+
+#ifndef CYTHON_INLINE
+ #if defined(__GNUC__)
+ #define CYTHON_INLINE __inline__
+ #elif defined(_MSC_VER)
+ #define CYTHON_INLINE __inline
+ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define CYTHON_INLINE inline
+ #else
+ #define CYTHON_INLINE
+ #endif
+#endif
+
+#if defined(WIN32) || defined(MS_WINDOWS)
+ #define _USE_MATH_DEFINES
+#endif
+#include <math.h>
+#ifdef NAN
+#define __PYX_NAN() ((float) NAN)
+#else
+static CYTHON_INLINE float __PYX_NAN() {
+ float value;
+ memset(&value, 0xFF, sizeof(value));
+ return value;
+}
+#endif
+#if defined(__CYGWIN__) && defined(_LDBL_EQ_DBL)
+#define __Pyx_truncl trunc
+#else
+#define __Pyx_truncl truncl
+#endif
+
+
+#define __PYX_ERR(f_index, lineno, Ln_error) \
+{ \
+ __pyx_filename = __pyx_f[f_index]; __pyx_lineno = lineno; __pyx_clineno = __LINE__; goto Ln_error; \
+}
+
+#if PY_MAJOR_VERSION >= 3
+ #define __Pyx_PyNumber_Divide(x,y) PyNumber_TrueDivide(x,y)
+ #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceTrueDivide(x,y)
+#else
+ #define __Pyx_PyNumber_Divide(x,y) PyNumber_Divide(x,y)
+ #define __Pyx_PyNumber_InPlaceDivide(x,y) PyNumber_InPlaceDivide(x,y)
+#endif
+
+#ifndef __PYX_EXTERN_C
+ #ifdef __cplusplus
+ #define __PYX_EXTERN_C extern "C"
+ #else
+ #define __PYX_EXTERN_C extern
+ #endif
+#endif
+
+#define __PYX_HAVE__skbio__metadata___intersection
+#define __PYX_HAVE_API__skbio__metadata___intersection
+#include "stdlib.h"
+#ifdef _OPENMP
+#include <omp.h>
+#endif /* _OPENMP */
+
+#ifdef PYREX_WITHOUT_ASSERTIONS
+#define CYTHON_WITHOUT_ASSERTIONS
+#endif
+
+#ifndef CYTHON_UNUSED
+# if defined(__GNUC__)
+# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))
+# define CYTHON_UNUSED __attribute__ ((__unused__))
+# else
+# define CYTHON_UNUSED
+# endif
+# elif defined(__ICC) || (defined(__INTEL_COMPILER) && !defined(_MSC_VER))
+# define CYTHON_UNUSED __attribute__ ((__unused__))
+# else
+# define CYTHON_UNUSED
+# endif
+#endif
+#ifndef CYTHON_NCP_UNUSED
+# if CYTHON_COMPILING_IN_CPYTHON
+# define CYTHON_NCP_UNUSED
+# else
+# define CYTHON_NCP_UNUSED CYTHON_UNUSED
+# endif
+#endif
+typedef struct {PyObject **p; const char *s; const Py_ssize_t n; const char* encoding;
+ const char is_unicode; const char is_str; const char intern; } __Pyx_StringTabEntry;
+
+#define __PYX_DEFAULT_STRING_ENCODING_IS_ASCII 0
+#define __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT 0
+#define __PYX_DEFAULT_STRING_ENCODING ""
+#define __Pyx_PyObject_FromString __Pyx_PyBytes_FromString
+#define __Pyx_PyObject_FromStringAndSize __Pyx_PyBytes_FromStringAndSize
+#define __Pyx_uchar_cast(c) ((unsigned char)c)
+#define __Pyx_long_cast(x) ((long)x)
+#define __Pyx_fits_Py_ssize_t(v, type, is_signed) (\
+ (sizeof(type) < sizeof(Py_ssize_t)) ||\
+ (sizeof(type) > sizeof(Py_ssize_t) &&\
+ likely(v < (type)PY_SSIZE_T_MAX ||\
+ v == (type)PY_SSIZE_T_MAX) &&\
+ (!is_signed || likely(v > (type)PY_SSIZE_T_MIN ||\
+ v == (type)PY_SSIZE_T_MIN))) ||\
+ (sizeof(type) == sizeof(Py_ssize_t) &&\
+ (is_signed || likely(v < (type)PY_SSIZE_T_MAX ||\
+ v == (type)PY_SSIZE_T_MAX))) )
+#if defined (__cplusplus) && __cplusplus >= 201103L
+ #include <cstdlib>
+ #define __Pyx_sst_abs(value) std::abs(value)
+#elif SIZEOF_INT >= SIZEOF_SIZE_T
+ #define __Pyx_sst_abs(value) abs(value)
+#elif SIZEOF_LONG >= SIZEOF_SIZE_T
+ #define __Pyx_sst_abs(value) labs(value)
+#elif defined (_MSC_VER) && defined (_M_X64)
+ #define __Pyx_sst_abs(value) _abs64(value)
+#elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+ #define __Pyx_sst_abs(value) llabs(value)
+#elif defined (__GNUC__)
+ #define __Pyx_sst_abs(value) __builtin_llabs(value)
+#else
+ #define __Pyx_sst_abs(value) ((value<0) ? -value : value)
+#endif
+static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject*);
+static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject*, Py_ssize_t* length);
+#define __Pyx_PyByteArray_FromString(s) PyByteArray_FromStringAndSize((const char*)s, strlen((const char*)s))
+#define __Pyx_PyByteArray_FromStringAndSize(s, l) PyByteArray_FromStringAndSize((const char*)s, l)
+#define __Pyx_PyBytes_FromString PyBytes_FromString
+#define __Pyx_PyBytes_FromStringAndSize PyBytes_FromStringAndSize
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char*);
+#if PY_MAJOR_VERSION < 3
+ #define __Pyx_PyStr_FromString __Pyx_PyBytes_FromString
+ #define __Pyx_PyStr_FromStringAndSize __Pyx_PyBytes_FromStringAndSize
+#else
+ #define __Pyx_PyStr_FromString __Pyx_PyUnicode_FromString
+ #define __Pyx_PyStr_FromStringAndSize __Pyx_PyUnicode_FromStringAndSize
+#endif
+#define __Pyx_PyObject_AsSString(s) ((signed char*) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_AsUString(s) ((unsigned char*) __Pyx_PyObject_AsString(s))
+#define __Pyx_PyObject_FromCString(s) __Pyx_PyObject_FromString((const char*)s)
+#define __Pyx_PyBytes_FromCString(s) __Pyx_PyBytes_FromString((const char*)s)
+#define __Pyx_PyByteArray_FromCString(s) __Pyx_PyByteArray_FromString((const char*)s)
+#define __Pyx_PyStr_FromCString(s) __Pyx_PyStr_FromString((const char*)s)
+#define __Pyx_PyUnicode_FromCString(s) __Pyx_PyUnicode_FromString((const char*)s)
+#if PY_MAJOR_VERSION < 3
+static CYTHON_INLINE size_t __Pyx_Py_UNICODE_strlen(const Py_UNICODE *u)
+{
+ const Py_UNICODE *u_end = u;
+ while (*u_end++) ;
+ return (size_t)(u_end - u - 1);
+}
+#else
+#define __Pyx_Py_UNICODE_strlen Py_UNICODE_strlen
+#endif
+#define __Pyx_PyUnicode_FromUnicode(u) PyUnicode_FromUnicode(u, __Pyx_Py_UNICODE_strlen(u))
+#define __Pyx_PyUnicode_FromUnicodeAndLength PyUnicode_FromUnicode
+#define __Pyx_PyUnicode_AsUnicode PyUnicode_AsUnicode
+#define __Pyx_NewRef(obj) (Py_INCREF(obj), obj)
+#define __Pyx_Owned_Py_None(b) __Pyx_NewRef(Py_None)
+#define __Pyx_PyBool_FromLong(b) ((b) ? __Pyx_NewRef(Py_True) : __Pyx_NewRef(Py_False))
+static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject*);
+static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x);
+static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject*);
+static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t);
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __pyx_PyFloat_AsDouble(x) (PyFloat_CheckExact(x) ? PyFloat_AS_DOUBLE(x) : PyFloat_AsDouble(x))
+#else
+#define __pyx_PyFloat_AsDouble(x) PyFloat_AsDouble(x)
+#endif
+#define __pyx_PyFloat_AsFloat(x) ((float) __pyx_PyFloat_AsDouble(x))
+#if PY_MAJOR_VERSION >= 3
+#define __Pyx_PyNumber_Int(x) (PyLong_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Long(x))
+#else
+#define __Pyx_PyNumber_Int(x) (PyInt_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Int(x))
+#endif
+#define __Pyx_PyNumber_Float(x) (PyFloat_CheckExact(x) ? __Pyx_NewRef(x) : PyNumber_Float(x))
+#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+static int __Pyx_sys_getdefaultencoding_not_ascii;
+static int __Pyx_init_sys_getdefaultencoding_params(void) {
+ PyObject* sys;
+ PyObject* default_encoding = NULL;
+ PyObject* ascii_chars_u = NULL;
+ PyObject* ascii_chars_b = NULL;
+ const char* default_encoding_c;
+ sys = PyImport_ImportModule("sys");
+ if (!sys) goto bad;
+ default_encoding = PyObject_CallMethod(sys, (char*) "getdefaultencoding", NULL);
+ Py_DECREF(sys);
+ if (!default_encoding) goto bad;
+ default_encoding_c = PyBytes_AsString(default_encoding);
+ if (!default_encoding_c) goto bad;
+ if (strcmp(default_encoding_c, "ascii") == 0) {
+ __Pyx_sys_getdefaultencoding_not_ascii = 0;
+ } else {
+ char ascii_chars[128];
+ int c;
+ for (c = 0; c < 128; c++) {
+ ascii_chars[c] = c;
+ }
+ __Pyx_sys_getdefaultencoding_not_ascii = 1;
+ ascii_chars_u = PyUnicode_DecodeASCII(ascii_chars, 128, NULL);
+ if (!ascii_chars_u) goto bad;
+ ascii_chars_b = PyUnicode_AsEncodedString(ascii_chars_u, default_encoding_c, NULL);
+ if (!ascii_chars_b || !PyBytes_Check(ascii_chars_b) || memcmp(ascii_chars, PyBytes_AS_STRING(ascii_chars_b), 128) != 0) {
+ PyErr_Format(
+ PyExc_ValueError,
+ "This module compiled with c_string_encoding=ascii, but default encoding '%.200s' is not a superset of ascii.",
+ default_encoding_c);
+ goto bad;
+ }
+ Py_DECREF(ascii_chars_u);
+ Py_DECREF(ascii_chars_b);
+ }
+ Py_DECREF(default_encoding);
+ return 0;
+bad:
+ Py_XDECREF(default_encoding);
+ Py_XDECREF(ascii_chars_u);
+ Py_XDECREF(ascii_chars_b);
+ return -1;
+}
+#endif
+#if __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT && PY_MAJOR_VERSION >= 3
+#define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_DecodeUTF8(c_str, size, NULL)
+#else
+#define __Pyx_PyUnicode_FromStringAndSize(c_str, size) PyUnicode_Decode(c_str, size, __PYX_DEFAULT_STRING_ENCODING, NULL)
+#if __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT
+static char* __PYX_DEFAULT_STRING_ENCODING;
+static int __Pyx_init_sys_getdefaultencoding_params(void) {
+ PyObject* sys;
+ PyObject* default_encoding = NULL;
+ char* default_encoding_c;
+ sys = PyImport_ImportModule("sys");
+ if (!sys) goto bad;
+ default_encoding = PyObject_CallMethod(sys, (char*) (const char*) "getdefaultencoding", NULL);
+ Py_DECREF(sys);
+ if (!default_encoding) goto bad;
+ default_encoding_c = PyBytes_AsString(default_encoding);
+ if (!default_encoding_c) goto bad;
+ __PYX_DEFAULT_STRING_ENCODING = (char*) malloc(strlen(default_encoding_c));
+ if (!__PYX_DEFAULT_STRING_ENCODING) goto bad;
+ strcpy(__PYX_DEFAULT_STRING_ENCODING, default_encoding_c);
+ Py_DECREF(default_encoding);
+ return 0;
+bad:
+ Py_XDECREF(default_encoding);
+ return -1;
+}
+#endif
+#endif
+
+
+/* Test for GCC > 2.95 */
+#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && (__GNUC_MINOR__ > 95)))
+ #define likely(x) __builtin_expect(!!(x), 1)
+ #define unlikely(x) __builtin_expect(!!(x), 0)
+#else /* !__GNUC__ or GCC < 2.95 */
+ #define likely(x) (x)
+ #define unlikely(x) (x)
+#endif /* __GNUC__ */
+
+static PyObject *__pyx_m;
+static PyObject *__pyx_d;
+static PyObject *__pyx_b;
+static PyObject *__pyx_empty_tuple;
+static PyObject *__pyx_empty_bytes;
+static PyObject *__pyx_empty_unicode;
+static int __pyx_lineno;
+static int __pyx_clineno = 0;
+static const char * __pyx_cfilenm= __FILE__;
+static const char *__pyx_filename;
+
+
+static const char *__pyx_f[] = {
+ "skbio/metadata/_intersection.pyx",
+};
+
+/*--- Type declarations ---*/
+struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode;
+struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj;
+struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree;
+struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left;
+struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right;
+
+/* "skbio/metadata/_intersection.pyx":263
+ *
+ *
+ * cpdef left(self, position, int n=1, int max_dist=2500): # <<<<<<<<<<<<<<
+ * """
+ * find n features with a start > than `position`
+ */
+struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left {
+ int __pyx_n;
+ int n;
+ int max_dist;
+};
+
+/* "skbio/metadata/_intersection.pyx":278
+ * return r[:n]
+ *
+ * cpdef right(self, position, int n=1, int max_dist=2500): # <<<<<<<<<<<<<<
+ * """
+ * find n features with a end < than position
+ */
+struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right {
+ int __pyx_n;
+ int n;
+ int max_dist;
+};
+
+/* "skbio/metadata/_intersection.pyx":66
+ * cdef float nlog = -1.0 / log(0.5)
+ *
+ * cdef class IntervalNode: # <<<<<<<<<<<<<<
+ * """
+ * A single node of an `IntervalTree`.
+ */
+struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode {
+ PyObject_HEAD
+ struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *__pyx_vtab;
+ float priority;
+ PyObject *interval;
+ int start;
+ int end;
+ int minend;
+ int maxend;
+ int minstart;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *cleft;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *cright;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *croot;
+};
+
+
+/* "skbio/metadata/_intersection.pyx":305
+ * ## ---- Wrappers that retain the old interface -------------------------------
+ *
+ * cdef class IntervalObj: # <<<<<<<<<<<<<<
+ * """
+ * Basic feature, with required integer start and end properties.
+ */
+struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj {
+ PyObject_HEAD
+ int start;
+ int end;
+ PyObject *value;
+ PyObject *chrom;
+ PyObject *strand;
+};
+
+
+/* "skbio/metadata/_intersection.pyx":357
+ * return self == other or self > other
+ *
+ * cdef class IntervalTree: # <<<<<<<<<<<<<<
+ * """
+ * Data structure for performing window intersect queries on a set of
+ */
+struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree {
+ PyObject_HEAD
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *root;
+};
+
+
+
+/* "skbio/metadata/_intersection.pyx":66
+ * cdef float nlog = -1.0 / log(0.5)
+ *
+ * cdef class IntervalNode: # <<<<<<<<<<<<<<
+ * """
+ * A single node of an `IntervalTree`.
+ */
+
+struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *(*insert)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, int, PyObject *, int __pyx_skip_dispatch);
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *(*rotate_right)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *);
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *(*rotate_left)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *);
+ void (*set_ends)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *);
+ void (*_intersect)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, int, PyObject *);
+ void (*update)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, int, PyObject *, PyObject *, int __pyx_skip_dispatch);
+ void (*_seek_left)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, PyObject *, int, int);
+ void (*_seek_right)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, PyObject *, int, int);
+ PyObject *(*left)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, PyObject *, int __pyx_skip_dispatch, struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left *__pyx_optional_args);
+ PyObject *(*right)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, PyObject *, int __pyx_skip_dispatch, struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right *__pyx_optional_args);
+ void (*_traverse)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, PyObject *);
+};
+static struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *__pyx_vtabptr_5skbio_8metadata_13_intersection_IntervalNode;
+static CYTHON_INLINE void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_set_ends(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *);
+
+/* --- Runtime support code (head) --- */
+/* Refnanny.proto */
+#ifndef CYTHON_REFNANNY
+ #define CYTHON_REFNANNY 0
+#endif
+#if CYTHON_REFNANNY
+ typedef struct {
+ void (*INCREF)(void*, PyObject*, int);
+ void (*DECREF)(void*, PyObject*, int);
+ void (*GOTREF)(void*, PyObject*, int);
+ void (*GIVEREF)(void*, PyObject*, int);
+ void* (*SetupContext)(const char*, int, const char*);
+ void (*FinishContext)(void**);
+ } __Pyx_RefNannyAPIStruct;
+ static __Pyx_RefNannyAPIStruct *__Pyx_RefNanny = NULL;
+ static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname);
+ #define __Pyx_RefNannyDeclarations void *__pyx_refnanny = NULL;
+#ifdef WITH_THREAD
+ #define __Pyx_RefNannySetupContext(name, acquire_gil)\
+ if (acquire_gil) {\
+ PyGILState_STATE __pyx_gilstate_save = PyGILState_Ensure();\
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__);\
+ PyGILState_Release(__pyx_gilstate_save);\
+ } else {\
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__);\
+ }
+#else
+ #define __Pyx_RefNannySetupContext(name, acquire_gil)\
+ __pyx_refnanny = __Pyx_RefNanny->SetupContext((name), __LINE__, __FILE__)
+#endif
+ #define __Pyx_RefNannyFinishContext()\
+ __Pyx_RefNanny->FinishContext(&__pyx_refnanny)
+ #define __Pyx_INCREF(r) __Pyx_RefNanny->INCREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
+ #define __Pyx_DECREF(r) __Pyx_RefNanny->DECREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
+ #define __Pyx_GOTREF(r) __Pyx_RefNanny->GOTREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
+ #define __Pyx_GIVEREF(r) __Pyx_RefNanny->GIVEREF(__pyx_refnanny, (PyObject *)(r), __LINE__)
+ #define __Pyx_XINCREF(r) do { if((r) != NULL) {__Pyx_INCREF(r); }} while(0)
+ #define __Pyx_XDECREF(r) do { if((r) != NULL) {__Pyx_DECREF(r); }} while(0)
+ #define __Pyx_XGOTREF(r) do { if((r) != NULL) {__Pyx_GOTREF(r); }} while(0)
+ #define __Pyx_XGIVEREF(r) do { if((r) != NULL) {__Pyx_GIVEREF(r);}} while(0)
+#else
+ #define __Pyx_RefNannyDeclarations
+ #define __Pyx_RefNannySetupContext(name, acquire_gil)
+ #define __Pyx_RefNannyFinishContext()
+ #define __Pyx_INCREF(r) Py_INCREF(r)
+ #define __Pyx_DECREF(r) Py_DECREF(r)
+ #define __Pyx_GOTREF(r)
+ #define __Pyx_GIVEREF(r)
+ #define __Pyx_XINCREF(r) Py_XINCREF(r)
+ #define __Pyx_XDECREF(r) Py_XDECREF(r)
+ #define __Pyx_XGOTREF(r)
+ #define __Pyx_XGIVEREF(r)
+#endif
+#define __Pyx_XDECREF_SET(r, v) do {\
+ PyObject *tmp = (PyObject *) r;\
+ r = v; __Pyx_XDECREF(tmp);\
+ } while (0)
+#define __Pyx_DECREF_SET(r, v) do {\
+ PyObject *tmp = (PyObject *) r;\
+ r = v; __Pyx_DECREF(tmp);\
+ } while (0)
+#define __Pyx_CLEAR(r) do { PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);} while(0)
+#define __Pyx_XCLEAR(r) do { if((r) != NULL) {PyObject* tmp = ((PyObject*)(r)); r = NULL; __Pyx_DECREF(tmp);}} while(0)
+
+/* RaiseArgTupleInvalid.proto */
+static void __Pyx_RaiseArgtupleInvalid(const char* func_name, int exact,
+ Py_ssize_t num_min, Py_ssize_t num_max, Py_ssize_t num_found);
+
+/* RaiseDoubleKeywords.proto */
+static void __Pyx_RaiseDoubleKeywordsError(const char* func_name, PyObject* kw_name);
+
+/* ParseKeywords.proto */
+static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject **argnames[],\
+ PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args,\
+ const char* function_name);
+
+/* PyObjectGetAttrStr.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_GetAttrStr(PyObject* obj, PyObject* attr_name) {
+ PyTypeObject* tp = Py_TYPE(obj);
+ if (likely(tp->tp_getattro))
+ return tp->tp_getattro(obj, attr_name);
+#if PY_MAJOR_VERSION < 3
+ if (likely(tp->tp_getattr))
+ return tp->tp_getattr(obj, PyString_AS_STRING(attr_name));
+#endif
+ return PyObject_GetAttr(obj, attr_name);
+}
+#else
+#define __Pyx_PyObject_GetAttrStr(o,n) PyObject_GetAttr(o,n)
+#endif
+
+/* PyObjectCall.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw);
+#else
+#define __Pyx_PyObject_Call(func, arg, kw) PyObject_Call(func, arg, kw)
+#endif
+
+/* ExtTypeTest.proto */
+static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type);
+
+/* ListAppend.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE int __Pyx_PyList_Append(PyObject* list, PyObject* x) {
+ PyListObject* L = (PyListObject*) list;
+ Py_ssize_t len = Py_SIZE(list);
+ if (likely(L->allocated > len) & likely(len > (L->allocated >> 1))) {
+ Py_INCREF(x);
+ PyList_SET_ITEM(list, len, x);
+ Py_SIZE(list) = len+1;
+ return 0;
+ }
+ return PyList_Append(list, x);
+}
+#else
+#define __Pyx_PyList_Append(L,x) PyList_Append(L,x)
+#endif
+
+/* PyThreadStateGet.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_PyThreadState_declare PyThreadState *__pyx_tstate;
+#define __Pyx_PyThreadState_assign __pyx_tstate = PyThreadState_GET();
+#else
+#define __Pyx_PyThreadState_declare
+#define __Pyx_PyThreadState_assign
+#endif
+
+/* PyErrFetchRestore.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+#define __Pyx_ErrRestoreWithState(type, value, tb) __Pyx_ErrRestoreInState(PyThreadState_GET(), type, value, tb)
+#define __Pyx_ErrFetchWithState(type, value, tb) __Pyx_ErrFetchInState(PyThreadState_GET(), type, value, tb)
+#define __Pyx_ErrRestore(type, value, tb) __Pyx_ErrRestoreInState(__pyx_tstate, type, value, tb)
+#define __Pyx_ErrFetch(type, value, tb) __Pyx_ErrFetchInState(__pyx_tstate, type, value, tb)
+static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb);
+static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb);
+#else
+#define __Pyx_ErrRestoreWithState(type, value, tb) PyErr_Restore(type, value, tb)
+#define __Pyx_ErrFetchWithState(type, value, tb) PyErr_Fetch(type, value, tb)
+#define __Pyx_ErrRestore(type, value, tb) PyErr_Restore(type, value, tb)
+#define __Pyx_ErrFetch(type, value, tb) PyErr_Fetch(type, value, tb)
+#endif
+
+/* WriteUnraisableException.proto */
+static void __Pyx_WriteUnraisable(const char *name, int clineno,
+ int lineno, const char *filename,
+ int full_traceback, int nogil);
+
+/* PyIntBinop.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+static PyObject* __Pyx_PyInt_SubtractObjC(PyObject *op1, PyObject *op2, long intval, int inplace);
+#else
+#define __Pyx_PyInt_SubtractObjC(op1, op2, intval, inplace)\
+ (inplace ? PyNumber_InPlaceSubtract(op1, op2) : PyNumber_Subtract(op1, op2))
+#endif
+
+/* GetBuiltinName.proto */
+static PyObject *__Pyx_GetBuiltinName(PyObject *name);
+
+/* GetModuleGlobalName.proto */
+static CYTHON_INLINE PyObject *__Pyx_GetModuleGlobalName(PyObject *name);
+
+/* SliceTupleAndList.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyList_GetSlice(PyObject* src, Py_ssize_t start, Py_ssize_t stop);
+static CYTHON_INLINE PyObject* __Pyx_PyTuple_GetSlice(PyObject* src, Py_ssize_t start, Py_ssize_t stop);
+#else
+#define __Pyx_PyList_GetSlice(seq, start, stop) PySequence_GetSlice(seq, start, stop)
+#define __Pyx_PyTuple_GetSlice(seq, start, stop) PySequence_GetSlice(seq, start, stop)
+#endif
+
+/* PyIntBinop.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+static PyObject* __Pyx_PyInt_AddObjC(PyObject *op1, PyObject *op2, long intval, int inplace);
+#else
+#define __Pyx_PyInt_AddObjC(op1, op2, intval, inplace)\
+ (inplace ? PyNumber_InPlaceAdd(op1, op2) : PyNumber_Add(op1, op2))
+#endif
+
+/* PyObjectCallMethO.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg);
+#endif
+
+/* PyObjectCallOneArg.proto */
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg);
+
+/* PyIntBinop.proto */
+#if CYTHON_COMPILING_IN_CPYTHON
+static PyObject* __Pyx_PyInt_EqObjC(PyObject *op1, PyObject *op2, long intval, int inplace);
+#else
+#define __Pyx_PyInt_EqObjC(op1, op2, intval, inplace)\
+ PyObject_RichCompare(op1, op2, Py_EQ)
+ #endif
+
+/* KeywordStringCheck.proto */
+static CYTHON_INLINE int __Pyx_CheckKeywordStrings(PyObject *kwdict, const char* function_name, int kw_allowed);
+
+/* IncludeStringH.proto */
+#include <string.h>
+
+/* BytesEquals.proto */
+static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals);
+
+/* UnicodeEquals.proto */
+static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals);
+
+/* StrEquals.proto */
+#if PY_MAJOR_VERSION >= 3
+#define __Pyx_PyString_Equals __Pyx_PyUnicode_Equals
+#else
+#define __Pyx_PyString_Equals __Pyx_PyBytes_Equals
+#endif
+
+/* SetVTable.proto */
+static int __Pyx_SetVtable(PyObject *dict, void *vtable);
+
+/* Import.proto */
+static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level);
+
+/* GetNameInClass.proto */
+static PyObject *__Pyx_GetNameInClass(PyObject *nmspace, PyObject *name);
+
+/* CodeObjectCache.proto */
+typedef struct {
+ PyCodeObject* code_object;
+ int code_line;
+} __Pyx_CodeObjectCacheEntry;
+struct __Pyx_CodeObjectCache {
+ int count;
+ int max_count;
+ __Pyx_CodeObjectCacheEntry* entries;
+};
+static struct __Pyx_CodeObjectCache __pyx_code_cache = {0,0,NULL};
+static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line);
+static PyCodeObject *__pyx_find_code_object(int code_line);
+static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object);
+
+/* AddTraceback.proto */
+static void __Pyx_AddTraceback(const char *funcname, int c_line,
+ int py_line, const char *filename);
+
+/* CIntToPy.proto */
+static CYTHON_INLINE PyObject* __Pyx_PyInt_From_int(int value);
+
+/* CIntFromPy.proto */
+static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *);
+
+/* CIntToPy.proto */
+static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value);
+
+/* CIntFromPy.proto */
+static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *);
+
+/* CheckBinaryVersion.proto */
+static int __Pyx_check_binary_version(void);
+
+/* InitStrings.proto */
+static int __Pyx_InitStrings(__Pyx_StringTabEntry *t);
+
+static struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_insert(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_interval, int __pyx_skip_dispatch); /* proto*/
+static struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_rotate_right(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto*/
+static struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_rotate_left(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto*/
+static CYTHON_INLINE void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_set_ends(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto*/
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__intersect(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_results); /* proto*/
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_update(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_old_feature, PyObject *__pyx_v_new_feature, int __pyx_skip_dispatch); /* proto*/
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__seek_left(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_position, PyObject *__pyx_v_results, int __pyx_v_n, int __pyx_v_max_dist); /* proto*/
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__seek_right(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_position, PyObject *__pyx_v_results, int __pyx_v_n, int __pyx_v_max_dist); /* proto*/
+static PyObject *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_left(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_position, int __pyx_skip_dispatch, struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left *__pyx_optional_args); /* proto*/
+static PyObject *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_right(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_position, int __pyx_skip_dispatch, struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right *__pyx_optional_args); /* proto*/
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__traverse(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_func); /* proto*/
+
+/* Module declarations from 'skbio.metadata._intersection' */
+static PyTypeObject *__pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode = 0;
+static PyTypeObject *__pyx_ptype_5skbio_8metadata_13_intersection_IntervalObj = 0;
+static PyTypeObject *__pyx_ptype_5skbio_8metadata_13_intersection_IntervalTree = 0;
+static float __pyx_v_5skbio_8metadata_13_intersection_nlog;
+static struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_5skbio_8metadata_13_intersection_EmptyNode = 0;
+static CYTHON_INLINE int __pyx_f_5skbio_8metadata_13_intersection_imax2(int, int); /*proto*/
+static CYTHON_INLINE int __pyx_f_5skbio_8metadata_13_intersection_imax3(int, int, int); /*proto*/
+static CYTHON_INLINE int __pyx_f_5skbio_8metadata_13_intersection_imin3(int, int, int); /*proto*/
+static CYTHON_INLINE int __pyx_f_5skbio_8metadata_13_intersection_imin2(int, int); /*proto*/
+#define __Pyx_MODULE_NAME "skbio.metadata._intersection"
+int __pyx_module_is_main_skbio__metadata___intersection = 0;
+
+/* Implementation of 'skbio.metadata._intersection' */
+static const char __pyx_k_n[] = "n";
+static const char __pyx_k__3[] = ")";
+static const char __pyx_k__4[] = "-";
+static const char __pyx_k_add[] = "add";
+static const char __pyx_k_end[] = "end";
+static const char __pyx_k_key[] = "key";
+static const char __pyx_k_find[] = "find";
+static const char __pyx_k_left[] = "left";
+static const char __pyx_k_main[] = "__main__";
+static const char __pyx_k_sort[] = "sort";
+static const char __pyx_k_test[] = "__test__";
+static const char __pyx_k_chrom[] = "chrom";
+static const char __pyx_k_right[] = "right";
+static const char __pyx_k_start[] = "start";
+static const char __pyx_k_value[] = "value";
+static const char __pyx_k_import[] = "__import__";
+static const char __pyx_k_insert[] = "insert";
+static const char __pyx_k_strand[] = "strand";
+static const char __pyx_k_update[] = "update";
+static const char __pyx_k_reverse[] = "reverse";
+static const char __pyx_k_value_2[] = ", value=";
+static const char __pyx_k_interval[] = "interval";
+static const char __pyx_k_max_dist[] = "max_dist";
+static const char __pyx_k_operator[] = "operator";
+static const char __pyx_k_position[] = "position";
+static const char __pyx_k_traverse[] = "traverse";
+static const char __pyx_k_intersect[] = "intersect";
+static const char __pyx_k_attrgetter[] = "attrgetter";
+static const char __pyx_k_pyx_vtable[] = "__pyx_vtable__";
+static const char __pyx_k_Intersecter[] = "Intersecter";
+static const char __pyx_k_new_feature[] = "new_feature";
+static const char __pyx_k_old_feature[] = "old_feature";
+static const char __pyx_k_add_interval[] = "add_interval";
+static const char __pyx_k_num_intervals[] = "num_intervals";
+static const char __pyx_k_IntervalObj_d_d[] = "IntervalObj(%d, %d";
+static const char __pyx_k_insert_interval[] = "insert_interval";
+static const char __pyx_k_IntervalNode_i_i[] = "IntervalNode(%i, %i)";
+static const char __pyx_k_start_must_be_less_than_end[] = "start must be less than end";
+static const char __pyx_k_Data_structure_for_performing_i[] = "\nData structure for performing intersect queries on a set of intervals which\npreserves all information about the intervals (unlike bitset projection methods).\n\n:Authors: James Taylor (james at jamestaylor.org),\n Ian Schenk (ian.schenck at gmail.com),\n Brent Pedersen (bpederse at gmail.com)\n";
+static PyObject *__pyx_n_s_Intersecter;
+static PyObject *__pyx_kp_s_IntervalNode_i_i;
+static PyObject *__pyx_kp_s_IntervalObj_d_d;
+static PyObject *__pyx_kp_s__3;
+static PyObject *__pyx_kp_s__4;
+static PyObject *__pyx_n_s_add;
+static PyObject *__pyx_n_s_add_interval;
+static PyObject *__pyx_n_s_attrgetter;
+static PyObject *__pyx_n_s_chrom;
+static PyObject *__pyx_n_s_end;
+static PyObject *__pyx_n_s_find;
+static PyObject *__pyx_n_s_import;
+static PyObject *__pyx_n_s_insert;
+static PyObject *__pyx_n_s_insert_interval;
+static PyObject *__pyx_n_s_intersect;
+static PyObject *__pyx_n_s_interval;
+static PyObject *__pyx_n_s_key;
+static PyObject *__pyx_n_s_left;
+static PyObject *__pyx_n_s_main;
+static PyObject *__pyx_n_s_max_dist;
+static PyObject *__pyx_n_s_n;
+static PyObject *__pyx_n_s_new_feature;
+static PyObject *__pyx_n_s_num_intervals;
+static PyObject *__pyx_n_s_old_feature;
+static PyObject *__pyx_n_s_operator;
+static PyObject *__pyx_n_s_position;
+static PyObject *__pyx_n_s_pyx_vtable;
+static PyObject *__pyx_n_s_reverse;
+static PyObject *__pyx_n_s_right;
+static PyObject *__pyx_n_s_sort;
+static PyObject *__pyx_n_s_start;
+static PyObject *__pyx_kp_s_start_must_be_less_than_end;
+static PyObject *__pyx_n_s_strand;
+static PyObject *__pyx_n_s_test;
+static PyObject *__pyx_n_s_traverse;
+static PyObject *__pyx_n_s_update;
+static PyObject *__pyx_n_s_value;
+static PyObject *__pyx_kp_s_value_2;
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_9left_node___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_10right_node___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_9root_node___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode___repr__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_2__cinit__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_interval); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_4insert(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_interval); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_6intersect(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, CYTHON_UNUSED PyObject *__pyx_v_sort); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8update(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_old_feature, PyObject *__pyx_v_new_feature); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_10left(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_position, int __pyx_v_n, int __pyx_v_max_dist); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_12right(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_position, int __pyx_v_n, int __pyx_v_max_dist); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_14traverse(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_func); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8interval___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8interval_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_value); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8interval_4__del__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_5start___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_5start_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_value); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_3end___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_3end_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_value); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj___init__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_value, PyObject *__pyx_v_chrom, PyObject *__pyx_v_strand); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_2__repr__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_4__richcmp__(PyObject *__pyx_v_self, PyObject *__pyx_v_other, PyObject *__pyx_v_op); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5start___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5start_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_3end___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_3end_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5value___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5value_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5value_4__del__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5chrom___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_4__del__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_6strand___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_6strand_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_6strand_4__del__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self); /* proto */
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree___cinit__(CYTHON_UNUSED struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_2insert(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_value); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_4update(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_start, PyObject *__pyx_v_end, PyObject *__pyx_v_old_feature, PyObject *__pyx_v_new_feature); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_6find(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_start, PyObject *__pyx_v_end); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_8before(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_position, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_10after(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_position, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_12insert_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_14before_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_16after_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_18upstream_of_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_20downstream_of_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist); /* proto */
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_22traverse(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_fn); /* proto */
+static PyObject *__pyx_tp_new_5skbio_8metadata_13_intersection_IntervalNode(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_5skbio_8metadata_13_intersection_IntervalObj(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_tp_new_5skbio_8metadata_13_intersection_IntervalTree(PyTypeObject *t, PyObject *a, PyObject *k); /*proto*/
+static PyObject *__pyx_int_0;
+static PyObject *__pyx_int_1;
+static PyObject *__pyx_int_2;
+static PyObject *__pyx_int_3;
+static PyObject *__pyx_int_4;
+static PyObject *__pyx_int_5;
+static PyObject *__pyx_int_2500;
+static PyObject *__pyx_int_neg_1;
+static PyObject *__pyx_tuple_;
+static PyObject *__pyx_tuple__2;
+static PyObject *__pyx_tuple__5;
+
+/* "skbio/metadata/_intersection.pyx":38
+ * int iabs(int)
+ *
+ * cdef inline int imax2(int a, int b): # <<<<<<<<<<<<<<
+ * if b > a: return b
+ * return a
+ */
+
+static CYTHON_INLINE int __pyx_f_5skbio_8metadata_13_intersection_imax2(int __pyx_v_a, int __pyx_v_b) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ __Pyx_RefNannySetupContext("imax2", 0);
+
+ /* "skbio/metadata/_intersection.pyx":39
+ *
+ * cdef inline int imax2(int a, int b):
+ * if b > a: return b # <<<<<<<<<<<<<<
+ * return a
+ *
+ */
+ __pyx_t_1 = ((__pyx_v_b > __pyx_v_a) != 0);
+ if (__pyx_t_1) {
+ __pyx_r = __pyx_v_b;
+ goto __pyx_L0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":40
+ * cdef inline int imax2(int a, int b):
+ * if b > a: return b
+ * return a # <<<<<<<<<<<<<<
+ *
+ * cdef inline int imax3(int a, int b, int c):
+ */
+ __pyx_r = __pyx_v_a;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":38
+ * int iabs(int)
+ *
+ * cdef inline int imax2(int a, int b): # <<<<<<<<<<<<<<
+ * if b > a: return b
+ * return a
+ */
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":42
+ * return a
+ *
+ * cdef inline int imax3(int a, int b, int c): # <<<<<<<<<<<<<<
+ * if b > a:
+ * if c > b:
+ */
+
+static CYTHON_INLINE int __pyx_f_5skbio_8metadata_13_intersection_imax3(int __pyx_v_a, int __pyx_v_b, int __pyx_v_c) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ __Pyx_RefNannySetupContext("imax3", 0);
+
+ /* "skbio/metadata/_intersection.pyx":43
+ *
+ * cdef inline int imax3(int a, int b, int c):
+ * if b > a: # <<<<<<<<<<<<<<
+ * if c > b:
+ * return c
+ */
+ __pyx_t_1 = ((__pyx_v_b > __pyx_v_a) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":44
+ * cdef inline int imax3(int a, int b, int c):
+ * if b > a:
+ * if c > b: # <<<<<<<<<<<<<<
+ * return c
+ * return b
+ */
+ __pyx_t_1 = ((__pyx_v_c > __pyx_v_b) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":45
+ * if b > a:
+ * if c > b:
+ * return c # <<<<<<<<<<<<<<
+ * return b
+ * if a > c:
+ */
+ __pyx_r = __pyx_v_c;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":44
+ * cdef inline int imax3(int a, int b, int c):
+ * if b > a:
+ * if c > b: # <<<<<<<<<<<<<<
+ * return c
+ * return b
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":46
+ * if c > b:
+ * return c
+ * return b # <<<<<<<<<<<<<<
+ * if a > c:
+ * return a
+ */
+ __pyx_r = __pyx_v_b;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":43
+ *
+ * cdef inline int imax3(int a, int b, int c):
+ * if b > a: # <<<<<<<<<<<<<<
+ * if c > b:
+ * return c
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":47
+ * return c
+ * return b
+ * if a > c: # <<<<<<<<<<<<<<
+ * return a
+ * return c
+ */
+ __pyx_t_1 = ((__pyx_v_a > __pyx_v_c) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":48
+ * return b
+ * if a > c:
+ * return a # <<<<<<<<<<<<<<
+ * return c
+ *
+ */
+ __pyx_r = __pyx_v_a;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":47
+ * return c
+ * return b
+ * if a > c: # <<<<<<<<<<<<<<
+ * return a
+ * return c
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":49
+ * if a > c:
+ * return a
+ * return c # <<<<<<<<<<<<<<
+ *
+ * cdef inline int imin3(int a, int b, int c):
+ */
+ __pyx_r = __pyx_v_c;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":42
+ * return a
+ *
+ * cdef inline int imax3(int a, int b, int c): # <<<<<<<<<<<<<<
+ * if b > a:
+ * if c > b:
+ */
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":51
+ * return c
+ *
+ * cdef inline int imin3(int a, int b, int c): # <<<<<<<<<<<<<<
+ * if b < a:
+ * if c < b:
+ */
+
+static CYTHON_INLINE int __pyx_f_5skbio_8metadata_13_intersection_imin3(int __pyx_v_a, int __pyx_v_b, int __pyx_v_c) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ __Pyx_RefNannySetupContext("imin3", 0);
+
+ /* "skbio/metadata/_intersection.pyx":52
+ *
+ * cdef inline int imin3(int a, int b, int c):
+ * if b < a: # <<<<<<<<<<<<<<
+ * if c < b:
+ * return c
+ */
+ __pyx_t_1 = ((__pyx_v_b < __pyx_v_a) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":53
+ * cdef inline int imin3(int a, int b, int c):
+ * if b < a:
+ * if c < b: # <<<<<<<<<<<<<<
+ * return c
+ * return b
+ */
+ __pyx_t_1 = ((__pyx_v_c < __pyx_v_b) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":54
+ * if b < a:
+ * if c < b:
+ * return c # <<<<<<<<<<<<<<
+ * return b
+ * if a < c:
+ */
+ __pyx_r = __pyx_v_c;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":53
+ * cdef inline int imin3(int a, int b, int c):
+ * if b < a:
+ * if c < b: # <<<<<<<<<<<<<<
+ * return c
+ * return b
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":55
+ * if c < b:
+ * return c
+ * return b # <<<<<<<<<<<<<<
+ * if a < c:
+ * return a
+ */
+ __pyx_r = __pyx_v_b;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":52
+ *
+ * cdef inline int imin3(int a, int b, int c):
+ * if b < a: # <<<<<<<<<<<<<<
+ * if c < b:
+ * return c
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":56
+ * return c
+ * return b
+ * if a < c: # <<<<<<<<<<<<<<
+ * return a
+ * return c
+ */
+ __pyx_t_1 = ((__pyx_v_a < __pyx_v_c) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":57
+ * return b
+ * if a < c:
+ * return a # <<<<<<<<<<<<<<
+ * return c
+ *
+ */
+ __pyx_r = __pyx_v_a;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":56
+ * return c
+ * return b
+ * if a < c: # <<<<<<<<<<<<<<
+ * return a
+ * return c
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":58
+ * if a < c:
+ * return a
+ * return c # <<<<<<<<<<<<<<
+ *
+ * cdef inline int imin2(int a, int b):
+ */
+ __pyx_r = __pyx_v_c;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":51
+ * return c
+ *
+ * cdef inline int imin3(int a, int b, int c): # <<<<<<<<<<<<<<
+ * if b < a:
+ * if c < b:
+ */
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":60
+ * return c
+ *
+ * cdef inline int imin2(int a, int b): # <<<<<<<<<<<<<<
+ * if b < a: return b
+ * return a
+ */
+
+static CYTHON_INLINE int __pyx_f_5skbio_8metadata_13_intersection_imin2(int __pyx_v_a, int __pyx_v_b) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ __Pyx_RefNannySetupContext("imin2", 0);
+
+ /* "skbio/metadata/_intersection.pyx":61
+ *
+ * cdef inline int imin2(int a, int b):
+ * if b < a: return b # <<<<<<<<<<<<<<
+ * return a
+ *
+ */
+ __pyx_t_1 = ((__pyx_v_b < __pyx_v_a) != 0);
+ if (__pyx_t_1) {
+ __pyx_r = __pyx_v_b;
+ goto __pyx_L0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":62
+ * cdef inline int imin2(int a, int b):
+ * if b < a: return b
+ * return a # <<<<<<<<<<<<<<
+ *
+ * cdef float nlog = -1.0 / log(0.5)
+ */
+ __pyx_r = __pyx_v_a;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":60
+ * return c
+ *
+ * cdef inline int imin2(int a, int b): # <<<<<<<<<<<<<<
+ * if b < a: return b
+ * return a
+ */
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":80
+ *
+ * property left_node:
+ * def __get__(self): # <<<<<<<<<<<<<<
+ * return self.cleft if self.cleft is not EmptyNode else None
+ * property right_node:
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9left_node_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9left_node_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_9left_node___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_9left_node___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ int __pyx_t_2;
+ __Pyx_RefNannySetupContext("__get__", 0);
+
+ /* "skbio/metadata/_intersection.pyx":81
+ * property left_node:
+ * def __get__(self):
+ * return self.cleft if self.cleft is not EmptyNode else None # <<<<<<<<<<<<<<
+ * property right_node:
+ * def __get__(self):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = (__pyx_v_self->cleft != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ if ((__pyx_t_2 != 0)) {
+ __Pyx_INCREF(((PyObject *)__pyx_v_self->cleft));
+ __pyx_t_1 = ((PyObject *)__pyx_v_self->cleft);
+ } else {
+ __Pyx_INCREF(Py_None);
+ __pyx_t_1 = Py_None;
+ }
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":80
+ *
+ * property left_node:
+ * def __get__(self): # <<<<<<<<<<<<<<
+ * return self.cleft if self.cleft is not EmptyNode else None
+ * property right_node:
+ */
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":83
+ * return self.cleft if self.cleft is not EmptyNode else None
+ * property right_node:
+ * def __get__(self): # <<<<<<<<<<<<<<
+ * return self.cright if self.cright is not EmptyNode else None
+ * property root_node:
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_10right_node_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_10right_node_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_10right_node___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_10right_node___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ int __pyx_t_2;
+ __Pyx_RefNannySetupContext("__get__", 0);
+
+ /* "skbio/metadata/_intersection.pyx":84
+ * property right_node:
+ * def __get__(self):
+ * return self.cright if self.cright is not EmptyNode else None # <<<<<<<<<<<<<<
+ * property root_node:
+ * def __get__(self):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = (__pyx_v_self->cright != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ if ((__pyx_t_2 != 0)) {
+ __Pyx_INCREF(((PyObject *)__pyx_v_self->cright));
+ __pyx_t_1 = ((PyObject *)__pyx_v_self->cright);
+ } else {
+ __Pyx_INCREF(Py_None);
+ __pyx_t_1 = Py_None;
+ }
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":83
+ * return self.cleft if self.cleft is not EmptyNode else None
+ * property right_node:
+ * def __get__(self): # <<<<<<<<<<<<<<
+ * return self.cright if self.cright is not EmptyNode else None
+ * property root_node:
+ */
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":86
+ * return self.cright if self.cright is not EmptyNode else None
+ * property root_node:
+ * def __get__(self): # <<<<<<<<<<<<<<
+ * return self.croot if self.croot is not EmptyNode else None
+ *
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9root_node_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9root_node_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_9root_node___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_9root_node___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ int __pyx_t_2;
+ __Pyx_RefNannySetupContext("__get__", 0);
+
+ /* "skbio/metadata/_intersection.pyx":87
+ * property root_node:
+ * def __get__(self):
+ * return self.croot if self.croot is not EmptyNode else None # <<<<<<<<<<<<<<
+ *
+ * def __repr__(self):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2 = (__pyx_v_self->croot != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ if ((__pyx_t_2 != 0)) {
+ __Pyx_INCREF(((PyObject *)__pyx_v_self->croot));
+ __pyx_t_1 = ((PyObject *)__pyx_v_self->croot);
+ } else {
+ __Pyx_INCREF(Py_None);
+ __pyx_t_1 = Py_None;
+ }
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":86
+ * return self.cright if self.cright is not EmptyNode else None
+ * property root_node:
+ * def __get__(self): # <<<<<<<<<<<<<<
+ * return self.croot if self.croot is not EmptyNode else None
+ *
+ */
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":89
+ * return self.croot if self.croot is not EmptyNode else None
+ *
+ * def __repr__(self): # <<<<<<<<<<<<<<
+ * return "IntervalNode(%i, %i)" % (self.start, self.end)
+ *
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_1__repr__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_1__repr__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__repr__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode___repr__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode___repr__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ __Pyx_RefNannySetupContext("__repr__", 0);
+
+ /* "skbio/metadata/_intersection.pyx":90
+ *
+ * def __repr__(self):
+ * return "IntervalNode(%i, %i)" % (self.start, self.end) # <<<<<<<<<<<<<<
+ *
+ * def __cinit__(IntervalNode self, int start, int end, object interval):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_self->start); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 90, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyInt_From_int(__pyx_v_self->end); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 90, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 90, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_1);
+ PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_2);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyString_Format(__pyx_kp_s_IntervalNode_i_i, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 90, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":89
+ * return self.croot if self.croot is not EmptyNode else None
+ *
+ * def __repr__(self): # <<<<<<<<<<<<<<
+ * return "IntervalNode(%i, %i)" % (self.start, self.end)
+ *
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.__repr__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":92
+ * return "IntervalNode(%i, %i)" % (self.start, self.end)
+ *
+ * def __cinit__(IntervalNode self, int start, int end, object interval): # <<<<<<<<<<<<<<
+ * # Python lacks the binomial distribution, so we convert a
+ * # uniform into a binomial because it naturally scales with
+ */
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_3__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_3__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ int __pyx_v_start;
+ int __pyx_v_end;
+ PyObject *__pyx_v_interval = 0;
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__cinit__ (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_start,&__pyx_n_s_end,&__pyx_n_s_interval,0};
+ PyObject* values[3] = {0,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 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_start)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_end)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("__cinit__", 1, 3, 3, 1); __PYX_ERR(0, 92, __pyx_L3_error)
+ }
+ case 2:
+ if (likely((values[2] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_interval)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("__cinit__", 1, 3, 3, 2); __PYX_ERR(0, 92, __pyx_L3_error)
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "__cinit__") < 0)) __PYX_ERR(0, 92, __pyx_L3_error)
+ }
+ } else if (PyTuple_GET_SIZE(__pyx_args) != 3) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ }
+ __pyx_v_start = __Pyx_PyInt_As_int(values[0]); if (unlikely((__pyx_v_start == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 92, __pyx_L3_error)
+ __pyx_v_end = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_end == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 92, __pyx_L3_error)
+ __pyx_v_interval = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("__cinit__", 1, 3, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 92, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.__cinit__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return -1;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_2__cinit__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), __pyx_v_start, __pyx_v_end, __pyx_v_interval);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_2__cinit__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_interval) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ double __pyx_t_1;
+ double __pyx_t_2;
+ __Pyx_RefNannySetupContext("__cinit__", 0);
+
+ /* "skbio/metadata/_intersection.pyx":97
+ * # tree size. Also, python's uniform is perfect since the
+ * # upper limit is not inclusive, which gives us undefined here.
+ * self.priority = ceil(nlog * log(-1.0/(1.0 * rand()/RAND_MAX - 1))) # <<<<<<<<<<<<<<
+ * self.start = start
+ * self.end = end
+ */
+ __pyx_t_1 = (1.0 * rand());
+ if (unlikely(RAND_MAX == 0)) {
+ PyErr_SetString(PyExc_ZeroDivisionError, "float division");
+ __PYX_ERR(0, 97, __pyx_L1_error)
+ }
+ __pyx_t_2 = ((__pyx_t_1 / RAND_MAX) - 1.0);
+ if (unlikely(__pyx_t_2 == 0)) {
+ PyErr_SetString(PyExc_ZeroDivisionError, "float division");
+ __PYX_ERR(0, 97, __pyx_L1_error)
+ }
+ __pyx_v_self->priority = ceil((__pyx_v_5skbio_8metadata_13_intersection_nlog * log((-1.0 / __pyx_t_2))));
+
+ /* "skbio/metadata/_intersection.pyx":98
+ * # upper limit is not inclusive, which gives us undefined here.
+ * self.priority = ceil(nlog * log(-1.0/(1.0 * rand()/RAND_MAX - 1)))
+ * self.start = start # <<<<<<<<<<<<<<
+ * self.end = end
+ * self.interval = interval
+ */
+ __pyx_v_self->start = __pyx_v_start;
+
+ /* "skbio/metadata/_intersection.pyx":99
+ * self.priority = ceil(nlog * log(-1.0/(1.0 * rand()/RAND_MAX - 1)))
+ * self.start = start
+ * self.end = end # <<<<<<<<<<<<<<
+ * self.interval = interval
+ * self.maxend = end
+ */
+ __pyx_v_self->end = __pyx_v_end;
+
+ /* "skbio/metadata/_intersection.pyx":100
+ * self.start = start
+ * self.end = end
+ * self.interval = interval # <<<<<<<<<<<<<<
+ * self.maxend = end
+ * self.minstart = start
+ */
+ __Pyx_INCREF(__pyx_v_interval);
+ __Pyx_GIVEREF(__pyx_v_interval);
+ __Pyx_GOTREF(__pyx_v_self->interval);
+ __Pyx_DECREF(__pyx_v_self->interval);
+ __pyx_v_self->interval = __pyx_v_interval;
+
+ /* "skbio/metadata/_intersection.pyx":101
+ * self.end = end
+ * self.interval = interval
+ * self.maxend = end # <<<<<<<<<<<<<<
+ * self.minstart = start
+ * self.minend = end
+ */
+ __pyx_v_self->maxend = __pyx_v_end;
+
+ /* "skbio/metadata/_intersection.pyx":102
+ * self.interval = interval
+ * self.maxend = end
+ * self.minstart = start # <<<<<<<<<<<<<<
+ * self.minend = end
+ * self.cleft = EmptyNode
+ */
+ __pyx_v_self->minstart = __pyx_v_start;
+
+ /* "skbio/metadata/_intersection.pyx":103
+ * self.maxend = end
+ * self.minstart = start
+ * self.minend = end # <<<<<<<<<<<<<<
+ * self.cleft = EmptyNode
+ * self.cright = EmptyNode
+ */
+ __pyx_v_self->minend = __pyx_v_end;
+
+ /* "skbio/metadata/_intersection.pyx":104
+ * self.minstart = start
+ * self.minend = end
+ * self.cleft = EmptyNode # <<<<<<<<<<<<<<
+ * self.cright = EmptyNode
+ * self.croot = EmptyNode
+ */
+ __Pyx_INCREF(((PyObject *)__pyx_v_5skbio_8metadata_13_intersection_EmptyNode));
+ __Pyx_GIVEREF(((PyObject *)__pyx_v_5skbio_8metadata_13_intersection_EmptyNode));
+ __Pyx_GOTREF(__pyx_v_self->cleft);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cleft));
+ __pyx_v_self->cleft = __pyx_v_5skbio_8metadata_13_intersection_EmptyNode;
+
+ /* "skbio/metadata/_intersection.pyx":105
+ * self.minend = end
+ * self.cleft = EmptyNode
+ * self.cright = EmptyNode # <<<<<<<<<<<<<<
+ * self.croot = EmptyNode
+ *
+ */
+ __Pyx_INCREF(((PyObject *)__pyx_v_5skbio_8metadata_13_intersection_EmptyNode));
+ __Pyx_GIVEREF(((PyObject *)__pyx_v_5skbio_8metadata_13_intersection_EmptyNode));
+ __Pyx_GOTREF(__pyx_v_self->cright);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cright));
+ __pyx_v_self->cright = __pyx_v_5skbio_8metadata_13_intersection_EmptyNode;
+
+ /* "skbio/metadata/_intersection.pyx":106
+ * self.cleft = EmptyNode
+ * self.cright = EmptyNode
+ * self.croot = EmptyNode # <<<<<<<<<<<<<<
+ *
+ * cpdef IntervalNode insert(IntervalNode self, int start, int end, object interval):
+ */
+ __Pyx_INCREF(((PyObject *)__pyx_v_5skbio_8metadata_13_intersection_EmptyNode));
+ __Pyx_GIVEREF(((PyObject *)__pyx_v_5skbio_8metadata_13_intersection_EmptyNode));
+ __Pyx_GOTREF(__pyx_v_self->croot);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->croot));
+ __pyx_v_self->croot = __pyx_v_5skbio_8metadata_13_intersection_EmptyNode;
+
+ /* "skbio/metadata/_intersection.pyx":92
+ * return "IntervalNode(%i, %i)" % (self.start, self.end)
+ *
+ * def __cinit__(IntervalNode self, int start, int end, object interval): # <<<<<<<<<<<<<<
+ * # Python lacks the binomial distribution, so we convert a
+ * # uniform into a binomial because it naturally scales with
+ */
+
+ /* function exit code */
+ __pyx_r = 0;
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.__cinit__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = -1;
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":108
+ * self.croot = EmptyNode
+ *
+ * cpdef IntervalNode insert(IntervalNode self, int start, int end, object interval): # <<<<<<<<<<<<<<
+ * """
+ * Insert a new IntervalNode into the tree of which this node is
+ */
+
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5insert(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_insert(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_interval, int __pyx_skip_dispatch) {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_croot = 0;
+ int __pyx_v_decision_endpoint;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ Py_ssize_t __pyx_t_7;
+ PyObject *__pyx_t_8 = NULL;
+ int __pyx_t_9;
+ int __pyx_t_10;
+ __Pyx_RefNannySetupContext("insert", 0);
+ /* Check if called by wrapper */
+ if (unlikely(__pyx_skip_dispatch)) ;
+ /* Check if overridden in Python */
+ else if (unlikely(Py_TYPE(((PyObject *)__pyx_v_self))->tp_dictoffset != 0)) {
+ __pyx_t_1 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s_insert); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 108, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (!PyCFunction_Check(__pyx_t_1) || (PyCFunction_GET_FUNCTION(__pyx_t_1) != (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5insert)) {
+ __Pyx_XDECREF(((PyObject *)__pyx_r));
+ __pyx_t_3 = __Pyx_PyInt_From_int(__pyx_v_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 108, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyInt_From_int(__pyx_v_end); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 108, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_5 = __pyx_t_1; __pyx_t_6 = NULL;
+ __pyx_t_7 = 0;
+ if (CYTHON_COMPILING_IN_CPYTHON && 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_7 = 1;
+ }
+ }
+ __pyx_t_8 = PyTuple_New(3+__pyx_t_7); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 108, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ if (__pyx_t_6) {
+ __Pyx_GIVEREF(__pyx_t_6); PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_6); __pyx_t_6 = NULL;
+ }
+ __Pyx_GIVEREF(__pyx_t_3);
+ PyTuple_SET_ITEM(__pyx_t_8, 0+__pyx_t_7, __pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_4);
+ PyTuple_SET_ITEM(__pyx_t_8, 1+__pyx_t_7, __pyx_t_4);
+ __Pyx_INCREF(__pyx_v_interval);
+ __Pyx_GIVEREF(__pyx_v_interval);
+ PyTuple_SET_ITEM(__pyx_t_8, 2+__pyx_t_7, __pyx_v_interval);
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_5, __pyx_t_8, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 108, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (!(likely(((__pyx_t_2) == Py_None) || likely(__Pyx_TypeTest(__pyx_t_2, __pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode))))) __PYX_ERR(0, 108, __pyx_L1_error)
+ __pyx_r = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_2);
+ __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ goto __pyx_L0;
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":114
+ * may or may not be this node!)
+ * """
+ * cdef IntervalNode croot = self # <<<<<<<<<<<<<<
+ * # If starts are the same, decide which to add interval to based on
+ * # end, thus maintaining sortedness relative to start/end
+ */
+ __Pyx_INCREF(((PyObject *)__pyx_v_self));
+ __pyx_v_croot = __pyx_v_self;
+
+ /* "skbio/metadata/_intersection.pyx":117
+ * # If starts are the same, decide which to add interval to based on
+ * # end, thus maintaining sortedness relative to start/end
+ * cdef int decision_endpoint = start # <<<<<<<<<<<<<<
+ * if start == self.start:
+ * decision_endpoint = end
+ */
+ __pyx_v_decision_endpoint = __pyx_v_start;
+
+ /* "skbio/metadata/_intersection.pyx":118
+ * # end, thus maintaining sortedness relative to start/end
+ * cdef int decision_endpoint = start
+ * if start == self.start: # <<<<<<<<<<<<<<
+ * decision_endpoint = end
+ *
+ */
+ __pyx_t_9 = ((__pyx_v_start == __pyx_v_self->start) != 0);
+ if (__pyx_t_9) {
+
+ /* "skbio/metadata/_intersection.pyx":119
+ * cdef int decision_endpoint = start
+ * if start == self.start:
+ * decision_endpoint = end # <<<<<<<<<<<<<<
+ *
+ * if decision_endpoint > self.start:
+ */
+ __pyx_v_decision_endpoint = __pyx_v_end;
+
+ /* "skbio/metadata/_intersection.pyx":118
+ * # end, thus maintaining sortedness relative to start/end
+ * cdef int decision_endpoint = start
+ * if start == self.start: # <<<<<<<<<<<<<<
+ * decision_endpoint = end
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":121
+ * decision_endpoint = end
+ *
+ * if decision_endpoint > self.start: # <<<<<<<<<<<<<<
+ * # insert to cright tree
+ * if self.cright is not EmptyNode:
+ */
+ __pyx_t_9 = ((__pyx_v_decision_endpoint > __pyx_v_self->start) != 0);
+ if (__pyx_t_9) {
+
+ /* "skbio/metadata/_intersection.pyx":123
+ * if decision_endpoint > self.start:
+ * # insert to cright tree
+ * if self.cright is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cright = self.cright.insert( start, end, interval )
+ * else:
+ */
+ __pyx_t_9 = (__pyx_v_self->cright != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_10 = (__pyx_t_9 != 0);
+ if (__pyx_t_10) {
+
+ /* "skbio/metadata/_intersection.pyx":124
+ * # insert to cright tree
+ * if self.cright is not EmptyNode:
+ * self.cright = self.cright.insert( start, end, interval ) # <<<<<<<<<<<<<<
+ * else:
+ * self.cright = IntervalNode( start, end, interval )
+ */
+ __pyx_t_1 = ((PyObject *)((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cright->__pyx_vtab)->insert(__pyx_v_self->cright, __pyx_v_start, __pyx_v_end, __pyx_v_interval, 0)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 124, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_1);
+ __Pyx_GOTREF(__pyx_v_self->cright);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cright));
+ __pyx_v_self->cright = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":123
+ * if decision_endpoint > self.start:
+ * # insert to cright tree
+ * if self.cright is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cright = self.cright.insert( start, end, interval )
+ * else:
+ */
+ goto __pyx_L5;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":126
+ * self.cright = self.cright.insert( start, end, interval )
+ * else:
+ * self.cright = IntervalNode( start, end, interval ) # <<<<<<<<<<<<<<
+ * # rebalance tree
+ * if self.priority < self.cright.priority:
+ */
+ /*else*/ {
+ __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_start); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyInt_From_int(__pyx_v_end); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_5 = PyTuple_New(3); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_GIVEREF(__pyx_t_1);
+ PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_2);
+ __Pyx_INCREF(__pyx_v_interval);
+ __Pyx_GIVEREF(__pyx_v_interval);
+ PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_v_interval);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyObject_Call(((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode), __pyx_t_5, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 126, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_GIVEREF(__pyx_t_2);
+ __Pyx_GOTREF(__pyx_v_self->cright);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cright));
+ __pyx_v_self->cright = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_2);
+ __pyx_t_2 = 0;
+ }
+ __pyx_L5:;
+
+ /* "skbio/metadata/_intersection.pyx":128
+ * self.cright = IntervalNode( start, end, interval )
+ * # rebalance tree
+ * if self.priority < self.cright.priority: # <<<<<<<<<<<<<<
+ * croot = self.rotate_left()
+ * else:
+ */
+ __pyx_t_10 = ((__pyx_v_self->priority < __pyx_v_self->cright->priority) != 0);
+ if (__pyx_t_10) {
+
+ /* "skbio/metadata/_intersection.pyx":129
+ * # rebalance tree
+ * if self.priority < self.cright.priority:
+ * croot = self.rotate_left() # <<<<<<<<<<<<<<
+ * else:
+ * # insert to cleft tree
+ */
+ __pyx_t_2 = ((PyObject *)((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->__pyx_vtab)->rotate_left(__pyx_v_self)); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 129, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF_SET(__pyx_v_croot, ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_2));
+ __pyx_t_2 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":128
+ * self.cright = IntervalNode( start, end, interval )
+ * # rebalance tree
+ * if self.priority < self.cright.priority: # <<<<<<<<<<<<<<
+ * croot = self.rotate_left()
+ * else:
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":121
+ * decision_endpoint = end
+ *
+ * if decision_endpoint > self.start: # <<<<<<<<<<<<<<
+ * # insert to cright tree
+ * if self.cright is not EmptyNode:
+ */
+ goto __pyx_L4;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":132
+ * else:
+ * # insert to cleft tree
+ * if self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cleft = self.cleft.insert( start, end, interval)
+ * else:
+ */
+ /*else*/ {
+ __pyx_t_10 = (__pyx_v_self->cleft != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_9 = (__pyx_t_10 != 0);
+ if (__pyx_t_9) {
+
+ /* "skbio/metadata/_intersection.pyx":133
+ * # insert to cleft tree
+ * if self.cleft is not EmptyNode:
+ * self.cleft = self.cleft.insert( start, end, interval) # <<<<<<<<<<<<<<
+ * else:
+ * self.cleft = IntervalNode( start, end, interval)
+ */
+ __pyx_t_2 = ((PyObject *)((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cleft->__pyx_vtab)->insert(__pyx_v_self->cleft, __pyx_v_start, __pyx_v_end, __pyx_v_interval, 0)); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 133, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_2);
+ __Pyx_GOTREF(__pyx_v_self->cleft);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cleft));
+ __pyx_v_self->cleft = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_2);
+ __pyx_t_2 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":132
+ * else:
+ * # insert to cleft tree
+ * if self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cleft = self.cleft.insert( start, end, interval)
+ * else:
+ */
+ goto __pyx_L7;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":135
+ * self.cleft = self.cleft.insert( start, end, interval)
+ * else:
+ * self.cleft = IntervalNode( start, end, interval) # <<<<<<<<<<<<<<
+ * # rebalance tree
+ * if self.priority < self.cleft.priority:
+ */
+ /*else*/ {
+ __pyx_t_2 = __Pyx_PyInt_From_int(__pyx_v_start); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 135, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_PyInt_From_int(__pyx_v_end); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 135, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_1 = PyTuple_New(3); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 135, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ PyTuple_SET_ITEM(__pyx_t_1, 0, __pyx_t_2);
+ __Pyx_GIVEREF(__pyx_t_5);
+ PyTuple_SET_ITEM(__pyx_t_1, 1, __pyx_t_5);
+ __Pyx_INCREF(__pyx_v_interval);
+ __Pyx_GIVEREF(__pyx_v_interval);
+ PyTuple_SET_ITEM(__pyx_t_1, 2, __pyx_v_interval);
+ __pyx_t_2 = 0;
+ __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_PyObject_Call(((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode), __pyx_t_1, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 135, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_GIVEREF(__pyx_t_5);
+ __Pyx_GOTREF(__pyx_v_self->cleft);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cleft));
+ __pyx_v_self->cleft = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_5);
+ __pyx_t_5 = 0;
+ }
+ __pyx_L7:;
+
+ /* "skbio/metadata/_intersection.pyx":137
+ * self.cleft = IntervalNode( start, end, interval)
+ * # rebalance tree
+ * if self.priority < self.cleft.priority: # <<<<<<<<<<<<<<
+ * croot = self.rotate_right()
+ *
+ */
+ __pyx_t_9 = ((__pyx_v_self->priority < __pyx_v_self->cleft->priority) != 0);
+ if (__pyx_t_9) {
+
+ /* "skbio/metadata/_intersection.pyx":138
+ * # rebalance tree
+ * if self.priority < self.cleft.priority:
+ * croot = self.rotate_right() # <<<<<<<<<<<<<<
+ *
+ * croot.set_ends()
+ */
+ __pyx_t_5 = ((PyObject *)((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->__pyx_vtab)->rotate_right(__pyx_v_self)); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 138, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF_SET(__pyx_v_croot, ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_5));
+ __pyx_t_5 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":137
+ * self.cleft = IntervalNode( start, end, interval)
+ * # rebalance tree
+ * if self.priority < self.cleft.priority: # <<<<<<<<<<<<<<
+ * croot = self.rotate_right()
+ *
+ */
+ }
+ }
+ __pyx_L4:;
+
+ /* "skbio/metadata/_intersection.pyx":140
+ * croot = self.rotate_right()
+ *
+ * croot.set_ends() # <<<<<<<<<<<<<<
+ * self.cleft.croot = croot
+ * self.cright.croot = croot
+ */
+ __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_set_ends(__pyx_v_croot);
+
+ /* "skbio/metadata/_intersection.pyx":141
+ *
+ * croot.set_ends()
+ * self.cleft.croot = croot # <<<<<<<<<<<<<<
+ * self.cright.croot = croot
+ * return croot
+ */
+ __Pyx_INCREF(((PyObject *)__pyx_v_croot));
+ __Pyx_GIVEREF(((PyObject *)__pyx_v_croot));
+ __Pyx_GOTREF(__pyx_v_self->cleft->croot);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cleft->croot));
+ __pyx_v_self->cleft->croot = __pyx_v_croot;
+
+ /* "skbio/metadata/_intersection.pyx":142
+ * croot.set_ends()
+ * self.cleft.croot = croot
+ * self.cright.croot = croot # <<<<<<<<<<<<<<
+ * return croot
+ *
+ */
+ __Pyx_INCREF(((PyObject *)__pyx_v_croot));
+ __Pyx_GIVEREF(((PyObject *)__pyx_v_croot));
+ __Pyx_GOTREF(__pyx_v_self->cright->croot);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cright->croot));
+ __pyx_v_self->cright->croot = __pyx_v_croot;
+
+ /* "skbio/metadata/_intersection.pyx":143
+ * self.cleft.croot = croot
+ * self.cright.croot = croot
+ * return croot # <<<<<<<<<<<<<<
+ *
+ * cdef IntervalNode rotate_right(IntervalNode self):
+ */
+ __Pyx_XDECREF(((PyObject *)__pyx_r));
+ __Pyx_INCREF(((PyObject *)__pyx_v_croot));
+ __pyx_r = __pyx_v_croot;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":108
+ * self.croot = EmptyNode
+ *
+ * cpdef IntervalNode insert(IntervalNode self, int start, int end, object interval): # <<<<<<<<<<<<<<
+ * """
+ * Insert a new IntervalNode into the tree of which this node is
+ */
+
+ /* 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_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.insert", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = 0;
+ __pyx_L0:;
+ __Pyx_XDECREF((PyObject *)__pyx_v_croot);
+ __Pyx_XGIVEREF((PyObject *)__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5insert(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_4insert[] = "\n Insert a new IntervalNode into the tree of which this node is\n currently the root. The return value is the new root of the tree (which\n may or may not be this node!)\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5insert(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ int __pyx_v_start;
+ int __pyx_v_end;
+ PyObject *__pyx_v_interval = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("insert (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_start,&__pyx_n_s_end,&__pyx_n_s_interval,0};
+ PyObject* values[3] = {0,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 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_start)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_end)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("insert", 1, 3, 3, 1); __PYX_ERR(0, 108, __pyx_L3_error)
+ }
+ case 2:
+ if (likely((values[2] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_interval)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("insert", 1, 3, 3, 2); __PYX_ERR(0, 108, __pyx_L3_error)
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "insert") < 0)) __PYX_ERR(0, 108, __pyx_L3_error)
+ }
+ } else if (PyTuple_GET_SIZE(__pyx_args) != 3) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ }
+ __pyx_v_start = __Pyx_PyInt_As_int(values[0]); if (unlikely((__pyx_v_start == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 108, __pyx_L3_error)
+ __pyx_v_end = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_end == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 108, __pyx_L3_error)
+ __pyx_v_interval = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("insert", 1, 3, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 108, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.insert", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_4insert(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), __pyx_v_start, __pyx_v_end, __pyx_v_interval);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_4insert(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_interval) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __Pyx_RefNannySetupContext("insert", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = ((PyObject *)__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_insert(__pyx_v_self, __pyx_v_start, __pyx_v_end, __pyx_v_interval, 1)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 108, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.insert", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":145
+ * return croot
+ *
+ * cdef IntervalNode rotate_right(IntervalNode self): # <<<<<<<<<<<<<<
+ * cdef IntervalNode croot = self.cleft
+ * self.cleft = self.cleft.cright
+ */
+
+static struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_rotate_right(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_croot = 0;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __Pyx_RefNannySetupContext("rotate_right", 0);
+
+ /* "skbio/metadata/_intersection.pyx":146
+ *
+ * cdef IntervalNode rotate_right(IntervalNode self):
+ * cdef IntervalNode croot = self.cleft # <<<<<<<<<<<<<<
+ * self.cleft = self.cleft.cright
+ * croot.cright = self
+ */
+ __pyx_t_1 = ((PyObject *)__pyx_v_self->cleft);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_v_croot = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":147
+ * cdef IntervalNode rotate_right(IntervalNode self):
+ * cdef IntervalNode croot = self.cleft
+ * self.cleft = self.cleft.cright # <<<<<<<<<<<<<<
+ * croot.cright = self
+ * self.set_ends()
+ */
+ __pyx_t_1 = ((PyObject *)__pyx_v_self->cleft->cright);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_1);
+ __Pyx_GOTREF(__pyx_v_self->cleft);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cleft));
+ __pyx_v_self->cleft = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":148
+ * cdef IntervalNode croot = self.cleft
+ * self.cleft = self.cleft.cright
+ * croot.cright = self # <<<<<<<<<<<<<<
+ * self.set_ends()
+ * return croot
+ */
+ __Pyx_INCREF(((PyObject *)__pyx_v_self));
+ __Pyx_GIVEREF(((PyObject *)__pyx_v_self));
+ __Pyx_GOTREF(__pyx_v_croot->cright);
+ __Pyx_DECREF(((PyObject *)__pyx_v_croot->cright));
+ __pyx_v_croot->cright = __pyx_v_self;
+
+ /* "skbio/metadata/_intersection.pyx":149
+ * self.cleft = self.cleft.cright
+ * croot.cright = self
+ * self.set_ends() # <<<<<<<<<<<<<<
+ * return croot
+ *
+ */
+ __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_set_ends(__pyx_v_self);
+
+ /* "skbio/metadata/_intersection.pyx":150
+ * croot.cright = self
+ * self.set_ends()
+ * return croot # <<<<<<<<<<<<<<
+ *
+ * cdef IntervalNode rotate_left(IntervalNode self):
+ */
+ __Pyx_XDECREF(((PyObject *)__pyx_r));
+ __Pyx_INCREF(((PyObject *)__pyx_v_croot));
+ __pyx_r = __pyx_v_croot;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":145
+ * return croot
+ *
+ * cdef IntervalNode rotate_right(IntervalNode self): # <<<<<<<<<<<<<<
+ * cdef IntervalNode croot = self.cleft
+ * self.cleft = self.cleft.cright
+ */
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_XDECREF((PyObject *)__pyx_v_croot);
+ __Pyx_XGIVEREF((PyObject *)__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":152
+ * return croot
+ *
+ * cdef IntervalNode rotate_left(IntervalNode self): # <<<<<<<<<<<<<<
+ * cdef IntervalNode croot = self.cright
+ * self.cright = self.cright.cleft
+ */
+
+static struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_rotate_left(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_croot = 0;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __Pyx_RefNannySetupContext("rotate_left", 0);
+
+ /* "skbio/metadata/_intersection.pyx":153
+ *
+ * cdef IntervalNode rotate_left(IntervalNode self):
+ * cdef IntervalNode croot = self.cright # <<<<<<<<<<<<<<
+ * self.cright = self.cright.cleft
+ * croot.cleft = self
+ */
+ __pyx_t_1 = ((PyObject *)__pyx_v_self->cright);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_v_croot = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":154
+ * cdef IntervalNode rotate_left(IntervalNode self):
+ * cdef IntervalNode croot = self.cright
+ * self.cright = self.cright.cleft # <<<<<<<<<<<<<<
+ * croot.cleft = self
+ * self.set_ends()
+ */
+ __pyx_t_1 = ((PyObject *)__pyx_v_self->cright->cleft);
+ __Pyx_INCREF(__pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_1);
+ __Pyx_GOTREF(__pyx_v_self->cright);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->cright));
+ __pyx_v_self->cright = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":155
+ * cdef IntervalNode croot = self.cright
+ * self.cright = self.cright.cleft
+ * croot.cleft = self # <<<<<<<<<<<<<<
+ * self.set_ends()
+ * return croot
+ */
+ __Pyx_INCREF(((PyObject *)__pyx_v_self));
+ __Pyx_GIVEREF(((PyObject *)__pyx_v_self));
+ __Pyx_GOTREF(__pyx_v_croot->cleft);
+ __Pyx_DECREF(((PyObject *)__pyx_v_croot->cleft));
+ __pyx_v_croot->cleft = __pyx_v_self;
+
+ /* "skbio/metadata/_intersection.pyx":156
+ * self.cright = self.cright.cleft
+ * croot.cleft = self
+ * self.set_ends() # <<<<<<<<<<<<<<
+ * return croot
+ *
+ */
+ __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_set_ends(__pyx_v_self);
+
+ /* "skbio/metadata/_intersection.pyx":157
+ * croot.cleft = self
+ * self.set_ends()
+ * return croot # <<<<<<<<<<<<<<
+ *
+ * cdef inline void set_ends(IntervalNode self):
+ */
+ __Pyx_XDECREF(((PyObject *)__pyx_r));
+ __Pyx_INCREF(((PyObject *)__pyx_v_croot));
+ __pyx_r = __pyx_v_croot;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":152
+ * return croot
+ *
+ * cdef IntervalNode rotate_left(IntervalNode self): # <<<<<<<<<<<<<<
+ * cdef IntervalNode croot = self.cright
+ * self.cright = self.cright.cleft
+ */
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_XDECREF((PyObject *)__pyx_v_croot);
+ __Pyx_XGIVEREF((PyObject *)__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":159
+ * return croot
+ *
+ * cdef inline void set_ends(IntervalNode self): # <<<<<<<<<<<<<<
+ * if self.cright is not EmptyNode and self.cleft is not EmptyNode:
+ * self.maxend = imax3(self.end, self.cright.maxend, self.cleft.maxend)
+ */
+
+static CYTHON_INLINE void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_set_ends(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ int __pyx_t_3;
+ __Pyx_RefNannySetupContext("set_ends", 0);
+
+ /* "skbio/metadata/_intersection.pyx":160
+ *
+ * cdef inline void set_ends(IntervalNode self):
+ * if self.cright is not EmptyNode and self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.maxend = imax3(self.end, self.cright.maxend, self.cleft.maxend)
+ * self.minend = imin3(self.end, self.cright.minend, self.cleft.minend)
+ */
+ __pyx_t_2 = (__pyx_v_self->cright != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_3 = (__pyx_t_2 != 0);
+ if (__pyx_t_3) {
+ } else {
+ __pyx_t_1 = __pyx_t_3;
+ goto __pyx_L4_bool_binop_done;
+ }
+ __pyx_t_3 = (__pyx_v_self->cleft != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_2 = (__pyx_t_3 != 0);
+ __pyx_t_1 = __pyx_t_2;
+ __pyx_L4_bool_binop_done:;
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":161
+ * cdef inline void set_ends(IntervalNode self):
+ * if self.cright is not EmptyNode and self.cleft is not EmptyNode:
+ * self.maxend = imax3(self.end, self.cright.maxend, self.cleft.maxend) # <<<<<<<<<<<<<<
+ * self.minend = imin3(self.end, self.cright.minend, self.cleft.minend)
+ * self.minstart = imin3(self.start, self.cright.minstart, self.cleft.minstart)
+ */
+ __pyx_v_self->maxend = __pyx_f_5skbio_8metadata_13_intersection_imax3(__pyx_v_self->end, __pyx_v_self->cright->maxend, __pyx_v_self->cleft->maxend);
+
+ /* "skbio/metadata/_intersection.pyx":162
+ * if self.cright is not EmptyNode and self.cleft is not EmptyNode:
+ * self.maxend = imax3(self.end, self.cright.maxend, self.cleft.maxend)
+ * self.minend = imin3(self.end, self.cright.minend, self.cleft.minend) # <<<<<<<<<<<<<<
+ * self.minstart = imin3(self.start, self.cright.minstart, self.cleft.minstart)
+ * elif self.cright is not EmptyNode:
+ */
+ __pyx_v_self->minend = __pyx_f_5skbio_8metadata_13_intersection_imin3(__pyx_v_self->end, __pyx_v_self->cright->minend, __pyx_v_self->cleft->minend);
+
+ /* "skbio/metadata/_intersection.pyx":163
+ * self.maxend = imax3(self.end, self.cright.maxend, self.cleft.maxend)
+ * self.minend = imin3(self.end, self.cright.minend, self.cleft.minend)
+ * self.minstart = imin3(self.start, self.cright.minstart, self.cleft.minstart) # <<<<<<<<<<<<<<
+ * elif self.cright is not EmptyNode:
+ * self.maxend = imax2(self.end, self.cright.maxend)
+ */
+ __pyx_v_self->minstart = __pyx_f_5skbio_8metadata_13_intersection_imin3(__pyx_v_self->start, __pyx_v_self->cright->minstart, __pyx_v_self->cleft->minstart);
+
+ /* "skbio/metadata/_intersection.pyx":160
+ *
+ * cdef inline void set_ends(IntervalNode self):
+ * if self.cright is not EmptyNode and self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.maxend = imax3(self.end, self.cright.maxend, self.cleft.maxend)
+ * self.minend = imin3(self.end, self.cright.minend, self.cleft.minend)
+ */
+ goto __pyx_L3;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":164
+ * self.minend = imin3(self.end, self.cright.minend, self.cleft.minend)
+ * self.minstart = imin3(self.start, self.cright.minstart, self.cleft.minstart)
+ * elif self.cright is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.maxend = imax2(self.end, self.cright.maxend)
+ * self.minend = imin2(self.end, self.cright.minend)
+ */
+ __pyx_t_1 = (__pyx_v_self->cright != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":165
+ * self.minstart = imin3(self.start, self.cright.minstart, self.cleft.minstart)
+ * elif self.cright is not EmptyNode:
+ * self.maxend = imax2(self.end, self.cright.maxend) # <<<<<<<<<<<<<<
+ * self.minend = imin2(self.end, self.cright.minend)
+ * self.minstart = imin2(self.start, self.cright.minstart)
+ */
+ __pyx_v_self->maxend = __pyx_f_5skbio_8metadata_13_intersection_imax2(__pyx_v_self->end, __pyx_v_self->cright->maxend);
+
+ /* "skbio/metadata/_intersection.pyx":166
+ * elif self.cright is not EmptyNode:
+ * self.maxend = imax2(self.end, self.cright.maxend)
+ * self.minend = imin2(self.end, self.cright.minend) # <<<<<<<<<<<<<<
+ * self.minstart = imin2(self.start, self.cright.minstart)
+ * elif self.cleft is not EmptyNode:
+ */
+ __pyx_v_self->minend = __pyx_f_5skbio_8metadata_13_intersection_imin2(__pyx_v_self->end, __pyx_v_self->cright->minend);
+
+ /* "skbio/metadata/_intersection.pyx":167
+ * self.maxend = imax2(self.end, self.cright.maxend)
+ * self.minend = imin2(self.end, self.cright.minend)
+ * self.minstart = imin2(self.start, self.cright.minstart) # <<<<<<<<<<<<<<
+ * elif self.cleft is not EmptyNode:
+ * self.maxend = imax2(self.end, self.cleft.maxend)
+ */
+ __pyx_v_self->minstart = __pyx_f_5skbio_8metadata_13_intersection_imin2(__pyx_v_self->start, __pyx_v_self->cright->minstart);
+
+ /* "skbio/metadata/_intersection.pyx":164
+ * self.minend = imin3(self.end, self.cright.minend, self.cleft.minend)
+ * self.minstart = imin3(self.start, self.cright.minstart, self.cleft.minstart)
+ * elif self.cright is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.maxend = imax2(self.end, self.cright.maxend)
+ * self.minend = imin2(self.end, self.cright.minend)
+ */
+ goto __pyx_L3;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":168
+ * self.minend = imin2(self.end, self.cright.minend)
+ * self.minstart = imin2(self.start, self.cright.minstart)
+ * elif self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.maxend = imax2(self.end, self.cleft.maxend)
+ * self.minend = imin2(self.end, self.cleft.minend)
+ */
+ __pyx_t_2 = (__pyx_v_self->cleft != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_1 = (__pyx_t_2 != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":169
+ * self.minstart = imin2(self.start, self.cright.minstart)
+ * elif self.cleft is not EmptyNode:
+ * self.maxend = imax2(self.end, self.cleft.maxend) # <<<<<<<<<<<<<<
+ * self.minend = imin2(self.end, self.cleft.minend)
+ * self.minstart = imin2(self.start, self.cleft.minstart)
+ */
+ __pyx_v_self->maxend = __pyx_f_5skbio_8metadata_13_intersection_imax2(__pyx_v_self->end, __pyx_v_self->cleft->maxend);
+
+ /* "skbio/metadata/_intersection.pyx":170
+ * elif self.cleft is not EmptyNode:
+ * self.maxend = imax2(self.end, self.cleft.maxend)
+ * self.minend = imin2(self.end, self.cleft.minend) # <<<<<<<<<<<<<<
+ * self.minstart = imin2(self.start, self.cleft.minstart)
+ *
+ */
+ __pyx_v_self->minend = __pyx_f_5skbio_8metadata_13_intersection_imin2(__pyx_v_self->end, __pyx_v_self->cleft->minend);
+
+ /* "skbio/metadata/_intersection.pyx":171
+ * self.maxend = imax2(self.end, self.cleft.maxend)
+ * self.minend = imin2(self.end, self.cleft.minend)
+ * self.minstart = imin2(self.start, self.cleft.minstart) # <<<<<<<<<<<<<<
+ *
+ * def intersect( self, int start, int end, sort=True ):
+ */
+ __pyx_v_self->minstart = __pyx_f_5skbio_8metadata_13_intersection_imin2(__pyx_v_self->start, __pyx_v_self->cleft->minstart);
+
+ /* "skbio/metadata/_intersection.pyx":168
+ * self.minend = imin2(self.end, self.cright.minend)
+ * self.minstart = imin2(self.start, self.cright.minstart)
+ * elif self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.maxend = imax2(self.end, self.cleft.maxend)
+ * self.minend = imin2(self.end, self.cleft.minend)
+ */
+ }
+ __pyx_L3:;
+
+ /* "skbio/metadata/_intersection.pyx":159
+ * return croot
+ *
+ * cdef inline void set_ends(IntervalNode self): # <<<<<<<<<<<<<<
+ * if self.cright is not EmptyNode and self.cleft is not EmptyNode:
+ * self.maxend = imax3(self.end, self.cright.maxend, self.cleft.maxend)
+ */
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+}
+
+/* "skbio/metadata/_intersection.pyx":173
+ * self.minstart = imin2(self.start, self.cleft.minstart)
+ *
+ * def intersect( self, int start, int end, sort=True ): # <<<<<<<<<<<<<<
+ * """
+ * given a start and a end, return a list of features
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_7intersect(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_6intersect[] = "\n given a start and a end, return a list of features\n falling within that range\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_7intersect(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ int __pyx_v_start;
+ int __pyx_v_end;
+ CYTHON_UNUSED PyObject *__pyx_v_sort = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("intersect (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_start,&__pyx_n_s_end,&__pyx_n_s_sort,0};
+ PyObject* values[3] = {0,0,0};
+ values[2] = ((PyObject *)Py_True);
+ if (unlikely(__pyx_kwds)) {
+ Py_ssize_t kw_args;
+ const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+ switch (pos_args) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_start)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_end)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("intersect", 0, 2, 3, 1); __PYX_ERR(0, 173, __pyx_L3_error)
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_sort);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "intersect") < 0)) __PYX_ERR(0, 173, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_start = __Pyx_PyInt_As_int(values[0]); if (unlikely((__pyx_v_start == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 173, __pyx_L3_error)
+ __pyx_v_end = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_end == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 173, __pyx_L3_error)
+ __pyx_v_sort = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("intersect", 0, 2, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 173, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.intersect", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_6intersect(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), __pyx_v_start, __pyx_v_end, __pyx_v_sort);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_6intersect(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, CYTHON_UNUSED PyObject *__pyx_v_sort) {
+ PyObject *__pyx_v_results = 0;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __Pyx_RefNannySetupContext("intersect", 0);
+
+ /* "skbio/metadata/_intersection.pyx":178
+ * falling within that range
+ * """
+ * cdef list results = [] # <<<<<<<<<<<<<<
+ * self._intersect( start, end, results )
+ * return results
+ */
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 178, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_results = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":179
+ * """
+ * cdef list results = []
+ * self._intersect( start, end, results ) # <<<<<<<<<<<<<<
+ * return results
+ *
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->__pyx_vtab)->_intersect(__pyx_v_self, __pyx_v_start, __pyx_v_end, __pyx_v_results);
+
+ /* "skbio/metadata/_intersection.pyx":180
+ * cdef list results = []
+ * self._intersect( start, end, results )
+ * return results # <<<<<<<<<<<<<<
+ *
+ * find = intersect
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_results);
+ __pyx_r = __pyx_v_results;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":173
+ * self.minstart = imin2(self.start, self.cleft.minstart)
+ *
+ * def intersect( self, int start, int end, sort=True ): # <<<<<<<<<<<<<<
+ * """
+ * given a start and a end, return a list of features
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.intersect", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_results);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":184
+ * find = intersect
+ *
+ * cdef void _intersect( IntervalNode self, int start, int end, list results): # <<<<<<<<<<<<<<
+ * cdef int send, qend
+ *
+ */
+
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__intersect(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_results) {
+ int __pyx_v_send;
+ int __pyx_v_qend;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ int __pyx_t_3;
+ int __pyx_t_4;
+ PyObject *__pyx_t_5 = NULL;
+ int __pyx_t_6;
+ __Pyx_RefNannySetupContext("_intersect", 0);
+
+ /* "skbio/metadata/_intersection.pyx":188
+ *
+ * # Left subtree
+ * if self.cleft is not EmptyNode and self.cleft.maxend >= start: # <<<<<<<<<<<<<<
+ * self.cleft._intersect( start, end, results )
+ * # This interval
+ */
+ __pyx_t_2 = (__pyx_v_self->cleft != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_3 = (__pyx_t_2 != 0);
+ if (__pyx_t_3) {
+ } else {
+ __pyx_t_1 = __pyx_t_3;
+ goto __pyx_L4_bool_binop_done;
+ }
+ __pyx_t_3 = ((__pyx_v_self->cleft->maxend >= __pyx_v_start) != 0);
+ __pyx_t_1 = __pyx_t_3;
+ __pyx_L4_bool_binop_done:;
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":189
+ * # Left subtree
+ * if self.cleft is not EmptyNode and self.cleft.maxend >= start:
+ * self.cleft._intersect( start, end, results ) # <<<<<<<<<<<<<<
+ * # This interval
+ * if start == end:
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cleft->__pyx_vtab)->_intersect(__pyx_v_self->cleft, __pyx_v_start, __pyx_v_end, __pyx_v_results);
+
+ /* "skbio/metadata/_intersection.pyx":188
+ *
+ * # Left subtree
+ * if self.cleft is not EmptyNode and self.cleft.maxend >= start: # <<<<<<<<<<<<<<
+ * self.cleft._intersect( start, end, results )
+ * # This interval
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":191
+ * self.cleft._intersect( start, end, results )
+ * # This interval
+ * if start == end: # <<<<<<<<<<<<<<
+ * qend = end
+ * else:
+ */
+ __pyx_t_1 = ((__pyx_v_start == __pyx_v_end) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":192
+ * # This interval
+ * if start == end:
+ * qend = end # <<<<<<<<<<<<<<
+ * else:
+ * qend = end - 1
+ */
+ __pyx_v_qend = __pyx_v_end;
+
+ /* "skbio/metadata/_intersection.pyx":191
+ * self.cleft._intersect( start, end, results )
+ * # This interval
+ * if start == end: # <<<<<<<<<<<<<<
+ * qend = end
+ * else:
+ */
+ goto __pyx_L6;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":194
+ * qend = end
+ * else:
+ * qend = end - 1 # <<<<<<<<<<<<<<
+ * if self.end == self.start:
+ * send = self.end
+ */
+ /*else*/ {
+ __pyx_v_qend = (__pyx_v_end - 1);
+ }
+ __pyx_L6:;
+
+ /* "skbio/metadata/_intersection.pyx":195
+ * else:
+ * qend = end - 1
+ * if self.end == self.start: # <<<<<<<<<<<<<<
+ * send = self.end
+ * else:
+ */
+ __pyx_t_1 = ((__pyx_v_self->end == __pyx_v_self->start) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":196
+ * qend = end - 1
+ * if self.end == self.start:
+ * send = self.end # <<<<<<<<<<<<<<
+ * else:
+ * send = self.end - 1
+ */
+ __pyx_t_4 = __pyx_v_self->end;
+ __pyx_v_send = __pyx_t_4;
+
+ /* "skbio/metadata/_intersection.pyx":195
+ * else:
+ * qend = end - 1
+ * if self.end == self.start: # <<<<<<<<<<<<<<
+ * send = self.end
+ * else:
+ */
+ goto __pyx_L7;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":198
+ * send = self.end
+ * else:
+ * send = self.end - 1 # <<<<<<<<<<<<<<
+ * if ( send >= start ) and ( self.start <= qend ):
+ * results.append( self.interval )
+ */
+ /*else*/ {
+ __pyx_v_send = (__pyx_v_self->end - 1);
+ }
+ __pyx_L7:;
+
+ /* "skbio/metadata/_intersection.pyx":199
+ * else:
+ * send = self.end - 1
+ * if ( send >= start ) and ( self.start <= qend ): # <<<<<<<<<<<<<<
+ * results.append( self.interval )
+ * # Right subtree
+ */
+ __pyx_t_3 = ((__pyx_v_send >= __pyx_v_start) != 0);
+ if (__pyx_t_3) {
+ } else {
+ __pyx_t_1 = __pyx_t_3;
+ goto __pyx_L9_bool_binop_done;
+ }
+ __pyx_t_3 = ((__pyx_v_self->start <= __pyx_v_qend) != 0);
+ __pyx_t_1 = __pyx_t_3;
+ __pyx_L9_bool_binop_done:;
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":200
+ * send = self.end - 1
+ * if ( send >= start ) and ( self.start <= qend ):
+ * results.append( self.interval ) # <<<<<<<<<<<<<<
+ * # Right subtree
+ * if self.cright is not EmptyNode and self.start <= qend:
+ */
+ if (unlikely(__pyx_v_results == Py_None)) {
+ PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", "append");
+ __PYX_ERR(0, 200, __pyx_L1_error)
+ }
+ __pyx_t_5 = __pyx_v_self->interval;
+ __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_6 = __Pyx_PyList_Append(__pyx_v_results, __pyx_t_5); if (unlikely(__pyx_t_6 == -1)) __PYX_ERR(0, 200, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":199
+ * else:
+ * send = self.end - 1
+ * if ( send >= start ) and ( self.start <= qend ): # <<<<<<<<<<<<<<
+ * results.append( self.interval )
+ * # Right subtree
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":202
+ * results.append( self.interval )
+ * # Right subtree
+ * if self.cright is not EmptyNode and self.start <= qend: # <<<<<<<<<<<<<<
+ * self.cright._intersect( start, end, results )
+ *
+ */
+ __pyx_t_3 = (__pyx_v_self->cright != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_2 = (__pyx_t_3 != 0);
+ if (__pyx_t_2) {
+ } else {
+ __pyx_t_1 = __pyx_t_2;
+ goto __pyx_L12_bool_binop_done;
+ }
+ __pyx_t_2 = ((__pyx_v_self->start <= __pyx_v_qend) != 0);
+ __pyx_t_1 = __pyx_t_2;
+ __pyx_L12_bool_binop_done:;
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":203
+ * # Right subtree
+ * if self.cright is not EmptyNode and self.start <= qend:
+ * self.cright._intersect( start, end, results ) # <<<<<<<<<<<<<<
+ *
+ * cpdef void update(IntervalNode self, int start, int end,
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cright->__pyx_vtab)->_intersect(__pyx_v_self->cright, __pyx_v_start, __pyx_v_end, __pyx_v_results);
+
+ /* "skbio/metadata/_intersection.pyx":202
+ * results.append( self.interval )
+ * # Right subtree
+ * if self.cright is not EmptyNode and self.start <= qend: # <<<<<<<<<<<<<<
+ * self.cright._intersect( start, end, results )
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":184
+ * find = intersect
+ *
+ * cdef void _intersect( IntervalNode self, int start, int end, list results): # <<<<<<<<<<<<<<
+ * cdef int send, qend
+ *
+ */
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_WriteUnraisable("skbio.metadata._intersection.IntervalNode._intersect", __pyx_clineno, __pyx_lineno, __pyx_filename, 0, 0);
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+}
+
+/* "skbio/metadata/_intersection.pyx":205
+ * self.cright._intersect( start, end, results )
+ *
+ * cpdef void update(IntervalNode self, int start, int end, # <<<<<<<<<<<<<<
+ * object old_feature, object new_feature):
+ * """
+ */
+
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9update(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_update(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_old_feature, PyObject *__pyx_v_new_feature, int __pyx_skip_dispatch) {
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ Py_ssize_t __pyx_t_7;
+ PyObject *__pyx_t_8 = NULL;
+ int __pyx_t_9;
+ int __pyx_t_10;
+ int __pyx_t_11;
+ __Pyx_RefNannySetupContext("update", 0);
+ /* Check if called by wrapper */
+ if (unlikely(__pyx_skip_dispatch)) ;
+ /* Check if overridden in Python */
+ else if (unlikely(Py_TYPE(((PyObject *)__pyx_v_self))->tp_dictoffset != 0)) {
+ __pyx_t_1 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s_update); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 205, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (!PyCFunction_Check(__pyx_t_1) || (PyCFunction_GET_FUNCTION(__pyx_t_1) != (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9update)) {
+ __pyx_t_3 = __Pyx_PyInt_From_int(__pyx_v_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 205, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyInt_From_int(__pyx_v_end); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 205, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_5 = __pyx_t_1; __pyx_t_6 = NULL;
+ __pyx_t_7 = 0;
+ if (CYTHON_COMPILING_IN_CPYTHON && 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_7 = 1;
+ }
+ }
+ __pyx_t_8 = PyTuple_New(4+__pyx_t_7); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 205, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ if (__pyx_t_6) {
+ __Pyx_GIVEREF(__pyx_t_6); PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_6); __pyx_t_6 = NULL;
+ }
+ __Pyx_GIVEREF(__pyx_t_3);
+ PyTuple_SET_ITEM(__pyx_t_8, 0+__pyx_t_7, __pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_4);
+ PyTuple_SET_ITEM(__pyx_t_8, 1+__pyx_t_7, __pyx_t_4);
+ __Pyx_INCREF(__pyx_v_old_feature);
+ __Pyx_GIVEREF(__pyx_v_old_feature);
+ PyTuple_SET_ITEM(__pyx_t_8, 2+__pyx_t_7, __pyx_v_old_feature);
+ __Pyx_INCREF(__pyx_v_new_feature);
+ __Pyx_GIVEREF(__pyx_v_new_feature);
+ PyTuple_SET_ITEM(__pyx_t_8, 3+__pyx_t_7, __pyx_v_new_feature);
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_5, __pyx_t_8, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 205, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ goto __pyx_L0;
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":213
+ *
+ * # Left subtree
+ * if self.cleft is not EmptyNode and self.cleft.maxend > start: # <<<<<<<<<<<<<<
+ * self.cleft.update( start, end, old_feature, new_feature )
+ * # This interval
+ */
+ __pyx_t_10 = (__pyx_v_self->cleft != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_11 = (__pyx_t_10 != 0);
+ if (__pyx_t_11) {
+ } else {
+ __pyx_t_9 = __pyx_t_11;
+ goto __pyx_L4_bool_binop_done;
+ }
+ __pyx_t_11 = ((__pyx_v_self->cleft->maxend > __pyx_v_start) != 0);
+ __pyx_t_9 = __pyx_t_11;
+ __pyx_L4_bool_binop_done:;
+ if (__pyx_t_9) {
+
+ /* "skbio/metadata/_intersection.pyx":214
+ * # Left subtree
+ * if self.cleft is not EmptyNode and self.cleft.maxend > start:
+ * self.cleft.update( start, end, old_feature, new_feature ) # <<<<<<<<<<<<<<
+ * # This interval
+ * if ( self.end > start ) and ( self.start < end ):
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cleft->__pyx_vtab)->update(__pyx_v_self->cleft, __pyx_v_start, __pyx_v_end, __pyx_v_old_feature, __pyx_v_new_feature, 0);
+
+ /* "skbio/metadata/_intersection.pyx":213
+ *
+ * # Left subtree
+ * if self.cleft is not EmptyNode and self.cleft.maxend > start: # <<<<<<<<<<<<<<
+ * self.cleft.update( start, end, old_feature, new_feature )
+ * # This interval
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":216
+ * self.cleft.update( start, end, old_feature, new_feature )
+ * # This interval
+ * if ( self.end > start ) and ( self.start < end ): # <<<<<<<<<<<<<<
+ * if self.interval == old_feature:
+ * self.interval = new_feature
+ */
+ __pyx_t_11 = ((__pyx_v_self->end > __pyx_v_start) != 0);
+ if (__pyx_t_11) {
+ } else {
+ __pyx_t_9 = __pyx_t_11;
+ goto __pyx_L7_bool_binop_done;
+ }
+ __pyx_t_11 = ((__pyx_v_self->start < __pyx_v_end) != 0);
+ __pyx_t_9 = __pyx_t_11;
+ __pyx_L7_bool_binop_done:;
+ if (__pyx_t_9) {
+
+ /* "skbio/metadata/_intersection.pyx":217
+ * # This interval
+ * if ( self.end > start ) and ( self.start < end ):
+ * if self.interval == old_feature: # <<<<<<<<<<<<<<
+ * self.interval = new_feature
+ * # Right subtree
+ */
+ __pyx_t_1 = PyObject_RichCompare(__pyx_v_self->interval, __pyx_v_old_feature, Py_EQ); __Pyx_XGOTREF(__pyx_t_1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 217, __pyx_L1_error)
+ __pyx_t_9 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_9 < 0)) __PYX_ERR(0, 217, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_9) {
+
+ /* "skbio/metadata/_intersection.pyx":218
+ * if ( self.end > start ) and ( self.start < end ):
+ * if self.interval == old_feature:
+ * self.interval = new_feature # <<<<<<<<<<<<<<
+ * # Right subtree
+ * if self.cright is not EmptyNode and self.start < end:
+ */
+ __Pyx_INCREF(__pyx_v_new_feature);
+ __Pyx_GIVEREF(__pyx_v_new_feature);
+ __Pyx_GOTREF(__pyx_v_self->interval);
+ __Pyx_DECREF(__pyx_v_self->interval);
+ __pyx_v_self->interval = __pyx_v_new_feature;
+
+ /* "skbio/metadata/_intersection.pyx":217
+ * # This interval
+ * if ( self.end > start ) and ( self.start < end ):
+ * if self.interval == old_feature: # <<<<<<<<<<<<<<
+ * self.interval = new_feature
+ * # Right subtree
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":216
+ * self.cleft.update( start, end, old_feature, new_feature )
+ * # This interval
+ * if ( self.end > start ) and ( self.start < end ): # <<<<<<<<<<<<<<
+ * if self.interval == old_feature:
+ * self.interval = new_feature
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":220
+ * self.interval = new_feature
+ * # Right subtree
+ * if self.cright is not EmptyNode and self.start < end: # <<<<<<<<<<<<<<
+ * self.cright.update( start, end, old_feature, new_feature )
+ *
+ */
+ __pyx_t_11 = (__pyx_v_self->cright != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_10 = (__pyx_t_11 != 0);
+ if (__pyx_t_10) {
+ } else {
+ __pyx_t_9 = __pyx_t_10;
+ goto __pyx_L11_bool_binop_done;
+ }
+ __pyx_t_10 = ((__pyx_v_self->start < __pyx_v_end) != 0);
+ __pyx_t_9 = __pyx_t_10;
+ __pyx_L11_bool_binop_done:;
+ if (__pyx_t_9) {
+
+ /* "skbio/metadata/_intersection.pyx":221
+ * # Right subtree
+ * if self.cright is not EmptyNode and self.start < end:
+ * self.cright.update( start, end, old_feature, new_feature ) # <<<<<<<<<<<<<<
+ *
+ * cdef void _seek_left(IntervalNode self, int position, list results, int n, int max_dist):
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cright->__pyx_vtab)->update(__pyx_v_self->cright, __pyx_v_start, __pyx_v_end, __pyx_v_old_feature, __pyx_v_new_feature, 0);
+
+ /* "skbio/metadata/_intersection.pyx":220
+ * self.interval = new_feature
+ * # Right subtree
+ * if self.cright is not EmptyNode and self.start < end: # <<<<<<<<<<<<<<
+ * self.cright.update( start, end, old_feature, new_feature )
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":205
+ * self.cright._intersect( start, end, results )
+ *
+ * cpdef void update(IntervalNode self, int start, int end, # <<<<<<<<<<<<<<
+ * object old_feature, object new_feature):
+ * """
+ */
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_WriteUnraisable("skbio.metadata._intersection.IntervalNode.update", __pyx_clineno, __pyx_lineno, __pyx_filename, 0, 0);
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+}
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9update(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_8update[] = "\n given a start and end, replace all objects that\n match the old_feature with new_feature.\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9update(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ int __pyx_v_start;
+ int __pyx_v_end;
+ PyObject *__pyx_v_old_feature = 0;
+ PyObject *__pyx_v_new_feature = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("update (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_start,&__pyx_n_s_end,&__pyx_n_s_old_feature,&__pyx_n_s_new_feature,0};
+ PyObject* values[4] = {0,0,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 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3);
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_start)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_end)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("update", 1, 4, 4, 1); __PYX_ERR(0, 205, __pyx_L3_error)
+ }
+ case 2:
+ if (likely((values[2] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_old_feature)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("update", 1, 4, 4, 2); __PYX_ERR(0, 205, __pyx_L3_error)
+ }
+ case 3:
+ if (likely((values[3] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_new_feature)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("update", 1, 4, 4, 3); __PYX_ERR(0, 205, __pyx_L3_error)
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "update") < 0)) __PYX_ERR(0, 205, __pyx_L3_error)
+ }
+ } else if (PyTuple_GET_SIZE(__pyx_args) != 4) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ values[3] = PyTuple_GET_ITEM(__pyx_args, 3);
+ }
+ __pyx_v_start = __Pyx_PyInt_As_int(values[0]); if (unlikely((__pyx_v_start == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 205, __pyx_L3_error)
+ __pyx_v_end = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_end == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 205, __pyx_L3_error)
+ __pyx_v_old_feature = values[2];
+ __pyx_v_new_feature = values[3];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("update", 1, 4, 4, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 205, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.update", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8update(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), __pyx_v_start, __pyx_v_end, __pyx_v_old_feature, __pyx_v_new_feature);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8update(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_old_feature, PyObject *__pyx_v_new_feature) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __Pyx_RefNannySetupContext("update", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_void_to_None(__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_update(__pyx_v_self, __pyx_v_start, __pyx_v_end, __pyx_v_old_feature, __pyx_v_new_feature, 1)); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 205, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.update", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":223
+ * self.cright.update( start, end, old_feature, new_feature )
+ *
+ * cdef void _seek_left(IntervalNode self, int position, list results, int n, int max_dist): # <<<<<<<<<<<<<<
+ * # we know we can bail in these 2 cases.
+ * if self.maxend + max_dist < position:
+ */
+
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__seek_left(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_position, PyObject *__pyx_v_results, int __pyx_v_n, int __pyx_v_max_dist) {
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ int __pyx_t_3;
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_t_5;
+ __Pyx_RefNannySetupContext("_seek_left", 0);
+
+ /* "skbio/metadata/_intersection.pyx":225
+ * cdef void _seek_left(IntervalNode self, int position, list results, int n, int max_dist):
+ * # we know we can bail in these 2 cases.
+ * if self.maxend + max_dist < position: # <<<<<<<<<<<<<<
+ * return
+ * if self.minstart > position:
+ */
+ __pyx_t_1 = (((__pyx_v_self->maxend + __pyx_v_max_dist) < __pyx_v_position) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":226
+ * # we know we can bail in these 2 cases.
+ * if self.maxend + max_dist < position:
+ * return # <<<<<<<<<<<<<<
+ * if self.minstart > position:
+ * return
+ */
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":225
+ * cdef void _seek_left(IntervalNode self, int position, list results, int n, int max_dist):
+ * # we know we can bail in these 2 cases.
+ * if self.maxend + max_dist < position: # <<<<<<<<<<<<<<
+ * return
+ * if self.minstart > position:
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":227
+ * if self.maxend + max_dist < position:
+ * return
+ * if self.minstart > position: # <<<<<<<<<<<<<<
+ * return
+ *
+ */
+ __pyx_t_1 = ((__pyx_v_self->minstart > __pyx_v_position) != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":228
+ * return
+ * if self.minstart > position:
+ * return # <<<<<<<<<<<<<<
+ *
+ * # the ordering of these 3 blocks makes it so the results are
+ */
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":227
+ * if self.maxend + max_dist < position:
+ * return
+ * if self.minstart > position: # <<<<<<<<<<<<<<
+ * return
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":232
+ * # the ordering of these 3 blocks makes it so the results are
+ * # ordered nearest to farest from the query position
+ * if self.cright is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cright._seek_left(position, results, n, max_dist)
+ *
+ */
+ __pyx_t_1 = (__pyx_v_self->cright != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":233
+ * # ordered nearest to farest from the query position
+ * if self.cright is not EmptyNode:
+ * self.cright._seek_left(position, results, n, max_dist) # <<<<<<<<<<<<<<
+ *
+ * if -1 < position - self.end < max_dist:
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cright->__pyx_vtab)->_seek_left(__pyx_v_self->cright, __pyx_v_position, __pyx_v_results, __pyx_v_n, __pyx_v_max_dist);
+
+ /* "skbio/metadata/_intersection.pyx":232
+ * # the ordering of these 3 blocks makes it so the results are
+ * # ordered nearest to farest from the query position
+ * if self.cright is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cright._seek_left(position, results, n, max_dist)
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":235
+ * self.cright._seek_left(position, results, n, max_dist)
+ *
+ * if -1 < position - self.end < max_dist: # <<<<<<<<<<<<<<
+ * results.append(self.interval)
+ *
+ */
+ __pyx_t_3 = (__pyx_v_position - __pyx_v_self->end);
+ __pyx_t_2 = (-1L < __pyx_t_3);
+ if (__pyx_t_2) {
+ __pyx_t_2 = (__pyx_t_3 < __pyx_v_max_dist);
+ }
+ __pyx_t_1 = (__pyx_t_2 != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":236
+ *
+ * if -1 < position - self.end < max_dist:
+ * results.append(self.interval) # <<<<<<<<<<<<<<
+ *
+ * # TODO: can these conditionals be more stringent?
+ */
+ if (unlikely(__pyx_v_results == Py_None)) {
+ PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", "append");
+ __PYX_ERR(0, 236, __pyx_L1_error)
+ }
+ __pyx_t_4 = __pyx_v_self->interval;
+ __Pyx_INCREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyList_Append(__pyx_v_results, __pyx_t_4); if (unlikely(__pyx_t_5 == -1)) __PYX_ERR(0, 236, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":235
+ * self.cright._seek_left(position, results, n, max_dist)
+ *
+ * if -1 < position - self.end < max_dist: # <<<<<<<<<<<<<<
+ * results.append(self.interval)
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":239
+ *
+ * # TODO: can these conditionals be more stringent?
+ * if self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cleft._seek_left(position, results, n, max_dist)
+ *
+ */
+ __pyx_t_1 = (__pyx_v_self->cleft != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":240
+ * # TODO: can these conditionals be more stringent?
+ * if self.cleft is not EmptyNode:
+ * self.cleft._seek_left(position, results, n, max_dist) # <<<<<<<<<<<<<<
+ *
+ *
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cleft->__pyx_vtab)->_seek_left(__pyx_v_self->cleft, __pyx_v_position, __pyx_v_results, __pyx_v_n, __pyx_v_max_dist);
+
+ /* "skbio/metadata/_intersection.pyx":239
+ *
+ * # TODO: can these conditionals be more stringent?
+ * if self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cleft._seek_left(position, results, n, max_dist)
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":223
+ * self.cright.update( start, end, old_feature, new_feature )
+ *
+ * cdef void _seek_left(IntervalNode self, int position, list results, int n, int max_dist): # <<<<<<<<<<<<<<
+ * # we know we can bail in these 2 cases.
+ * if self.maxend + max_dist < position:
+ */
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_WriteUnraisable("skbio.metadata._intersection.IntervalNode._seek_left", __pyx_clineno, __pyx_lineno, __pyx_filename, 0, 0);
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+}
+
+/* "skbio/metadata/_intersection.pyx":244
+ *
+ *
+ * cdef void _seek_right(IntervalNode self, int position, list results, int n, int max_dist): # <<<<<<<<<<<<<<
+ * # we know we can bail in these 2 cases.
+ * if self.maxend < position: return
+ */
+
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__seek_right(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, int __pyx_v_position, PyObject *__pyx_v_results, int __pyx_v_n, int __pyx_v_max_dist) {
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ int __pyx_t_3;
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_t_5;
+ __Pyx_RefNannySetupContext("_seek_right", 0);
+
+ /* "skbio/metadata/_intersection.pyx":246
+ * cdef void _seek_right(IntervalNode self, int position, list results, int n, int max_dist):
+ * # we know we can bail in these 2 cases.
+ * if self.maxend < position: return # <<<<<<<<<<<<<<
+ * if self.minstart - max_dist > position: return
+ *
+ */
+ __pyx_t_1 = ((__pyx_v_self->maxend < __pyx_v_position) != 0);
+ if (__pyx_t_1) {
+ goto __pyx_L0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":247
+ * # we know we can bail in these 2 cases.
+ * if self.maxend < position: return
+ * if self.minstart - max_dist > position: return # <<<<<<<<<<<<<<
+ *
+ * #print "SEEK_RIGHT:",self, self.cleft, self.maxend, self.minstart, position
+ */
+ __pyx_t_1 = (((__pyx_v_self->minstart - __pyx_v_max_dist) > __pyx_v_position) != 0);
+ if (__pyx_t_1) {
+ goto __pyx_L0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":253
+ * # the ordering of these 3 blocks makes it so the results are
+ * # ordered nearest to farest from the query position
+ * if self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cleft._seek_right(position, results, n, max_dist)
+ *
+ */
+ __pyx_t_1 = (__pyx_v_self->cleft != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":254
+ * # ordered nearest to farest from the query position
+ * if self.cleft is not EmptyNode:
+ * self.cleft._seek_right(position, results, n, max_dist) # <<<<<<<<<<<<<<
+ *
+ * if -1 < self.start - position < max_dist:
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cleft->__pyx_vtab)->_seek_right(__pyx_v_self->cleft, __pyx_v_position, __pyx_v_results, __pyx_v_n, __pyx_v_max_dist);
+
+ /* "skbio/metadata/_intersection.pyx":253
+ * # the ordering of these 3 blocks makes it so the results are
+ * # ordered nearest to farest from the query position
+ * if self.cleft is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cleft._seek_right(position, results, n, max_dist)
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":256
+ * self.cleft._seek_right(position, results, n, max_dist)
+ *
+ * if -1 < self.start - position < max_dist: # <<<<<<<<<<<<<<
+ * results.append(self.interval)
+ *
+ */
+ __pyx_t_3 = (__pyx_v_self->start - __pyx_v_position);
+ __pyx_t_2 = (-1L < __pyx_t_3);
+ if (__pyx_t_2) {
+ __pyx_t_2 = (__pyx_t_3 < __pyx_v_max_dist);
+ }
+ __pyx_t_1 = (__pyx_t_2 != 0);
+ if (__pyx_t_1) {
+
+ /* "skbio/metadata/_intersection.pyx":257
+ *
+ * if -1 < self.start - position < max_dist:
+ * results.append(self.interval) # <<<<<<<<<<<<<<
+ *
+ * if self.cright is not EmptyNode:
+ */
+ if (unlikely(__pyx_v_results == Py_None)) {
+ PyErr_Format(PyExc_AttributeError, "'NoneType' object has no attribute '%s'", "append");
+ __PYX_ERR(0, 257, __pyx_L1_error)
+ }
+ __pyx_t_4 = __pyx_v_self->interval;
+ __Pyx_INCREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyList_Append(__pyx_v_results, __pyx_t_4); if (unlikely(__pyx_t_5 == -1)) __PYX_ERR(0, 257, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":256
+ * self.cleft._seek_right(position, results, n, max_dist)
+ *
+ * if -1 < self.start - position < max_dist: # <<<<<<<<<<<<<<
+ * results.append(self.interval)
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":259
+ * results.append(self.interval)
+ *
+ * if self.cright is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cright._seek_right(position, results, n, max_dist)
+ *
+ */
+ __pyx_t_1 = (__pyx_v_self->cright != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":260
+ *
+ * if self.cright is not EmptyNode:
+ * self.cright._seek_right(position, results, n, max_dist) # <<<<<<<<<<<<<<
+ *
+ *
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cright->__pyx_vtab)->_seek_right(__pyx_v_self->cright, __pyx_v_position, __pyx_v_results, __pyx_v_n, __pyx_v_max_dist);
+
+ /* "skbio/metadata/_intersection.pyx":259
+ * results.append(self.interval)
+ *
+ * if self.cright is not EmptyNode: # <<<<<<<<<<<<<<
+ * self.cright._seek_right(position, results, n, max_dist)
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":244
+ *
+ *
+ * cdef void _seek_right(IntervalNode self, int position, list results, int n, int max_dist): # <<<<<<<<<<<<<<
+ * # we know we can bail in these 2 cases.
+ * if self.maxend < position: return
+ */
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_WriteUnraisable("skbio.metadata._intersection.IntervalNode._seek_right", __pyx_clineno, __pyx_lineno, __pyx_filename, 0, 0);
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+}
+
+/* "skbio/metadata/_intersection.pyx":263
+ *
+ *
+ * cpdef left(self, position, int n=1, int max_dist=2500): # <<<<<<<<<<<<<<
+ * """
+ * find n features with a start > than `position`
+ */
+
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_11left(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static PyObject *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_left(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_position, int __pyx_skip_dispatch, struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left *__pyx_optional_args) {
+ int __pyx_v_n = ((int)1);
+ int __pyx_v_max_dist = ((int)0x9C4);
+ PyObject *__pyx_v_results = 0;
+ PyObject *__pyx_v_r = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ Py_ssize_t __pyx_t_7;
+ PyObject *__pyx_t_8 = NULL;
+ int __pyx_t_9;
+ int __pyx_t_10;
+ __Pyx_RefNannySetupContext("left", 0);
+ if (__pyx_optional_args) {
+ if (__pyx_optional_args->__pyx_n > 0) {
+ __pyx_v_n = __pyx_optional_args->n;
+ if (__pyx_optional_args->__pyx_n > 1) {
+ __pyx_v_max_dist = __pyx_optional_args->max_dist;
+ }
+ }
+ }
+ /* Check if called by wrapper */
+ if (unlikely(__pyx_skip_dispatch)) ;
+ /* Check if overridden in Python */
+ else if (unlikely(Py_TYPE(((PyObject *)__pyx_v_self))->tp_dictoffset != 0)) {
+ __pyx_t_1 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s_left); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 263, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (!PyCFunction_Check(__pyx_t_1) || (PyCFunction_GET_FUNCTION(__pyx_t_1) != (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_11left)) {
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyInt_From_int(__pyx_v_n); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 263, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyInt_From_int(__pyx_v_max_dist); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 263, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_5 = __pyx_t_1; __pyx_t_6 = NULL;
+ __pyx_t_7 = 0;
+ if (CYTHON_COMPILING_IN_CPYTHON && 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_7 = 1;
+ }
+ }
+ __pyx_t_8 = PyTuple_New(3+__pyx_t_7); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 263, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ if (__pyx_t_6) {
+ __Pyx_GIVEREF(__pyx_t_6); PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_6); __pyx_t_6 = NULL;
+ }
+ __Pyx_INCREF(__pyx_v_position);
+ __Pyx_GIVEREF(__pyx_v_position);
+ PyTuple_SET_ITEM(__pyx_t_8, 0+__pyx_t_7, __pyx_v_position);
+ __Pyx_GIVEREF(__pyx_t_3);
+ PyTuple_SET_ITEM(__pyx_t_8, 1+__pyx_t_7, __pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_4);
+ PyTuple_SET_ITEM(__pyx_t_8, 2+__pyx_t_7, __pyx_t_4);
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_5, __pyx_t_8, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 263, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ goto __pyx_L0;
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":270
+ * max_dist: the maximum distance to look before giving up.
+ * """
+ * cdef list results = [] # <<<<<<<<<<<<<<
+ * # use start - 1 becuase .left() assumes strictly left-of
+ * self._seek_left( position - 1, results, n, max_dist )
+ */
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 270, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_results = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":272
+ * cdef list results = []
+ * # use start - 1 becuase .left() assumes strictly left-of
+ * self._seek_left( position - 1, results, n, max_dist ) # <<<<<<<<<<<<<<
+ * if len(results) == n: return results
+ * r = results
+ */
+ __pyx_t_1 = __Pyx_PyInt_SubtractObjC(__pyx_v_position, __pyx_int_1, 1, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 272, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_9 = __Pyx_PyInt_As_int(__pyx_t_1); if (unlikely((__pyx_t_9 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 272, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->__pyx_vtab)->_seek_left(__pyx_v_self, __pyx_t_9, __pyx_v_results, __pyx_v_n, __pyx_v_max_dist);
+
+ /* "skbio/metadata/_intersection.pyx":273
+ * # use start - 1 becuase .left() assumes strictly left-of
+ * self._seek_left( position - 1, results, n, max_dist )
+ * if len(results) == n: return results # <<<<<<<<<<<<<<
+ * r = results
+ * r.sort(key=operator.attrgetter('end'), reverse=True)
+ */
+ __pyx_t_7 = PyList_GET_SIZE(__pyx_v_results); if (unlikely(__pyx_t_7 == -1)) __PYX_ERR(0, 273, __pyx_L1_error)
+ __pyx_t_10 = ((__pyx_t_7 == __pyx_v_n) != 0);
+ if (__pyx_t_10) {
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_results);
+ __pyx_r = __pyx_v_results;
+ goto __pyx_L0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":274
+ * self._seek_left( position - 1, results, n, max_dist )
+ * if len(results) == n: return results
+ * r = results # <<<<<<<<<<<<<<
+ * r.sort(key=operator.attrgetter('end'), reverse=True)
+ * return r[:n]
+ */
+ __Pyx_INCREF(__pyx_v_results);
+ __pyx_v_r = __pyx_v_results;
+
+ /* "skbio/metadata/_intersection.pyx":275
+ * if len(results) == n: return results
+ * r = results
+ * r.sort(key=operator.attrgetter('end'), reverse=True) # <<<<<<<<<<<<<<
+ * return r[:n]
+ *
+ */
+ __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_r, __pyx_n_s_sort); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 275, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyDict_New(); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 275, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_GetModuleGlobalName(__pyx_n_s_operator); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 275, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s_attrgetter); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 275, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_8, __pyx_tuple_, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 275, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_key, __pyx_t_5) < 0) __PYX_ERR(0, 275, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_reverse, Py_True) < 0) __PYX_ERR(0, 275, __pyx_L1_error)
+ __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_empty_tuple, __pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 275, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":276
+ * r = results
+ * r.sort(key=operator.attrgetter('end'), reverse=True)
+ * return r[:n] # <<<<<<<<<<<<<<
+ *
+ * cpdef right(self, position, int n=1, int max_dist=2500):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_5 = __Pyx_PyList_GetSlice(__pyx_v_r, 0, __pyx_v_n); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 276, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":263
+ *
+ *
+ * cpdef left(self, position, int n=1, int max_dist=2500): # <<<<<<<<<<<<<<
+ * """
+ * find n features with a start > than `position`
+ */
+
+ /* 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_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.left", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = 0;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_results);
+ __Pyx_XDECREF(__pyx_v_r);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_11left(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_10left[] = "\n find n features with a start > than `position`\n f: a Interval object (or anything with an `end` attribute)\n n: the number of features to return\n max_dist: the maximum distance to look before giving up.\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_11left(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_position = 0;
+ int __pyx_v_n;
+ int __pyx_v_max_dist;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("left (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_position,&__pyx_n_s_n,&__pyx_n_s_max_dist,0};
+ PyObject* values[3] = {0,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 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_position)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_n);
+ if (value) { values[1] = value; kw_args--; }
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_max_dist);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "left") < 0)) __PYX_ERR(0, 263, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_position = values[0];
+ if (values[1]) {
+ __pyx_v_n = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_n == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 263, __pyx_L3_error)
+ } else {
+ __pyx_v_n = ((int)1);
+ }
+ if (values[2]) {
+ __pyx_v_max_dist = __Pyx_PyInt_As_int(values[2]); if (unlikely((__pyx_v_max_dist == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 263, __pyx_L3_error)
+ } else {
+ __pyx_v_max_dist = ((int)0x9C4);
+ }
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("left", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 263, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.left", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_10left(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), __pyx_v_position, __pyx_v_n, __pyx_v_max_dist);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_10left(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_position, int __pyx_v_n, int __pyx_v_max_dist) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left __pyx_t_2;
+ __Pyx_RefNannySetupContext("left", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2.__pyx_n = 2;
+ __pyx_t_2.n = __pyx_v_n;
+ __pyx_t_2.max_dist = __pyx_v_max_dist;
+ __pyx_t_1 = __pyx_vtabptr_5skbio_8metadata_13_intersection_IntervalNode->left(__pyx_v_self, __pyx_v_position, 1, &__pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 263, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.left", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":278
+ * return r[:n]
+ *
+ * cpdef right(self, position, int n=1, int max_dist=2500): # <<<<<<<<<<<<<<
+ * """
+ * find n features with a end < than position
+ */
+
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_13right(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static PyObject *__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_right(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_position, int __pyx_skip_dispatch, struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right *__pyx_optional_args) {
+ int __pyx_v_n = ((int)1);
+ int __pyx_v_max_dist = ((int)0x9C4);
+ PyObject *__pyx_v_results = 0;
+ PyObject *__pyx_v_r = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ Py_ssize_t __pyx_t_7;
+ PyObject *__pyx_t_8 = NULL;
+ int __pyx_t_9;
+ int __pyx_t_10;
+ __Pyx_RefNannySetupContext("right", 0);
+ if (__pyx_optional_args) {
+ if (__pyx_optional_args->__pyx_n > 0) {
+ __pyx_v_n = __pyx_optional_args->n;
+ if (__pyx_optional_args->__pyx_n > 1) {
+ __pyx_v_max_dist = __pyx_optional_args->max_dist;
+ }
+ }
+ }
+ /* Check if called by wrapper */
+ if (unlikely(__pyx_skip_dispatch)) ;
+ /* Check if overridden in Python */
+ else if (unlikely(Py_TYPE(((PyObject *)__pyx_v_self))->tp_dictoffset != 0)) {
+ __pyx_t_1 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s_right); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 278, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (!PyCFunction_Check(__pyx_t_1) || (PyCFunction_GET_FUNCTION(__pyx_t_1) != (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_13right)) {
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyInt_From_int(__pyx_v_n); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 278, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyInt_From_int(__pyx_v_max_dist); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 278, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_1);
+ __pyx_t_5 = __pyx_t_1; __pyx_t_6 = NULL;
+ __pyx_t_7 = 0;
+ if (CYTHON_COMPILING_IN_CPYTHON && 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_7 = 1;
+ }
+ }
+ __pyx_t_8 = PyTuple_New(3+__pyx_t_7); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 278, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ if (__pyx_t_6) {
+ __Pyx_GIVEREF(__pyx_t_6); PyTuple_SET_ITEM(__pyx_t_8, 0, __pyx_t_6); __pyx_t_6 = NULL;
+ }
+ __Pyx_INCREF(__pyx_v_position);
+ __Pyx_GIVEREF(__pyx_v_position);
+ PyTuple_SET_ITEM(__pyx_t_8, 0+__pyx_t_7, __pyx_v_position);
+ __Pyx_GIVEREF(__pyx_t_3);
+ PyTuple_SET_ITEM(__pyx_t_8, 1+__pyx_t_7, __pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_4);
+ PyTuple_SET_ITEM(__pyx_t_8, 2+__pyx_t_7, __pyx_t_4);
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_5, __pyx_t_8, NULL); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 278, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_r = __pyx_t_2;
+ __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ goto __pyx_L0;
+ }
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":285
+ * max_dist: the maximum distance to look before giving up.
+ * """
+ * cdef list results = [] # <<<<<<<<<<<<<<
+ * # use end + 1 becuase .right() assumes strictly right-of
+ * self._seek_right(position + 1, results, n, max_dist)
+ */
+ __pyx_t_1 = PyList_New(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 285, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_v_results = ((PyObject*)__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":287
+ * cdef list results = []
+ * # use end + 1 becuase .right() assumes strictly right-of
+ * self._seek_right(position + 1, results, n, max_dist) # <<<<<<<<<<<<<<
+ * if len(results) == n: return results
+ * r = results
+ */
+ __pyx_t_1 = __Pyx_PyInt_AddObjC(__pyx_v_position, __pyx_int_1, 1, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 287, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_9 = __Pyx_PyInt_As_int(__pyx_t_1); if (unlikely((__pyx_t_9 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 287, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->__pyx_vtab)->_seek_right(__pyx_v_self, __pyx_t_9, __pyx_v_results, __pyx_v_n, __pyx_v_max_dist);
+
+ /* "skbio/metadata/_intersection.pyx":288
+ * # use end + 1 becuase .right() assumes strictly right-of
+ * self._seek_right(position + 1, results, n, max_dist)
+ * if len(results) == n: return results # <<<<<<<<<<<<<<
+ * r = results
+ * r.sort(key=operator.attrgetter('start'))
+ */
+ __pyx_t_7 = PyList_GET_SIZE(__pyx_v_results); if (unlikely(__pyx_t_7 == -1)) __PYX_ERR(0, 288, __pyx_L1_error)
+ __pyx_t_10 = ((__pyx_t_7 == __pyx_v_n) != 0);
+ if (__pyx_t_10) {
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_results);
+ __pyx_r = __pyx_v_results;
+ goto __pyx_L0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":289
+ * self._seek_right(position + 1, results, n, max_dist)
+ * if len(results) == n: return results
+ * r = results # <<<<<<<<<<<<<<
+ * r.sort(key=operator.attrgetter('start'))
+ * return r[:n]
+ */
+ __Pyx_INCREF(__pyx_v_results);
+ __pyx_v_r = __pyx_v_results;
+
+ /* "skbio/metadata/_intersection.pyx":290
+ * if len(results) == n: return results
+ * r = results
+ * r.sort(key=operator.attrgetter('start')) # <<<<<<<<<<<<<<
+ * return r[:n]
+ *
+ */
+ __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_v_r, __pyx_n_s_sort); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 290, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = PyDict_New(); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 290, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_5 = __Pyx_GetModuleGlobalName(__pyx_n_s_operator); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 290, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_5, __pyx_n_s_attrgetter); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 290, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_8);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_8, __pyx_tuple__2, NULL); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 290, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_8); __pyx_t_8 = 0;
+ if (PyDict_SetItem(__pyx_t_2, __pyx_n_s_key, __pyx_t_5) < 0) __PYX_ERR(0, 290, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_empty_tuple, __pyx_t_2); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 290, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":291
+ * r = results
+ * r.sort(key=operator.attrgetter('start'))
+ * return r[:n] # <<<<<<<<<<<<<<
+ *
+ * def traverse(self, func):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_5 = __Pyx_PyList_GetSlice(__pyx_v_r, 0, __pyx_v_n); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 291, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_r = __pyx_t_5;
+ __pyx_t_5 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":278
+ * return r[:n]
+ *
+ * cpdef right(self, position, int n=1, int max_dist=2500): # <<<<<<<<<<<<<<
+ * """
+ * find n features with a end < than position
+ */
+
+ /* 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_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_XDECREF(__pyx_t_8);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.right", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = 0;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_results);
+ __Pyx_XDECREF(__pyx_v_r);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_13right(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_12right[] = "\n find n features with a end < than position\n f: a Interval object (or anything with a `start` attribute)\n n: the number of features to return\n max_dist: the maximum distance to look before giving up.\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_13right(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_position = 0;
+ int __pyx_v_n;
+ int __pyx_v_max_dist;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("right (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_position,&__pyx_n_s_n,&__pyx_n_s_max_dist,0};
+ PyObject* values[3] = {0,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 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_position)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_n);
+ if (value) { values[1] = value; kw_args--; }
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_max_dist);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "right") < 0)) __PYX_ERR(0, 278, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_position = values[0];
+ if (values[1]) {
+ __pyx_v_n = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_n == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 278, __pyx_L3_error)
+ } else {
+ __pyx_v_n = ((int)1);
+ }
+ if (values[2]) {
+ __pyx_v_max_dist = __Pyx_PyInt_As_int(values[2]); if (unlikely((__pyx_v_max_dist == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 278, __pyx_L3_error)
+ } else {
+ __pyx_v_max_dist = ((int)0x9C4);
+ }
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("right", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 278, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.right", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_12right(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), __pyx_v_position, __pyx_v_n, __pyx_v_max_dist);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_12right(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_position, int __pyx_v_n, int __pyx_v_max_dist) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right __pyx_t_2;
+ __Pyx_RefNannySetupContext("right", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_2.__pyx_n = 2;
+ __pyx_t_2.n = __pyx_v_n;
+ __pyx_t_2.max_dist = __pyx_v_max_dist;
+ __pyx_t_1 = __pyx_vtabptr_5skbio_8metadata_13_intersection_IntervalNode->right(__pyx_v_self, __pyx_v_position, 1, &__pyx_t_2); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 278, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.right", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":293
+ * return r[:n]
+ *
+ * def traverse(self, func): # <<<<<<<<<<<<<<
+ * self._traverse(func)
+ *
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_15traverse(PyObject *__pyx_v_self, PyObject *__pyx_v_func); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_15traverse(PyObject *__pyx_v_self, PyObject *__pyx_v_func) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("traverse (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_14traverse(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), ((PyObject *)__pyx_v_func));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_14traverse(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_func) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("traverse", 0);
+
+ /* "skbio/metadata/_intersection.pyx":294
+ *
+ * def traverse(self, func):
+ * self._traverse(func) # <<<<<<<<<<<<<<
+ *
+ * cdef void _traverse(IntervalNode self, object func):
+ */
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->__pyx_vtab)->_traverse(__pyx_v_self, __pyx_v_func);
+
+ /* "skbio/metadata/_intersection.pyx":293
+ * return r[:n]
+ *
+ * def traverse(self, func): # <<<<<<<<<<<<<<
+ * self._traverse(func)
+ *
+ */
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":296
+ * self._traverse(func)
+ *
+ * cdef void _traverse(IntervalNode self, object func): # <<<<<<<<<<<<<<
+ * if self.cleft is not EmptyNode: self.cleft._traverse(func)
+ * func(self)
+ */
+
+static void __pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__traverse(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_func) {
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ __Pyx_RefNannySetupContext("_traverse", 0);
+
+ /* "skbio/metadata/_intersection.pyx":297
+ *
+ * cdef void _traverse(IntervalNode self, object func):
+ * if self.cleft is not EmptyNode: self.cleft._traverse(func) # <<<<<<<<<<<<<<
+ * func(self)
+ * if self.cright is not EmptyNode: self.cright._traverse(func)
+ */
+ __pyx_t_1 = (__pyx_v_self->cleft != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cleft->__pyx_vtab)->_traverse(__pyx_v_self->cleft, __pyx_v_func);
+ }
+
+ /* "skbio/metadata/_intersection.pyx":298
+ * cdef void _traverse(IntervalNode self, object func):
+ * if self.cleft is not EmptyNode: self.cleft._traverse(func)
+ * func(self) # <<<<<<<<<<<<<<
+ * if self.cright is not EmptyNode: self.cright._traverse(func)
+ *
+ */
+ __Pyx_INCREF(__pyx_v_func);
+ __pyx_t_4 = __pyx_v_func; __pyx_t_5 = NULL;
+ if (CYTHON_COMPILING_IN_CPYTHON && unlikely(PyMethod_Check(__pyx_t_4))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_4);
+ if (likely(__pyx_t_5)) {
+ PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(function);
+ __Pyx_DECREF_SET(__pyx_t_4, function);
+ }
+ }
+ if (!__pyx_t_5) {
+ __pyx_t_3 = __Pyx_PyObject_CallOneArg(__pyx_t_4, ((PyObject *)__pyx_v_self)); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 298, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ } else {
+ __pyx_t_6 = PyTuple_New(1+1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 298, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GIVEREF(__pyx_t_5); PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_5); __pyx_t_5 = NULL;
+ __Pyx_INCREF(((PyObject *)__pyx_v_self));
+ __Pyx_GIVEREF(((PyObject *)__pyx_v_self));
+ PyTuple_SET_ITEM(__pyx_t_6, 0+1, ((PyObject *)__pyx_v_self));
+ __pyx_t_3 = __Pyx_PyObject_Call(__pyx_t_4, __pyx_t_6, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 298, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":299
+ * if self.cleft is not EmptyNode: self.cleft._traverse(func)
+ * func(self)
+ * if self.cright is not EmptyNode: self.cright._traverse(func) # <<<<<<<<<<<<<<
+ *
+ * cdef IntervalNode EmptyNode = IntervalNode( 0, 0, IntervalObj(0, 0))
+ */
+ __pyx_t_2 = (__pyx_v_self->cright != __pyx_v_5skbio_8metadata_13_intersection_EmptyNode);
+ __pyx_t_1 = (__pyx_t_2 != 0);
+ if (__pyx_t_1) {
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->cright->__pyx_vtab)->_traverse(__pyx_v_self->cright, __pyx_v_func);
+ }
+
+ /* "skbio/metadata/_intersection.pyx":296
+ * self._traverse(func)
+ *
+ * cdef void _traverse(IntervalNode self, object func): # <<<<<<<<<<<<<<
+ * if self.cleft is not EmptyNode: self.cleft._traverse(func)
+ * func(self)
+ */
+
+ /* function exit code */
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_WriteUnraisable("skbio.metadata._intersection.IntervalNode._traverse", __pyx_clineno, __pyx_lineno, __pyx_filename, 0, 0);
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+}
+
+/* "skbio/metadata/_intersection.pyx":74
+ * """
+ * cdef float priority
+ * cdef public object interval # <<<<<<<<<<<<<<
+ * cdef public int start, end
+ * cdef int minend, maxend, minstart
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_8interval_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_8interval_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8interval___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8interval___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_self->interval);
+ __pyx_r = __pyx_v_self->interval;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_8interval_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_8interval_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8interval_2__set__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), ((PyObject *)__pyx_v_value));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8interval_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__", 0);
+ __Pyx_INCREF(__pyx_v_value);
+ __Pyx_GIVEREF(__pyx_v_value);
+ __Pyx_GOTREF(__pyx_v_self->interval);
+ __Pyx_DECREF(__pyx_v_self->interval);
+ __pyx_v_self->interval = __pyx_v_value;
+
+ /* function exit code */
+ __pyx_r = 0;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_8interval_5__del__(PyObject *__pyx_v_self); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_8interval_5__del__(PyObject *__pyx_v_self) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__del__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8interval_4__del__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_8interval_4__del__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__del__", 0);
+ __Pyx_INCREF(Py_None);
+ __Pyx_GIVEREF(Py_None);
+ __Pyx_GOTREF(__pyx_v_self->interval);
+ __Pyx_DECREF(__pyx_v_self->interval);
+ __pyx_v_self->interval = Py_None;
+
+ /* function exit code */
+ __pyx_r = 0;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":75
+ * cdef float priority
+ * cdef public object interval
+ * cdef public int start, end # <<<<<<<<<<<<<<
+ * cdef int minend, maxend, minstart
+ * cdef IntervalNode cleft, cright, croot
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5start_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5start_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_5start___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_5start___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __Pyx_RefNannySetupContext("__get__", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_self->start); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 75, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.start.__get__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5start_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5start_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_5start_2__set__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), ((PyObject *)__pyx_v_value));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_5start_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ __Pyx_RefNannySetupContext("__set__", 0);
+ __pyx_t_1 = __Pyx_PyInt_As_int(__pyx_v_value); if (unlikely((__pyx_t_1 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 75, __pyx_L1_error)
+ __pyx_v_self->start = __pyx_t_1;
+
+ /* function exit code */
+ __pyx_r = 0;
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.start.__set__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = -1;
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_3end_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_3end_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_3end___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_3end___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __Pyx_RefNannySetupContext("__get__", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_self->end); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 75, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.end.__get__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_3end_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_3end_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_3end_2__set__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self), ((PyObject *)__pyx_v_value));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalNode_3end_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ __Pyx_RefNannySetupContext("__set__", 0);
+ __pyx_t_1 = __Pyx_PyInt_As_int(__pyx_v_value); if (unlikely((__pyx_t_1 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 75, __pyx_L1_error)
+ __pyx_v_self->end = __pyx_t_1;
+
+ /* function exit code */
+ __pyx_r = 0;
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalNode.end.__set__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = -1;
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":322
+ * cdef public object value, chrom, strand
+ *
+ * def __init__(self, int start, int end, object value=None, object chrom=None, object strand=None ): # <<<<<<<<<<<<<<
+ * assert start <= end, "start must be less than end"
+ * self.start = start
+ */
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_1__init__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_1__init__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ int __pyx_v_start;
+ int __pyx_v_end;
+ PyObject *__pyx_v_value = 0;
+ PyObject *__pyx_v_chrom = 0;
+ PyObject *__pyx_v_strand = 0;
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__init__ (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_start,&__pyx_n_s_end,&__pyx_n_s_value,&__pyx_n_s_chrom,&__pyx_n_s_strand,0};
+ PyObject* values[5] = {0,0,0,0,0};
+ values[2] = ((PyObject *)Py_None);
+ values[3] = ((PyObject *)Py_None);
+ values[4] = ((PyObject *)Py_None);
+ if (unlikely(__pyx_kwds)) {
+ Py_ssize_t kw_args;
+ const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+ switch (pos_args) {
+ case 5: values[4] = PyTuple_GET_ITEM(__pyx_args, 4);
+ case 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3);
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_start)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_end)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("__init__", 0, 2, 5, 1); __PYX_ERR(0, 322, __pyx_L3_error)
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_value);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ case 3:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_chrom);
+ if (value) { values[3] = value; kw_args--; }
+ }
+ case 4:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_strand);
+ if (value) { values[4] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "__init__") < 0)) __PYX_ERR(0, 322, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 5: values[4] = PyTuple_GET_ITEM(__pyx_args, 4);
+ case 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3);
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_start = __Pyx_PyInt_As_int(values[0]); if (unlikely((__pyx_v_start == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 322, __pyx_L3_error)
+ __pyx_v_end = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_end == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 322, __pyx_L3_error)
+ __pyx_v_value = values[2];
+ __pyx_v_chrom = values[3];
+ __pyx_v_strand = values[4];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("__init__", 0, 2, 5, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 322, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalObj.__init__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return -1;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj___init__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self), __pyx_v_start, __pyx_v_end, __pyx_v_value, __pyx_v_chrom, __pyx_v_strand);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj___init__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_value, PyObject *__pyx_v_chrom, PyObject *__pyx_v_strand) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__init__", 0);
+
+ /* "skbio/metadata/_intersection.pyx":323
+ *
+ * def __init__(self, int start, int end, object value=None, object chrom=None, object strand=None ):
+ * assert start <= end, "start must be less than end" # <<<<<<<<<<<<<<
+ * self.start = start
+ * self.end = end
+ */
+ #ifndef CYTHON_WITHOUT_ASSERTIONS
+ if (unlikely(!Py_OptimizeFlag)) {
+ if (unlikely(!((__pyx_v_start <= __pyx_v_end) != 0))) {
+ PyErr_SetObject(PyExc_AssertionError, __pyx_kp_s_start_must_be_less_than_end);
+ __PYX_ERR(0, 323, __pyx_L1_error)
+ }
+ }
+ #endif
+
+ /* "skbio/metadata/_intersection.pyx":324
+ * def __init__(self, int start, int end, object value=None, object chrom=None, object strand=None ):
+ * assert start <= end, "start must be less than end"
+ * self.start = start # <<<<<<<<<<<<<<
+ * self.end = end
+ * self.value = value
+ */
+ __pyx_v_self->start = __pyx_v_start;
+
+ /* "skbio/metadata/_intersection.pyx":325
+ * assert start <= end, "start must be less than end"
+ * self.start = start
+ * self.end = end # <<<<<<<<<<<<<<
+ * self.value = value
+ * self.chrom = chrom
+ */
+ __pyx_v_self->end = __pyx_v_end;
+
+ /* "skbio/metadata/_intersection.pyx":326
+ * self.start = start
+ * self.end = end
+ * self.value = value # <<<<<<<<<<<<<<
+ * self.chrom = chrom
+ * self.strand = strand
+ */
+ __Pyx_INCREF(__pyx_v_value);
+ __Pyx_GIVEREF(__pyx_v_value);
+ __Pyx_GOTREF(__pyx_v_self->value);
+ __Pyx_DECREF(__pyx_v_self->value);
+ __pyx_v_self->value = __pyx_v_value;
+
+ /* "skbio/metadata/_intersection.pyx":327
+ * self.end = end
+ * self.value = value
+ * self.chrom = chrom # <<<<<<<<<<<<<<
+ * self.strand = strand
+ *
+ */
+ __Pyx_INCREF(__pyx_v_chrom);
+ __Pyx_GIVEREF(__pyx_v_chrom);
+ __Pyx_GOTREF(__pyx_v_self->chrom);
+ __Pyx_DECREF(__pyx_v_self->chrom);
+ __pyx_v_self->chrom = __pyx_v_chrom;
+
+ /* "skbio/metadata/_intersection.pyx":328
+ * self.value = value
+ * self.chrom = chrom
+ * self.strand = strand # <<<<<<<<<<<<<<
+ *
+ * def __repr__(self):
+ */
+ __Pyx_INCREF(__pyx_v_strand);
+ __Pyx_GIVEREF(__pyx_v_strand);
+ __Pyx_GOTREF(__pyx_v_self->strand);
+ __Pyx_DECREF(__pyx_v_self->strand);
+ __pyx_v_self->strand = __pyx_v_strand;
+
+ /* "skbio/metadata/_intersection.pyx":322
+ * cdef public object value, chrom, strand
+ *
+ * def __init__(self, int start, int end, object value=None, object chrom=None, object strand=None ): # <<<<<<<<<<<<<<
+ * assert start <= end, "start must be less than end"
+ * self.start = start
+ */
+
+ /* function exit code */
+ __pyx_r = 0;
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalObj.__init__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = -1;
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":330
+ * self.strand = strand
+ *
+ * def __repr__(self): # <<<<<<<<<<<<<<
+ * fstr = "IntervalObj(%d, %d" % (self.start, self.end)
+ * if not self.value is None:
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_3__repr__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_3__repr__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__repr__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_2__repr__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_2__repr__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self) {
+ PyObject *__pyx_v_fstr = NULL;
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_t_4;
+ int __pyx_t_5;
+ __Pyx_RefNannySetupContext("__repr__", 0);
+
+ /* "skbio/metadata/_intersection.pyx":331
+ *
+ * def __repr__(self):
+ * fstr = "IntervalObj(%d, %d" % (self.start, self.end) # <<<<<<<<<<<<<<
+ * if not self.value is None:
+ * fstr += ", value=" + str(self.value)
+ */
+ __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_self->start); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 331, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyInt_From_int(__pyx_v_self->end); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 331, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = PyTuple_New(2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 331, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_1);
+ PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_t_1);
+ __Pyx_GIVEREF(__pyx_t_2);
+ PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_2);
+ __pyx_t_1 = 0;
+ __pyx_t_2 = 0;
+ __pyx_t_2 = __Pyx_PyString_Format(__pyx_kp_s_IntervalObj_d_d, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 331, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_v_fstr = __pyx_t_2;
+ __pyx_t_2 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":332
+ * def __repr__(self):
+ * fstr = "IntervalObj(%d, %d" % (self.start, self.end)
+ * if not self.value is None: # <<<<<<<<<<<<<<
+ * fstr += ", value=" + str(self.value)
+ * fstr += ")"
+ */
+ __pyx_t_4 = (__pyx_v_self->value != Py_None);
+ __pyx_t_5 = (__pyx_t_4 != 0);
+ if (__pyx_t_5) {
+
+ /* "skbio/metadata/_intersection.pyx":333
+ * fstr = "IntervalObj(%d, %d" % (self.start, self.end)
+ * if not self.value is None:
+ * fstr += ", value=" + str(self.value) # <<<<<<<<<<<<<<
+ * fstr += ")"
+ * return fstr
+ */
+ __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 333, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_INCREF(__pyx_v_self->value);
+ __Pyx_GIVEREF(__pyx_v_self->value);
+ PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_v_self->value);
+ __pyx_t_3 = __Pyx_PyObject_Call(((PyObject *)(&PyString_Type)), __pyx_t_2, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 333, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __pyx_t_2 = PyNumber_Add(__pyx_kp_s_value_2, __pyx_t_3); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 333, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_3 = PyNumber_InPlaceAdd(__pyx_v_fstr, __pyx_t_2); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 333, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF_SET(__pyx_v_fstr, __pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":332
+ * def __repr__(self):
+ * fstr = "IntervalObj(%d, %d" % (self.start, self.end)
+ * if not self.value is None: # <<<<<<<<<<<<<<
+ * fstr += ", value=" + str(self.value)
+ * fstr += ")"
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":334
+ * if not self.value is None:
+ * fstr += ", value=" + str(self.value)
+ * fstr += ")" # <<<<<<<<<<<<<<
+ * return fstr
+ *
+ */
+ __pyx_t_3 = PyNumber_InPlaceAdd(__pyx_v_fstr, __pyx_kp_s__3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 334, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF_SET(__pyx_v_fstr, __pyx_t_3);
+ __pyx_t_3 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":335
+ * fstr += ", value=" + str(self.value)
+ * fstr += ")"
+ * return fstr # <<<<<<<<<<<<<<
+ *
+ * def __richcmp__(self, other, op):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_fstr);
+ __pyx_r = __pyx_v_fstr;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":330
+ * self.strand = strand
+ *
+ * def __repr__(self): # <<<<<<<<<<<<<<
+ * fstr = "IntervalObj(%d, %d" % (self.start, self.end)
+ * if not self.value is None:
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalObj.__repr__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XDECREF(__pyx_v_fstr);
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":337
+ * return fstr
+ *
+ * def __richcmp__(self, other, op): # <<<<<<<<<<<<<<
+ * if op == 0:
+ * # <
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5__richcmp__(PyObject *__pyx_v_self, PyObject *__pyx_v_other, int __pyx_arg_op); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5__richcmp__(PyObject *__pyx_v_self, PyObject *__pyx_v_other, int __pyx_arg_op) {
+ PyObject *__pyx_v_op = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__richcmp__ (wrapper)", 0);
+ __pyx_v_op = __Pyx_PyInt_From_int(__pyx_arg_op); if (unlikely(!__pyx_v_op)) __PYX_ERR(0, 337, __pyx_L3_error)
+ __Pyx_GOTREF(__pyx_v_op);
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalObj.__richcmp__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_4__richcmp__(((PyObject *)__pyx_v_self), ((PyObject *)__pyx_v_other), ((PyObject *)__pyx_v_op));
+
+ /* function exit code */
+ __Pyx_XDECREF(__pyx_v_op);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_4__richcmp__(PyObject *__pyx_v_self, PyObject *__pyx_v_other, PyObject *__pyx_v_op) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ __Pyx_RefNannySetupContext("__richcmp__", 0);
+
+ /* "skbio/metadata/_intersection.pyx":338
+ *
+ * def __richcmp__(self, other, op):
+ * if op == 0: # <<<<<<<<<<<<<<
+ * # <
+ * return self.start < other.start or self.end < other.end
+ */
+ __pyx_t_1 = __Pyx_PyInt_EqObjC(__pyx_v_op, __pyx_int_0, 0, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 338, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 338, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":340
+ * if op == 0:
+ * # <
+ * return self.start < other.start or self.end < other.end # <<<<<<<<<<<<<<
+ * elif op == 1:
+ * # <=
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_other, __pyx_n_s_start); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyObject_RichCompare(__pyx_t_3, __pyx_t_4, Py_LT); __Pyx_XGOTREF(__pyx_t_5); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 340, __pyx_L1_error)
+ if (!__pyx_t_2) {
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_1 = __pyx_t_5;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ goto __pyx_L4_bool_binop_done;
+ }
+ __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_end); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_other, __pyx_n_s_end); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = PyObject_RichCompare(__pyx_t_5, __pyx_t_4, Py_LT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 340, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_t_3;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_L4_bool_binop_done:;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":338
+ *
+ * def __richcmp__(self, other, op):
+ * if op == 0: # <<<<<<<<<<<<<<
+ * # <
+ * return self.start < other.start or self.end < other.end
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":341
+ * # <
+ * return self.start < other.start or self.end < other.end
+ * elif op == 1: # <<<<<<<<<<<<<<
+ * # <=
+ * return self == other or self < other
+ */
+ __pyx_t_1 = __Pyx_PyInt_EqObjC(__pyx_v_op, __pyx_int_1, 1, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 341, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 341, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":343
+ * elif op == 1:
+ * # <=
+ * return self == other or self < other # <<<<<<<<<<<<<<
+ * elif op == 2:
+ * # ==
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyObject_RichCompare(__pyx_v_self, __pyx_v_other, Py_EQ); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 343, __pyx_L1_error)
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 343, __pyx_L1_error)
+ if (!__pyx_t_2) {
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_t_3;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L6_bool_binop_done;
+ }
+ __pyx_t_3 = PyObject_RichCompare(__pyx_v_self, __pyx_v_other, Py_LT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 343, __pyx_L1_error)
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_t_3;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_L6_bool_binop_done:;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":341
+ * # <
+ * return self.start < other.start or self.end < other.end
+ * elif op == 1: # <<<<<<<<<<<<<<
+ * # <=
+ * return self == other or self < other
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":344
+ * # <=
+ * return self == other or self < other
+ * elif op == 2: # <<<<<<<<<<<<<<
+ * # ==
+ * return self.start == other.start and self.end == other.end
+ */
+ __pyx_t_1 = __Pyx_PyInt_EqObjC(__pyx_v_op, __pyx_int_2, 2, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 344, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 344, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":346
+ * elif op == 2:
+ * # ==
+ * return self.start == other.start and self.end == other.end # <<<<<<<<<<<<<<
+ * elif op == 3:
+ * # !=
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 346, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_other, __pyx_n_s_start); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 346, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyObject_RichCompare(__pyx_t_3, __pyx_t_4, Py_EQ); __Pyx_XGOTREF(__pyx_t_5); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 346, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 346, __pyx_L1_error)
+ if (__pyx_t_2) {
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_1 = __pyx_t_5;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ goto __pyx_L8_bool_binop_done;
+ }
+ __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_end); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 346, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_other, __pyx_n_s_end); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 346, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = PyObject_RichCompare(__pyx_t_5, __pyx_t_4, Py_EQ); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 346, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_t_3;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_L8_bool_binop_done:;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":344
+ * # <=
+ * return self == other or self < other
+ * elif op == 2: # <<<<<<<<<<<<<<
+ * # ==
+ * return self.start == other.start and self.end == other.end
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":347
+ * # ==
+ * return self.start == other.start and self.end == other.end
+ * elif op == 3: # <<<<<<<<<<<<<<
+ * # !=
+ * return self.start != other.start or self.end != other.end
+ */
+ __pyx_t_1 = __Pyx_PyInt_EqObjC(__pyx_v_op, __pyx_int_3, 3, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 347, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 347, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":349
+ * elif op == 3:
+ * # !=
+ * return self.start != other.start or self.end != other.end # <<<<<<<<<<<<<<
+ * elif op == 4:
+ * # >
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 349, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_other, __pyx_n_s_start); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 349, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyObject_RichCompare(__pyx_t_3, __pyx_t_4, Py_NE); __Pyx_XGOTREF(__pyx_t_5); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 349, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 349, __pyx_L1_error)
+ if (!__pyx_t_2) {
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_1 = __pyx_t_5;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ goto __pyx_L10_bool_binop_done;
+ }
+ __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_end); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 349, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_other, __pyx_n_s_end); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 349, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = PyObject_RichCompare(__pyx_t_5, __pyx_t_4, Py_NE); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 349, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_t_3;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_L10_bool_binop_done:;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":347
+ * # ==
+ * return self.start == other.start and self.end == other.end
+ * elif op == 3: # <<<<<<<<<<<<<<
+ * # !=
+ * return self.start != other.start or self.end != other.end
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":350
+ * # !=
+ * return self.start != other.start or self.end != other.end
+ * elif op == 4: # <<<<<<<<<<<<<<
+ * # >
+ * return self.start > other.start or self.end > other.end
+ */
+ __pyx_t_1 = __Pyx_PyInt_EqObjC(__pyx_v_op, __pyx_int_4, 4, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 350, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 350, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":352
+ * elif op == 4:
+ * # >
+ * return self.start > other.start or self.end > other.end # <<<<<<<<<<<<<<
+ * elif op == 5:
+ * # >=
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_other, __pyx_n_s_start); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyObject_RichCompare(__pyx_t_3, __pyx_t_4, Py_GT); __Pyx_XGOTREF(__pyx_t_5); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_5); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 352, __pyx_L1_error)
+ if (!__pyx_t_2) {
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_t_5);
+ __pyx_t_1 = __pyx_t_5;
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ goto __pyx_L12_bool_binop_done;
+ }
+ __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_v_self, __pyx_n_s_end); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_other, __pyx_n_s_end); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_3 = PyObject_RichCompare(__pyx_t_5, __pyx_t_4, Py_GT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 352, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_t_3;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_L12_bool_binop_done:;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":350
+ * # !=
+ * return self.start != other.start or self.end != other.end
+ * elif op == 4: # <<<<<<<<<<<<<<
+ * # >
+ * return self.start > other.start or self.end > other.end
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":353
+ * # >
+ * return self.start > other.start or self.end > other.end
+ * elif op == 5: # <<<<<<<<<<<<<<
+ * # >=
+ * return self == other or self > other
+ */
+ __pyx_t_1 = __Pyx_PyInt_EqObjC(__pyx_v_op, __pyx_int_5, 5, 0); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 353, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_1); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 353, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":355
+ * elif op == 5:
+ * # >=
+ * return self == other or self > other # <<<<<<<<<<<<<<
+ *
+ * cdef class IntervalTree:
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyObject_RichCompare(__pyx_v_self, __pyx_v_other, Py_EQ); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 355, __pyx_L1_error)
+ __pyx_t_2 = __Pyx_PyObject_IsTrue(__pyx_t_3); if (unlikely(__pyx_t_2 < 0)) __PYX_ERR(0, 355, __pyx_L1_error)
+ if (!__pyx_t_2) {
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ } else {
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_t_3;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ goto __pyx_L14_bool_binop_done;
+ }
+ __pyx_t_3 = PyObject_RichCompare(__pyx_v_self, __pyx_v_other, Py_GT); __Pyx_XGOTREF(__pyx_t_3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 355, __pyx_L1_error)
+ __Pyx_INCREF(__pyx_t_3);
+ __pyx_t_1 = __pyx_t_3;
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_L14_bool_binop_done:;
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":353
+ * # >
+ * return self.start > other.start or self.end > other.end
+ * elif op == 5: # <<<<<<<<<<<<<<
+ * # >=
+ * return self == other or self > other
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":337
+ * return fstr
+ *
+ * def __richcmp__(self, other, op): # <<<<<<<<<<<<<<
+ * if op == 0:
+ * # <
+ */
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalObj.__richcmp__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":319
+ *
+ * """
+ * cdef public int start, end # <<<<<<<<<<<<<<
+ * cdef public object value, chrom, strand
+ *
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5start_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5start_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5start___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5start___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __Pyx_RefNannySetupContext("__get__", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_self->start); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 319, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalObj.start.__get__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5start_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5start_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5start_2__set__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self), ((PyObject *)__pyx_v_value));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5start_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ __Pyx_RefNannySetupContext("__set__", 0);
+ __pyx_t_1 = __Pyx_PyInt_As_int(__pyx_v_value); if (unlikely((__pyx_t_1 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 319, __pyx_L1_error)
+ __pyx_v_self->start = __pyx_t_1;
+
+ /* function exit code */
+ __pyx_r = 0;
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalObj.start.__set__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = -1;
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_3end_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_3end_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_3end___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_3end___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ __Pyx_RefNannySetupContext("__get__", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_1 = __Pyx_PyInt_From_int(__pyx_v_self->end); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 319, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_r = __pyx_t_1;
+ __pyx_t_1 = 0;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalObj.end.__get__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_3end_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_3end_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_3end_2__set__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self), ((PyObject *)__pyx_v_value));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_3end_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ __Pyx_RefNannySetupContext("__set__", 0);
+ __pyx_t_1 = __Pyx_PyInt_As_int(__pyx_v_value); if (unlikely((__pyx_t_1 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 319, __pyx_L1_error)
+ __pyx_v_self->end = __pyx_t_1;
+
+ /* function exit code */
+ __pyx_r = 0;
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalObj.end.__set__", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = -1;
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":320
+ * """
+ * cdef public int start, end
+ * cdef public object value, chrom, strand # <<<<<<<<<<<<<<
+ *
+ * def __init__(self, int start, int end, object value=None, object chrom=None, object strand=None ):
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5value_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5value_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5value___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5value___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_self->value);
+ __pyx_r = __pyx_v_self->value;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5value_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5value_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5value_2__set__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self), ((PyObject *)__pyx_v_value));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5value_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__", 0);
+ __Pyx_INCREF(__pyx_v_value);
+ __Pyx_GIVEREF(__pyx_v_value);
+ __Pyx_GOTREF(__pyx_v_self->value);
+ __Pyx_DECREF(__pyx_v_self->value);
+ __pyx_v_self->value = __pyx_v_value;
+
+ /* function exit code */
+ __pyx_r = 0;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5value_5__del__(PyObject *__pyx_v_self); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5value_5__del__(PyObject *__pyx_v_self) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__del__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5value_4__del__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5value_4__del__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__del__", 0);
+ __Pyx_INCREF(Py_None);
+ __Pyx_GIVEREF(Py_None);
+ __Pyx_GOTREF(__pyx_v_self->value);
+ __Pyx_DECREF(__pyx_v_self->value);
+ __pyx_v_self->value = Py_None;
+
+ /* function exit code */
+ __pyx_r = 0;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5chrom___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5chrom___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_self->chrom);
+ __pyx_r = __pyx_v_self->chrom;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_2__set__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self), ((PyObject *)__pyx_v_value));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__", 0);
+ __Pyx_INCREF(__pyx_v_value);
+ __Pyx_GIVEREF(__pyx_v_value);
+ __Pyx_GOTREF(__pyx_v_self->chrom);
+ __Pyx_DECREF(__pyx_v_self->chrom);
+ __pyx_v_self->chrom = __pyx_v_value;
+
+ /* function exit code */
+ __pyx_r = 0;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_5__del__(PyObject *__pyx_v_self); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_5__del__(PyObject *__pyx_v_self) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__del__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_4__del__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_4__del__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__del__", 0);
+ __Pyx_INCREF(Py_None);
+ __Pyx_GIVEREF(Py_None);
+ __Pyx_GOTREF(__pyx_v_self->chrom);
+ __Pyx_DECREF(__pyx_v_self->chrom);
+ __pyx_v_self->chrom = Py_None;
+
+ /* function exit code */
+ __pyx_r = 0;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_6strand_1__get__(PyObject *__pyx_v_self); /*proto*/
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_6strand_1__get__(PyObject *__pyx_v_self) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_6strand___get__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_6strand___get__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__get__", 0);
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(__pyx_v_self->strand);
+ __pyx_r = __pyx_v_self->strand;
+ goto __pyx_L0;
+
+ /* function exit code */
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_6strand_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_6strand_3__set__(PyObject *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_6strand_2__set__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self), ((PyObject *)__pyx_v_value));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_6strand_2__set__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self, PyObject *__pyx_v_value) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__set__", 0);
+ __Pyx_INCREF(__pyx_v_value);
+ __Pyx_GIVEREF(__pyx_v_value);
+ __Pyx_GOTREF(__pyx_v_self->strand);
+ __Pyx_DECREF(__pyx_v_self->strand);
+ __pyx_v_self->strand = __pyx_v_value;
+
+ /* function exit code */
+ __pyx_r = 0;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_6strand_5__del__(PyObject *__pyx_v_self); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_6strand_5__del__(PyObject *__pyx_v_self) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__del__ (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_6strand_4__del__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_11IntervalObj_6strand_4__del__(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *__pyx_v_self) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__del__", 0);
+ __Pyx_INCREF(Py_None);
+ __Pyx_GIVEREF(Py_None);
+ __Pyx_GOTREF(__pyx_v_self->strand);
+ __Pyx_DECREF(__pyx_v_self->strand);
+ __pyx_v_self->strand = Py_None;
+
+ /* function exit code */
+ __pyx_r = 0;
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":415
+ * cdef IntervalNode root
+ *
+ * def __cinit__( self ): # <<<<<<<<<<<<<<
+ * root = None
+ *
+ */
+
+/* Python wrapper */
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_1__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static int __pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_1__cinit__(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__cinit__ (wrapper)", 0);
+ if (unlikely(PyTuple_GET_SIZE(__pyx_args) > 0)) {
+ __Pyx_RaiseArgtupleInvalid("__cinit__", 1, 0, 0, PyTuple_GET_SIZE(__pyx_args)); return -1;}
+ if (unlikely(__pyx_kwds) && unlikely(PyDict_Size(__pyx_kwds) > 0) && unlikely(!__Pyx_CheckKeywordStrings(__pyx_kwds, "__cinit__", 0))) return -1;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree___cinit__(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static int __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree___cinit__(CYTHON_UNUSED struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self) {
+ CYTHON_UNUSED PyObject *__pyx_v_root = NULL;
+ int __pyx_r;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__cinit__", 0);
+
+ /* "skbio/metadata/_intersection.pyx":416
+ *
+ * def __cinit__( self ):
+ * root = None # <<<<<<<<<<<<<<
+ *
+ * # ---- Position based interfaces -----------------------------------------
+ */
+ __Pyx_INCREF(Py_None);
+ __pyx_v_root = Py_None;
+
+ /* "skbio/metadata/_intersection.pyx":415
+ * cdef IntervalNode root
+ *
+ * def __cinit__( self ): # <<<<<<<<<<<<<<
+ * root = None
+ *
+ */
+
+ /* function exit code */
+ __pyx_r = 0;
+ __Pyx_XDECREF(__pyx_v_root);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":420
+ * # ---- Position based interfaces -----------------------------------------
+ *
+ * def insert( self, int start, int end, object value=None ): # <<<<<<<<<<<<<<
+ * """
+ * Insert the interval [start,end) associated with value `value`.
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_3insert(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_2insert[] = "\n Insert the interval [start,end) associated with value `value`.\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_3insert(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ int __pyx_v_start;
+ int __pyx_v_end;
+ PyObject *__pyx_v_value = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("insert (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_start,&__pyx_n_s_end,&__pyx_n_s_value,0};
+ PyObject* values[3] = {0,0,0};
+ values[2] = ((PyObject *)Py_None);
+ if (unlikely(__pyx_kwds)) {
+ Py_ssize_t kw_args;
+ const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+ switch (pos_args) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_start)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_end)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("insert", 0, 2, 3, 1); __PYX_ERR(0, 420, __pyx_L3_error)
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_value);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "insert") < 0)) __PYX_ERR(0, 420, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_start = __Pyx_PyInt_As_int(values[0]); if (unlikely((__pyx_v_start == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 420, __pyx_L3_error)
+ __pyx_v_end = __Pyx_PyInt_As_int(values[1]); if (unlikely((__pyx_v_end == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 420, __pyx_L3_error)
+ __pyx_v_value = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("insert", 0, 2, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 420, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.insert", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_2insert(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), __pyx_v_start, __pyx_v_end, __pyx_v_value);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_2insert(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, int __pyx_v_start, int __pyx_v_end, PyObject *__pyx_v_value) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ __Pyx_RefNannySetupContext("insert", 0);
+
+ /* "skbio/metadata/_intersection.pyx":424
+ * Insert the interval [start,end) associated with value `value`.
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * self.root = IntervalNode( start, end, value )
+ * else:
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) == Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":425
+ * """
+ * if self.root is None:
+ * self.root = IntervalNode( start, end, value ) # <<<<<<<<<<<<<<
+ * else:
+ * self.root = self.root.insert( start, end, value )
+ */
+ __pyx_t_3 = __Pyx_PyInt_From_int(__pyx_v_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 425, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyInt_From_int(__pyx_v_end); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 425, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = PyTuple_New(3); if (unlikely(!__pyx_t_5)) __PYX_ERR(0, 425, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_5);
+ __Pyx_GIVEREF(__pyx_t_3);
+ PyTuple_SET_ITEM(__pyx_t_5, 0, __pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_4);
+ PyTuple_SET_ITEM(__pyx_t_5, 1, __pyx_t_4);
+ __Pyx_INCREF(__pyx_v_value);
+ __Pyx_GIVEREF(__pyx_v_value);
+ PyTuple_SET_ITEM(__pyx_t_5, 2, __pyx_v_value);
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_t_4 = __Pyx_PyObject_Call(((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode), __pyx_t_5, NULL); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 425, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
+ __Pyx_GIVEREF(__pyx_t_4);
+ __Pyx_GOTREF(__pyx_v_self->root);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->root));
+ __pyx_v_self->root = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_4);
+ __pyx_t_4 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":424
+ * Insert the interval [start,end) associated with value `value`.
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * self.root = IntervalNode( start, end, value )
+ * else:
+ */
+ goto __pyx_L3;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":427
+ * self.root = IntervalNode( start, end, value )
+ * else:
+ * self.root = self.root.insert( start, end, value ) # <<<<<<<<<<<<<<
+ *
+ * add = insert
+ */
+ /*else*/ {
+ __pyx_t_4 = ((PyObject *)((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->insert(__pyx_v_self->root, __pyx_v_start, __pyx_v_end, __pyx_v_value, 0)); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 427, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_GIVEREF(__pyx_t_4);
+ __Pyx_GOTREF(__pyx_v_self->root);
+ __Pyx_DECREF(((PyObject *)__pyx_v_self->root));
+ __pyx_v_self->root = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_4);
+ __pyx_t_4 = 0;
+ }
+ __pyx_L3:;
+
+ /* "skbio/metadata/_intersection.pyx":420
+ * # ---- Position based interfaces -----------------------------------------
+ *
+ * def insert( self, int start, int end, object value=None ): # <<<<<<<<<<<<<<
+ * """
+ * Insert the interval [start,end) associated with value `value`.
+ */
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.insert", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":432
+ *
+ *
+ * def update( self, start, end, old_feature, new_feature): # <<<<<<<<<<<<<<
+ * """
+ * Given an interval [start, end), replace all objects that
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_5update(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_4update[] = "\n Given an interval [start, end), replace all objects that\n match the `old_feature` with `new_feature`.\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_5update(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_start = 0;
+ PyObject *__pyx_v_end = 0;
+ PyObject *__pyx_v_old_feature = 0;
+ PyObject *__pyx_v_new_feature = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("update (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_start,&__pyx_n_s_end,&__pyx_n_s_old_feature,&__pyx_n_s_new_feature,0};
+ PyObject* values[4] = {0,0,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 4: values[3] = PyTuple_GET_ITEM(__pyx_args, 3);
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_start)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_end)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("update", 1, 4, 4, 1); __PYX_ERR(0, 432, __pyx_L3_error)
+ }
+ case 2:
+ if (likely((values[2] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_old_feature)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("update", 1, 4, 4, 2); __PYX_ERR(0, 432, __pyx_L3_error)
+ }
+ case 3:
+ if (likely((values[3] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_new_feature)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("update", 1, 4, 4, 3); __PYX_ERR(0, 432, __pyx_L3_error)
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "update") < 0)) __PYX_ERR(0, 432, __pyx_L3_error)
+ }
+ } else if (PyTuple_GET_SIZE(__pyx_args) != 4) {
+ goto __pyx_L5_argtuple_error;
+ } else {
+ values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ values[3] = PyTuple_GET_ITEM(__pyx_args, 3);
+ }
+ __pyx_v_start = values[0];
+ __pyx_v_end = values[1];
+ __pyx_v_old_feature = values[2];
+ __pyx_v_new_feature = values[3];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("update", 1, 4, 4, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 432, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.update", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_4update(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), __pyx_v_start, __pyx_v_end, __pyx_v_old_feature, __pyx_v_new_feature);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_4update(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_start, PyObject *__pyx_v_end, PyObject *__pyx_v_old_feature, PyObject *__pyx_v_new_feature) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ int __pyx_t_3;
+ int __pyx_t_4;
+ __Pyx_RefNannySetupContext("update", 0);
+
+ /* "skbio/metadata/_intersection.pyx":437
+ * match the `old_feature` with `new_feature`.
+ * """
+ * if self.root is not None: # <<<<<<<<<<<<<<
+ * self.root.update(start, end, old_feature, new_feature)
+ *
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) != Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":438
+ * """
+ * if self.root is not None:
+ * self.root.update(start, end, old_feature, new_feature) # <<<<<<<<<<<<<<
+ *
+ *
+ */
+ __pyx_t_3 = __Pyx_PyInt_As_int(__pyx_v_start); if (unlikely((__pyx_t_3 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 438, __pyx_L1_error)
+ __pyx_t_4 = __Pyx_PyInt_As_int(__pyx_v_end); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 438, __pyx_L1_error)
+ ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->update(__pyx_v_self->root, __pyx_t_3, __pyx_t_4, __pyx_v_old_feature, __pyx_v_new_feature, 0);
+
+ /* "skbio/metadata/_intersection.pyx":437
+ * match the `old_feature` with `new_feature`.
+ * """
+ * if self.root is not None: # <<<<<<<<<<<<<<
+ * self.root.update(start, end, old_feature, new_feature)
+ *
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":432
+ *
+ *
+ * def update( self, start, end, old_feature, new_feature): # <<<<<<<<<<<<<<
+ * """
+ * Given an interval [start, end), replace all objects that
+ */
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.update", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":441
+ *
+ *
+ * def find( self, start, end ): # <<<<<<<<<<<<<<
+ * """
+ * Return a sorted list of all intervals overlapping [start,end).
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_7find(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_6find[] = "\n Return a sorted list of all intervals overlapping [start,end).\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_7find(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_start = 0;
+ PyObject *__pyx_v_end = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("find (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_start,&__pyx_n_s_end,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);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_start)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_end)) != 0)) kw_args--;
+ else {
+ __Pyx_RaiseArgtupleInvalid("find", 1, 2, 2, 1); __PYX_ERR(0, 441, __pyx_L3_error)
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "find") < 0)) __PYX_ERR(0, 441, __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_start = values[0];
+ __pyx_v_end = values[1];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("find", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 441, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.find", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_6find(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), __pyx_v_start, __pyx_v_end);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_6find(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_start, PyObject *__pyx_v_end) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ Py_ssize_t __pyx_t_6;
+ PyObject *__pyx_t_7 = NULL;
+ __Pyx_RefNannySetupContext("find", 0);
+
+ /* "skbio/metadata/_intersection.pyx":445
+ * Return a sorted list of all intervals overlapping [start,end).
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.find( start, end )
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) == Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":446
+ * """
+ * if self.root is None:
+ * return [] # <<<<<<<<<<<<<<
+ * return self.root.find( start, end )
+ *
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 446, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":445
+ * Return a sorted list of all intervals overlapping [start,end).
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.find( start, end )
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":447
+ * if self.root is None:
+ * return []
+ * return self.root.find( start, end ) # <<<<<<<<<<<<<<
+ *
+ * def before( self, position, num_intervals=1, max_dist=2500 ):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self->root), __pyx_n_s_find); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 447, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = NULL;
+ __pyx_t_6 = 0;
+ if (CYTHON_COMPILING_IN_CPYTHON && likely(PyMethod_Check(__pyx_t_4))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_4);
+ if (likely(__pyx_t_5)) {
+ PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(function);
+ __Pyx_DECREF_SET(__pyx_t_4, function);
+ __pyx_t_6 = 1;
+ }
+ }
+ __pyx_t_7 = PyTuple_New(2+__pyx_t_6); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 447, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ if (__pyx_t_5) {
+ __Pyx_GIVEREF(__pyx_t_5); PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_5); __pyx_t_5 = NULL;
+ }
+ __Pyx_INCREF(__pyx_v_start);
+ __Pyx_GIVEREF(__pyx_v_start);
+ PyTuple_SET_ITEM(__pyx_t_7, 0+__pyx_t_6, __pyx_v_start);
+ __Pyx_INCREF(__pyx_v_end);
+ __Pyx_GIVEREF(__pyx_v_end);
+ PyTuple_SET_ITEM(__pyx_t_7, 1+__pyx_t_6, __pyx_v_end);
+ __pyx_t_3 = __Pyx_PyObject_Call(__pyx_t_4, __pyx_t_7, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 447, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":441
+ *
+ *
+ * def find( self, start, end ): # <<<<<<<<<<<<<<
+ * """
+ * Return a sorted list of all intervals overlapping [start,end).
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.find", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":449
+ * return self.root.find( start, end )
+ *
+ * def before( self, position, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie before `position` and are no
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_9before(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_8before[] = "\n Find `num_intervals` intervals that lie before `position` and are no\n further than `max_dist` positions away\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_9before(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_position = 0;
+ PyObject *__pyx_v_num_intervals = 0;
+ PyObject *__pyx_v_max_dist = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("before (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_position,&__pyx_n_s_num_intervals,&__pyx_n_s_max_dist,0};
+ PyObject* values[3] = {0,0,0};
+ values[1] = ((PyObject *)__pyx_int_1);
+ values[2] = ((PyObject *)__pyx_int_2500);
+ if (unlikely(__pyx_kwds)) {
+ Py_ssize_t kw_args;
+ const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+ switch (pos_args) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_position)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_num_intervals);
+ if (value) { values[1] = value; kw_args--; }
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_max_dist);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "before") < 0)) __PYX_ERR(0, 449, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_position = values[0];
+ __pyx_v_num_intervals = values[1];
+ __pyx_v_max_dist = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("before", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 449, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.before", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_8before(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), __pyx_v_position, __pyx_v_num_intervals, __pyx_v_max_dist);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_8before(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_position, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_t_4;
+ int __pyx_t_5;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left __pyx_t_6;
+ __Pyx_RefNannySetupContext("before", 0);
+
+ /* "skbio/metadata/_intersection.pyx":454
+ * further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.left( position, num_intervals, max_dist )
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) == Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":455
+ * """
+ * if self.root is None:
+ * return [] # <<<<<<<<<<<<<<
+ * return self.root.left( position, num_intervals, max_dist )
+ *
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 455, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":454
+ * further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.left( position, num_intervals, max_dist )
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":456
+ * if self.root is None:
+ * return []
+ * return self.root.left( position, num_intervals, max_dist ) # <<<<<<<<<<<<<<
+ *
+ * def after( self, position, num_intervals=1, max_dist=2500 ):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_4 = __Pyx_PyInt_As_int(__pyx_v_num_intervals); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 456, __pyx_L1_error)
+ __pyx_t_5 = __Pyx_PyInt_As_int(__pyx_v_max_dist); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 456, __pyx_L1_error)
+ __pyx_t_6.__pyx_n = 2;
+ __pyx_t_6.n = __pyx_t_4;
+ __pyx_t_6.max_dist = __pyx_t_5;
+ __pyx_t_3 = ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->left(__pyx_v_self->root, __pyx_v_position, 0, &__pyx_t_6); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 456, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":449
+ * return self.root.find( start, end )
+ *
+ * def before( self, position, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie before `position` and are no
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.before", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":458
+ * return self.root.left( position, num_intervals, max_dist )
+ *
+ * def after( self, position, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie after `position` and are no
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_11after(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_10after[] = "\n Find `num_intervals` intervals that lie after `position` and are no\n further than `max_dist` positions away\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_11after(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_position = 0;
+ PyObject *__pyx_v_num_intervals = 0;
+ PyObject *__pyx_v_max_dist = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("after (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_position,&__pyx_n_s_num_intervals,&__pyx_n_s_max_dist,0};
+ PyObject* values[3] = {0,0,0};
+ values[1] = ((PyObject *)__pyx_int_1);
+ values[2] = ((PyObject *)__pyx_int_2500);
+ if (unlikely(__pyx_kwds)) {
+ Py_ssize_t kw_args;
+ const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+ switch (pos_args) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_position)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_num_intervals);
+ if (value) { values[1] = value; kw_args--; }
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_max_dist);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "after") < 0)) __PYX_ERR(0, 458, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_position = values[0];
+ __pyx_v_num_intervals = values[1];
+ __pyx_v_max_dist = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("after", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 458, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.after", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_10after(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), __pyx_v_position, __pyx_v_num_intervals, __pyx_v_max_dist);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_10after(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_position, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_t_4;
+ int __pyx_t_5;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right __pyx_t_6;
+ __Pyx_RefNannySetupContext("after", 0);
+
+ /* "skbio/metadata/_intersection.pyx":463
+ * further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.right( position, num_intervals, max_dist )
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) == Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":464
+ * """
+ * if self.root is None:
+ * return [] # <<<<<<<<<<<<<<
+ * return self.root.right( position, num_intervals, max_dist )
+ *
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 464, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":463
+ * further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.right( position, num_intervals, max_dist )
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":465
+ * if self.root is None:
+ * return []
+ * return self.root.right( position, num_intervals, max_dist ) # <<<<<<<<<<<<<<
+ *
+ * # ---- Interval-like object based interfaces -----------------------------
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_4 = __Pyx_PyInt_As_int(__pyx_v_num_intervals); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 465, __pyx_L1_error)
+ __pyx_t_5 = __Pyx_PyInt_As_int(__pyx_v_max_dist); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 465, __pyx_L1_error)
+ __pyx_t_6.__pyx_n = 2;
+ __pyx_t_6.n = __pyx_t_4;
+ __pyx_t_6.max_dist = __pyx_t_5;
+ __pyx_t_3 = ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->right(__pyx_v_self->root, __pyx_v_position, 0, &__pyx_t_6); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 465, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":458
+ * return self.root.left( position, num_intervals, max_dist )
+ *
+ * def after( self, position, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie after `position` and are no
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.after", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":469
+ * # ---- Interval-like object based interfaces -----------------------------
+ *
+ * def insert_interval( self, interval ): # <<<<<<<<<<<<<<
+ * """
+ * Insert an "interval" like object (one with at least start and end
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_13insert_interval(PyObject *__pyx_v_self, PyObject *__pyx_v_interval); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_12insert_interval[] = "\n Insert an \"interval\" like object (one with at least start and end\n attributes)\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_13insert_interval(PyObject *__pyx_v_self, PyObject *__pyx_v_interval) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("insert_interval (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_12insert_interval(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), ((PyObject *)__pyx_v_interval));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_12insert_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ PyObject *__pyx_t_1 = NULL;
+ PyObject *__pyx_t_2 = NULL;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ Py_ssize_t __pyx_t_6;
+ PyObject *__pyx_t_7 = NULL;
+ __Pyx_RefNannySetupContext("insert_interval", 0);
+
+ /* "skbio/metadata/_intersection.pyx":474
+ * attributes)
+ * """
+ * self.insert( interval.start, interval.end, interval ) # <<<<<<<<<<<<<<
+ *
+ * add_interval = insert_interval
+ */
+ __pyx_t_2 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self), __pyx_n_s_insert); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 474, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_2);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 474, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_end); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 474, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = NULL;
+ __pyx_t_6 = 0;
+ if (CYTHON_COMPILING_IN_CPYTHON && 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_6 = 1;
+ }
+ }
+ __pyx_t_7 = PyTuple_New(3+__pyx_t_6); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 474, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_7);
+ if (__pyx_t_5) {
+ __Pyx_GIVEREF(__pyx_t_5); PyTuple_SET_ITEM(__pyx_t_7, 0, __pyx_t_5); __pyx_t_5 = NULL;
+ }
+ __Pyx_GIVEREF(__pyx_t_3);
+ PyTuple_SET_ITEM(__pyx_t_7, 0+__pyx_t_6, __pyx_t_3);
+ __Pyx_GIVEREF(__pyx_t_4);
+ PyTuple_SET_ITEM(__pyx_t_7, 1+__pyx_t_6, __pyx_t_4);
+ __Pyx_INCREF(__pyx_v_interval);
+ __Pyx_GIVEREF(__pyx_v_interval);
+ PyTuple_SET_ITEM(__pyx_t_7, 2+__pyx_t_6, __pyx_v_interval);
+ __pyx_t_3 = 0;
+ __pyx_t_4 = 0;
+ __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_t_7, NULL); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 474, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
+ __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":469
+ * # ---- Interval-like object based interfaces -----------------------------
+ *
+ * def insert_interval( self, interval ): # <<<<<<<<<<<<<<
+ * """
+ * Insert an "interval" like object (one with at least start and end
+ */
+
+ /* function exit code */
+ __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_2);
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_7);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.insert_interval", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":478
+ * add_interval = insert_interval
+ *
+ * def before_interval( self, interval, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie completely before `interval`
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_15before_interval(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_14before_interval[] = "\n Find `num_intervals` intervals that lie completely before `interval`\n and are no further than `max_dist` positions away\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_15before_interval(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_interval = 0;
+ PyObject *__pyx_v_num_intervals = 0;
+ PyObject *__pyx_v_max_dist = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("before_interval (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_interval,&__pyx_n_s_num_intervals,&__pyx_n_s_max_dist,0};
+ PyObject* values[3] = {0,0,0};
+ values[1] = ((PyObject *)__pyx_int_1);
+ values[2] = ((PyObject *)__pyx_int_2500);
+ if (unlikely(__pyx_kwds)) {
+ Py_ssize_t kw_args;
+ const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+ switch (pos_args) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_interval)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_num_intervals);
+ if (value) { values[1] = value; kw_args--; }
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_max_dist);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "before_interval") < 0)) __PYX_ERR(0, 478, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_interval = values[0];
+ __pyx_v_num_intervals = values[1];
+ __pyx_v_max_dist = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("before_interval", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 478, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.before_interval", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_14before_interval(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), __pyx_v_interval, __pyx_v_num_intervals, __pyx_v_max_dist);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_14before_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_t_4;
+ int __pyx_t_5;
+ PyObject *__pyx_t_6 = NULL;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left __pyx_t_7;
+ __Pyx_RefNannySetupContext("before_interval", 0);
+
+ /* "skbio/metadata/_intersection.pyx":483
+ * and are no further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) == Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":484
+ * """
+ * if self.root is None:
+ * return [] # <<<<<<<<<<<<<<
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ *
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 484, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":483
+ * and are no further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":485
+ * if self.root is None:
+ * return []
+ * return self.root.left( interval.start, num_intervals, max_dist ) # <<<<<<<<<<<<<<
+ *
+ * def after_interval( self, interval, num_intervals=1, max_dist=2500 ):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 485, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyInt_As_int(__pyx_v_num_intervals); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 485, __pyx_L1_error)
+ __pyx_t_5 = __Pyx_PyInt_As_int(__pyx_v_max_dist); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 485, __pyx_L1_error)
+ __pyx_t_7.__pyx_n = 2;
+ __pyx_t_7.n = __pyx_t_4;
+ __pyx_t_7.max_dist = __pyx_t_5;
+ __pyx_t_6 = ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->left(__pyx_v_self->root, __pyx_t_3, 0, &__pyx_t_7); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 485, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_6;
+ __pyx_t_6 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":478
+ * add_interval = insert_interval
+ *
+ * def before_interval( self, interval, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie completely before `interval`
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.before_interval", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":487
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ *
+ * def after_interval( self, interval, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie completely after `interval` and
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_17after_interval(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_16after_interval[] = "\n Find `num_intervals` intervals that lie completely after `interval` and\n are no further than `max_dist` positions away\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_17after_interval(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_interval = 0;
+ PyObject *__pyx_v_num_intervals = 0;
+ PyObject *__pyx_v_max_dist = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("after_interval (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_interval,&__pyx_n_s_num_intervals,&__pyx_n_s_max_dist,0};
+ PyObject* values[3] = {0,0,0};
+ values[1] = ((PyObject *)__pyx_int_1);
+ values[2] = ((PyObject *)__pyx_int_2500);
+ if (unlikely(__pyx_kwds)) {
+ Py_ssize_t kw_args;
+ const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+ switch (pos_args) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_interval)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_num_intervals);
+ if (value) { values[1] = value; kw_args--; }
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_max_dist);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "after_interval") < 0)) __PYX_ERR(0, 487, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_interval = values[0];
+ __pyx_v_num_intervals = values[1];
+ __pyx_v_max_dist = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("after_interval", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 487, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.after_interval", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_16after_interval(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), __pyx_v_interval, __pyx_v_num_intervals, __pyx_v_max_dist);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_16after_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ int __pyx_t_4;
+ int __pyx_t_5;
+ PyObject *__pyx_t_6 = NULL;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right __pyx_t_7;
+ __Pyx_RefNannySetupContext("after_interval", 0);
+
+ /* "skbio/metadata/_intersection.pyx":492
+ * are no further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) == Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":493
+ * """
+ * if self.root is None:
+ * return [] # <<<<<<<<<<<<<<
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ *
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 493, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":492
+ * are no further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":494
+ * if self.root is None:
+ * return []
+ * return self.root.right( interval.end, num_intervals, max_dist ) # <<<<<<<<<<<<<<
+ *
+ * def upstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ):
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_end); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 494, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyInt_As_int(__pyx_v_num_intervals); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 494, __pyx_L1_error)
+ __pyx_t_5 = __Pyx_PyInt_As_int(__pyx_v_max_dist); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 494, __pyx_L1_error)
+ __pyx_t_7.__pyx_n = 2;
+ __pyx_t_7.n = __pyx_t_4;
+ __pyx_t_7.max_dist = __pyx_t_5;
+ __pyx_t_6 = ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->right(__pyx_v_self->root, __pyx_t_3, 0, &__pyx_t_7); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 494, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_6;
+ __pyx_t_6 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":487
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ *
+ * def after_interval( self, interval, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie completely after `interval` and
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.after_interval", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":496
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ *
+ * def upstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie completely upstream of
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_19upstream_of_interval(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_18upstream_of_interval[] = "\n Find `num_intervals` intervals that lie completely upstream of\n `interval` and are no further than `max_dist` positions away\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_19upstream_of_interval(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_interval = 0;
+ PyObject *__pyx_v_num_intervals = 0;
+ PyObject *__pyx_v_max_dist = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("upstream_of_interval (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_interval,&__pyx_n_s_num_intervals,&__pyx_n_s_max_dist,0};
+ PyObject* values[3] = {0,0,0};
+ values[1] = ((PyObject *)__pyx_int_1);
+ values[2] = ((PyObject *)__pyx_int_2500);
+ if (unlikely(__pyx_kwds)) {
+ Py_ssize_t kw_args;
+ const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+ switch (pos_args) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_interval)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_num_intervals);
+ if (value) { values[1] = value; kw_args--; }
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_max_dist);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "upstream_of_interval") < 0)) __PYX_ERR(0, 496, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_interval = values[0];
+ __pyx_v_num_intervals = values[1];
+ __pyx_v_max_dist = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("upstream_of_interval", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 496, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.upstream_of_interval", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_18upstream_of_interval(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), __pyx_v_interval, __pyx_v_num_intervals, __pyx_v_max_dist);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_18upstream_of_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_t_5;
+ int __pyx_t_6;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right __pyx_t_7;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left __pyx_t_8;
+ __Pyx_RefNannySetupContext("upstream_of_interval", 0);
+
+ /* "skbio/metadata/_intersection.pyx":501
+ * `interval` and are no further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * if interval.strand == -1 or interval.strand == "-":
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) == Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":502
+ * """
+ * if self.root is None:
+ * return [] # <<<<<<<<<<<<<<
+ * if interval.strand == -1 or interval.strand == "-":
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 502, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":501
+ * `interval` and are no further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * if interval.strand == -1 or interval.strand == "-":
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":503
+ * if self.root is None:
+ * return []
+ * if interval.strand == -1 or interval.strand == "-": # <<<<<<<<<<<<<<
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ * else:
+ */
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_strand); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 503, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyInt_EqObjC(__pyx_t_3, __pyx_int_neg_1, -1L, 0); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 503, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_1 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely(__pyx_t_1 < 0)) __PYX_ERR(0, 503, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (!__pyx_t_1) {
+ } else {
+ __pyx_t_2 = __pyx_t_1;
+ goto __pyx_L5_bool_binop_done;
+ }
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_strand); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 503, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_1 = (__Pyx_PyString_Equals(__pyx_t_4, __pyx_kp_s__4, Py_EQ)); if (unlikely(__pyx_t_1 < 0)) __PYX_ERR(0, 503, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_2 = __pyx_t_1;
+ __pyx_L5_bool_binop_done:;
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":504
+ * return []
+ * if interval.strand == -1 or interval.strand == "-":
+ * return self.root.right( interval.end, num_intervals, max_dist ) # <<<<<<<<<<<<<<
+ * else:
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_end); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 504, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyInt_As_int(__pyx_v_num_intervals); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 504, __pyx_L1_error)
+ __pyx_t_6 = __Pyx_PyInt_As_int(__pyx_v_max_dist); if (unlikely((__pyx_t_6 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 504, __pyx_L1_error)
+ __pyx_t_7.__pyx_n = 2;
+ __pyx_t_7.n = __pyx_t_5;
+ __pyx_t_7.max_dist = __pyx_t_6;
+ __pyx_t_3 = ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->right(__pyx_v_self->root, __pyx_t_4, 0, &__pyx_t_7); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 504, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":503
+ * if self.root is None:
+ * return []
+ * if interval.strand == -1 or interval.strand == "-": # <<<<<<<<<<<<<<
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ * else:
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":506
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ * else:
+ * return self.root.left( interval.start, num_intervals, max_dist ) # <<<<<<<<<<<<<<
+ *
+ * def downstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ):
+ */
+ /*else*/ {
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_start); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 506, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = __Pyx_PyInt_As_int(__pyx_v_num_intervals); if (unlikely((__pyx_t_6 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 506, __pyx_L1_error)
+ __pyx_t_5 = __Pyx_PyInt_As_int(__pyx_v_max_dist); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 506, __pyx_L1_error)
+ __pyx_t_8.__pyx_n = 2;
+ __pyx_t_8.n = __pyx_t_6;
+ __pyx_t_8.max_dist = __pyx_t_5;
+ __pyx_t_4 = ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->left(__pyx_v_self->root, __pyx_t_3, 0, &__pyx_t_8); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 506, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_4;
+ __pyx_t_4 = 0;
+ goto __pyx_L0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":496
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ *
+ * def upstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie completely upstream of
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.upstream_of_interval", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":508
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ *
+ * def downstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie completely downstream of
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_21downstream_of_interval(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_20downstream_of_interval[] = "\n Find `num_intervals` intervals that lie completely downstream of\n `interval` and are no further than `max_dist` positions away\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_21downstream_of_interval(PyObject *__pyx_v_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+ PyObject *__pyx_v_interval = 0;
+ PyObject *__pyx_v_num_intervals = 0;
+ PyObject *__pyx_v_max_dist = 0;
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("downstream_of_interval (wrapper)", 0);
+ {
+ static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_interval,&__pyx_n_s_num_intervals,&__pyx_n_s_max_dist,0};
+ PyObject* values[3] = {0,0,0};
+ values[1] = ((PyObject *)__pyx_int_1);
+ values[2] = ((PyObject *)__pyx_int_2500);
+ if (unlikely(__pyx_kwds)) {
+ Py_ssize_t kw_args;
+ const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+ switch (pos_args) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ case 0: break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ kw_args = PyDict_Size(__pyx_kwds);
+ switch (pos_args) {
+ case 0:
+ if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_interval)) != 0)) kw_args--;
+ else goto __pyx_L5_argtuple_error;
+ case 1:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_num_intervals);
+ if (value) { values[1] = value; kw_args--; }
+ }
+ case 2:
+ if (kw_args > 0) {
+ PyObject* value = PyDict_GetItem(__pyx_kwds, __pyx_n_s_max_dist);
+ if (value) { values[2] = value; kw_args--; }
+ }
+ }
+ if (unlikely(kw_args > 0)) {
+ if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "downstream_of_interval") < 0)) __PYX_ERR(0, 508, __pyx_L3_error)
+ }
+ } else {
+ switch (PyTuple_GET_SIZE(__pyx_args)) {
+ case 3: values[2] = PyTuple_GET_ITEM(__pyx_args, 2);
+ case 2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+ case 1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+ break;
+ default: goto __pyx_L5_argtuple_error;
+ }
+ }
+ __pyx_v_interval = values[0];
+ __pyx_v_num_intervals = values[1];
+ __pyx_v_max_dist = values[2];
+ }
+ goto __pyx_L4_argument_unpacking_done;
+ __pyx_L5_argtuple_error:;
+ __Pyx_RaiseArgtupleInvalid("downstream_of_interval", 0, 1, 3, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 508, __pyx_L3_error)
+ __pyx_L3_error:;
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.downstream_of_interval", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __Pyx_RefNannyFinishContext();
+ return NULL;
+ __pyx_L4_argument_unpacking_done:;
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_20downstream_of_interval(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), __pyx_v_interval, __pyx_v_num_intervals, __pyx_v_max_dist);
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_20downstream_of_interval(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_interval, PyObject *__pyx_v_num_intervals, PyObject *__pyx_v_max_dist) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ int __pyx_t_5;
+ int __pyx_t_6;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left __pyx_t_7;
+ struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right __pyx_t_8;
+ __Pyx_RefNannySetupContext("downstream_of_interval", 0);
+
+ /* "skbio/metadata/_intersection.pyx":513
+ * `interval` and are no further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * if interval.strand == -1 or interval.strand == "-":
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) == Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":514
+ * """
+ * if self.root is None:
+ * return [] # <<<<<<<<<<<<<<
+ * if interval.strand == -1 or interval.strand == "-":
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = PyList_New(0); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 514, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":513
+ * `interval` and are no further than `max_dist` positions away
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return []
+ * if interval.strand == -1 or interval.strand == "-":
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":515
+ * if self.root is None:
+ * return []
+ * if interval.strand == -1 or interval.strand == "-": # <<<<<<<<<<<<<<
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ * else:
+ */
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_strand); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 515, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_4 = __Pyx_PyInt_EqObjC(__pyx_t_3, __pyx_int_neg_1, -1L, 0); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 515, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_t_1 = __Pyx_PyObject_IsTrue(__pyx_t_4); if (unlikely(__pyx_t_1 < 0)) __PYX_ERR(0, 515, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ if (!__pyx_t_1) {
+ } else {
+ __pyx_t_2 = __pyx_t_1;
+ goto __pyx_L5_bool_binop_done;
+ }
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_strand); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 515, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_1 = (__Pyx_PyString_Equals(__pyx_t_4, __pyx_kp_s__4, Py_EQ)); if (unlikely(__pyx_t_1 < 0)) __PYX_ERR(0, 515, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_t_2 = __pyx_t_1;
+ __pyx_L5_bool_binop_done:;
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":516
+ * return []
+ * if interval.strand == -1 or interval.strand == "-":
+ * return self.root.left( interval.start, num_intervals, max_dist ) # <<<<<<<<<<<<<<
+ * else:
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_start); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 516, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = __Pyx_PyInt_As_int(__pyx_v_num_intervals); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 516, __pyx_L1_error)
+ __pyx_t_6 = __Pyx_PyInt_As_int(__pyx_v_max_dist); if (unlikely((__pyx_t_6 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 516, __pyx_L1_error)
+ __pyx_t_7.__pyx_n = 2;
+ __pyx_t_7.n = __pyx_t_5;
+ __pyx_t_7.max_dist = __pyx_t_6;
+ __pyx_t_3 = ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->left(__pyx_v_self->root, __pyx_t_4, 0, &__pyx_t_7); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 516, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":515
+ * if self.root is None:
+ * return []
+ * if interval.strand == -1 or interval.strand == "-": # <<<<<<<<<<<<<<
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ * else:
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":518
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ * else:
+ * return self.root.right( interval.end, num_intervals, max_dist ) # <<<<<<<<<<<<<<
+ *
+ * def traverse(self, fn):
+ */
+ /*else*/ {
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_v_interval, __pyx_n_s_end); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 518, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __pyx_t_6 = __Pyx_PyInt_As_int(__pyx_v_num_intervals); if (unlikely((__pyx_t_6 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 518, __pyx_L1_error)
+ __pyx_t_5 = __Pyx_PyInt_As_int(__pyx_v_max_dist); if (unlikely((__pyx_t_5 == (int)-1) && PyErr_Occurred())) __PYX_ERR(0, 518, __pyx_L1_error)
+ __pyx_t_8.__pyx_n = 2;
+ __pyx_t_8.n = __pyx_t_6;
+ __pyx_t_8.max_dist = __pyx_t_5;
+ __pyx_t_4 = ((struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_v_self->root->__pyx_vtab)->right(__pyx_v_self->root, __pyx_t_3, 0, &__pyx_t_8); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 518, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __pyx_r = __pyx_t_4;
+ __pyx_t_4 = 0;
+ goto __pyx_L0;
+ }
+
+ /* "skbio/metadata/_intersection.pyx":508
+ * return self.root.left( interval.start, num_intervals, max_dist )
+ *
+ * def downstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ): # <<<<<<<<<<<<<<
+ * """
+ * Find `num_intervals` intervals that lie completely downstream of
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.downstream_of_interval", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+/* "skbio/metadata/_intersection.pyx":520
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ *
+ * def traverse(self, fn): # <<<<<<<<<<<<<<
+ * """
+ * call fn for each element in the tree
+ */
+
+/* Python wrapper */
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_23traverse(PyObject *__pyx_v_self, PyObject *__pyx_v_fn); /*proto*/
+static char __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_22traverse[] = "\n call fn for each element in the tree\n ";
+static PyObject *__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_23traverse(PyObject *__pyx_v_self, PyObject *__pyx_v_fn) {
+ PyObject *__pyx_r = 0;
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("traverse (wrapper)", 0);
+ __pyx_r = __pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_22traverse(((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)__pyx_v_self), ((PyObject *)__pyx_v_fn));
+
+ /* function exit code */
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+
+static PyObject *__pyx_pf_5skbio_8metadata_13_intersection_12IntervalTree_22traverse(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *__pyx_v_self, PyObject *__pyx_v_fn) {
+ PyObject *__pyx_r = NULL;
+ __Pyx_RefNannyDeclarations
+ int __pyx_t_1;
+ int __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ PyObject *__pyx_t_4 = NULL;
+ PyObject *__pyx_t_5 = NULL;
+ PyObject *__pyx_t_6 = NULL;
+ __Pyx_RefNannySetupContext("traverse", 0);
+
+ /* "skbio/metadata/_intersection.pyx":524
+ * call fn for each element in the tree
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return None
+ * return self.root.traverse(fn)
+ */
+ __pyx_t_1 = (((PyObject *)__pyx_v_self->root) == Py_None);
+ __pyx_t_2 = (__pyx_t_1 != 0);
+ if (__pyx_t_2) {
+
+ /* "skbio/metadata/_intersection.pyx":525
+ * """
+ * if self.root is None:
+ * return None # <<<<<<<<<<<<<<
+ * return self.root.traverse(fn)
+ *
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __Pyx_INCREF(Py_None);
+ __pyx_r = Py_None;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":524
+ * call fn for each element in the tree
+ * """
+ * if self.root is None: # <<<<<<<<<<<<<<
+ * return None
+ * return self.root.traverse(fn)
+ */
+ }
+
+ /* "skbio/metadata/_intersection.pyx":526
+ * if self.root is None:
+ * return None
+ * return self.root.traverse(fn) # <<<<<<<<<<<<<<
+ *
+ * # For backward compatibility
+ */
+ __Pyx_XDECREF(__pyx_r);
+ __pyx_t_4 = __Pyx_PyObject_GetAttrStr(((PyObject *)__pyx_v_self->root), __pyx_n_s_traverse); if (unlikely(!__pyx_t_4)) __PYX_ERR(0, 526, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_4);
+ __pyx_t_5 = NULL;
+ if (CYTHON_COMPILING_IN_CPYTHON && likely(PyMethod_Check(__pyx_t_4))) {
+ __pyx_t_5 = PyMethod_GET_SELF(__pyx_t_4);
+ if (likely(__pyx_t_5)) {
+ PyObject* function = PyMethod_GET_FUNCTION(__pyx_t_4);
+ __Pyx_INCREF(__pyx_t_5);
+ __Pyx_INCREF(function);
+ __Pyx_DECREF_SET(__pyx_t_4, function);
+ }
+ }
+ if (!__pyx_t_5) {
+ __pyx_t_3 = __Pyx_PyObject_CallOneArg(__pyx_t_4, __pyx_v_fn); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 526, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ } else {
+ __pyx_t_6 = PyTuple_New(1+1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 526, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_6);
+ __Pyx_GIVEREF(__pyx_t_5); PyTuple_SET_ITEM(__pyx_t_6, 0, __pyx_t_5); __pyx_t_5 = NULL;
+ __Pyx_INCREF(__pyx_v_fn);
+ __Pyx_GIVEREF(__pyx_v_fn);
+ PyTuple_SET_ITEM(__pyx_t_6, 0+1, __pyx_v_fn);
+ __pyx_t_3 = __Pyx_PyObject_Call(__pyx_t_4, __pyx_t_6, NULL); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 526, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
+ }
+ __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
+ __pyx_r = __pyx_t_3;
+ __pyx_t_3 = 0;
+ goto __pyx_L0;
+
+ /* "skbio/metadata/_intersection.pyx":520
+ * return self.root.right( interval.end, num_intervals, max_dist )
+ *
+ * def traverse(self, fn): # <<<<<<<<<<<<<<
+ * """
+ * call fn for each element in the tree
+ */
+
+ /* function exit code */
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_3);
+ __Pyx_XDECREF(__pyx_t_4);
+ __Pyx_XDECREF(__pyx_t_5);
+ __Pyx_XDECREF(__pyx_t_6);
+ __Pyx_AddTraceback("skbio.metadata._intersection.IntervalTree.traverse", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ __pyx_r = NULL;
+ __pyx_L0:;
+ __Pyx_XGIVEREF(__pyx_r);
+ __Pyx_RefNannyFinishContext();
+ return __pyx_r;
+}
+static struct __pyx_vtabstruct_5skbio_8metadata_13_intersection_IntervalNode __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode;
+
+static PyObject *__pyx_tp_new_5skbio_8metadata_13_intersection_IntervalNode(PyTypeObject *t, PyObject *a, PyObject *k) {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *p;
+ PyObject *o;
+ if (likely((t->tp_flags & Py_TPFLAGS_IS_ABSTRACT) == 0)) {
+ o = (*t->tp_alloc)(t, 0);
+ } else {
+ o = (PyObject *) PyBaseObject_Type.tp_new(t, __pyx_empty_tuple, 0);
+ }
+ if (unlikely(!o)) return 0;
+ p = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)o);
+ p->__pyx_vtab = __pyx_vtabptr_5skbio_8metadata_13_intersection_IntervalNode;
+ p->interval = Py_None; Py_INCREF(Py_None);
+ p->cleft = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)Py_None); Py_INCREF(Py_None);
+ p->cright = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)Py_None); Py_INCREF(Py_None);
+ p->croot = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)Py_None); Py_INCREF(Py_None);
+ if (unlikely(__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_3__cinit__(o, a, k) < 0)) {
+ Py_DECREF(o); o = 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_5skbio_8metadata_13_intersection_IntervalNode(PyObject *o) {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *p = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)o;
+ #if PY_VERSION_HEX >= 0x030400a1
+ if (unlikely(Py_TYPE(o)->tp_finalize) && !_PyGC_FINALIZED(o)) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->interval);
+ Py_CLEAR(p->cleft);
+ Py_CLEAR(p->cright);
+ Py_CLEAR(p->croot);
+ (*Py_TYPE(o)->tp_free)(o);
+}
+
+static int __pyx_tp_traverse_5skbio_8metadata_13_intersection_IntervalNode(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *p = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)o;
+ if (p->interval) {
+ e = (*v)(p->interval, a); if (e) return e;
+ }
+ if (p->cleft) {
+ e = (*v)(((PyObject*)p->cleft), a); if (e) return e;
+ }
+ if (p->cright) {
+ e = (*v)(((PyObject*)p->cright), a); if (e) return e;
+ }
+ if (p->croot) {
+ e = (*v)(((PyObject*)p->croot), a); if (e) return e;
+ }
+ return 0;
+}
+
+static int __pyx_tp_clear_5skbio_8metadata_13_intersection_IntervalNode(PyObject *o) {
+ PyObject* tmp;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *p = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)o;
+ tmp = ((PyObject*)p->interval);
+ p->interval = Py_None; Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ tmp = ((PyObject*)p->cleft);
+ p->cleft = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)Py_None); Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ tmp = ((PyObject*)p->cright);
+ p->cright = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)Py_None); Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ tmp = ((PyObject*)p->croot);
+ p->croot = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)Py_None); Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ return 0;
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_left_node(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9left_node_1__get__(o);
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_right_node(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_10right_node_1__get__(o);
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_root_node(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9root_node_1__get__(o);
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_interval(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_8interval_1__get__(o);
+}
+
+static int __pyx_setprop_5skbio_8metadata_13_intersection_12IntervalNode_interval(PyObject *o, PyObject *v, CYTHON_UNUSED void *x) {
+ if (v) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_8interval_3__set__(o, v);
+ }
+ else {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_8interval_5__del__(o);
+ }
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_start(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5start_1__get__(o);
+}
+
+static int __pyx_setprop_5skbio_8metadata_13_intersection_12IntervalNode_start(PyObject *o, PyObject *v, CYTHON_UNUSED void *x) {
+ if (v) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5start_3__set__(o, v);
+ }
+ else {
+ PyErr_SetString(PyExc_NotImplementedError, "__del__");
+ return -1;
+ }
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_end(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_3end_1__get__(o);
+}
+
+static int __pyx_setprop_5skbio_8metadata_13_intersection_12IntervalNode_end(PyObject *o, PyObject *v, CYTHON_UNUSED void *x) {
+ if (v) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_3end_3__set__(o, v);
+ }
+ else {
+ PyErr_SetString(PyExc_NotImplementedError, "__del__");
+ return -1;
+ }
+}
+
+static PyMethodDef __pyx_methods_5skbio_8metadata_13_intersection_IntervalNode[] = {
+ {"insert", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_5insert, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_4insert},
+ {"intersect", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_7intersect, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_6intersect},
+ {"update", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_9update, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_8update},
+ {"left", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_11left, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_10left},
+ {"right", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_13right, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalNode_12right},
+ {"traverse", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_15traverse, METH_O, 0},
+ {0, 0, 0, 0}
+};
+
+static struct PyGetSetDef __pyx_getsets_5skbio_8metadata_13_intersection_IntervalNode[] = {
+ {(char *)"left_node", __pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_left_node, 0, (char *)0, 0},
+ {(char *)"right_node", __pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_right_node, 0, (char *)0, 0},
+ {(char *)"root_node", __pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_root_node, 0, (char *)0, 0},
+ {(char *)"interval", __pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_interval, __pyx_setprop_5skbio_8metadata_13_intersection_12IntervalNode_interval, (char *)0, 0},
+ {(char *)"start", __pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_start, __pyx_setprop_5skbio_8metadata_13_intersection_12IntervalNode_start, (char *)0, 0},
+ {(char *)"end", __pyx_getprop_5skbio_8metadata_13_intersection_12IntervalNode_end, __pyx_setprop_5skbio_8metadata_13_intersection_12IntervalNode_end, (char *)0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static PyTypeObject __pyx_type_5skbio_8metadata_13_intersection_IntervalNode = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "skbio.metadata._intersection.IntervalNode", /*tp_name*/
+ sizeof(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_5skbio_8metadata_13_intersection_IntervalNode, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ #if PY_MAJOR_VERSION < 3
+ 0, /*tp_compare*/
+ #endif
+ #if PY_MAJOR_VERSION >= 3
+ 0, /*tp_as_async*/
+ #endif
+ __pyx_pw_5skbio_8metadata_13_intersection_12IntervalNode_1__repr__, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ "\n A single node of an `IntervalTree`.\n\n NOTE: Unless you really know what you are doing, you probably should us\n `IntervalTree` rather than using this directly.\n ", /*tp_doc*/
+ __pyx_tp_traverse_5skbio_8metadata_13_intersection_IntervalNode, /*tp_traverse*/
+ __pyx_tp_clear_5skbio_8metadata_13_intersection_IntervalNode, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ __pyx_methods_5skbio_8metadata_13_intersection_IntervalNode, /*tp_methods*/
+ 0, /*tp_members*/
+ __pyx_getsets_5skbio_8metadata_13_intersection_IntervalNode, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_5skbio_8metadata_13_intersection_IntervalNode, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if PY_VERSION_HEX >= 0x030400a1
+ 0, /*tp_finalize*/
+ #endif
+};
+
+static PyObject *__pyx_tp_new_5skbio_8metadata_13_intersection_IntervalObj(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *p;
+ PyObject *o;
+ if (likely((t->tp_flags & Py_TPFLAGS_IS_ABSTRACT) == 0)) {
+ o = (*t->tp_alloc)(t, 0);
+ } else {
+ o = (PyObject *) PyBaseObject_Type.tp_new(t, __pyx_empty_tuple, 0);
+ }
+ if (unlikely(!o)) return 0;
+ p = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)o);
+ p->value = Py_None; Py_INCREF(Py_None);
+ p->chrom = Py_None; Py_INCREF(Py_None);
+ p->strand = Py_None; Py_INCREF(Py_None);
+ return o;
+}
+
+static void __pyx_tp_dealloc_5skbio_8metadata_13_intersection_IntervalObj(PyObject *o) {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *p = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)o;
+ #if PY_VERSION_HEX >= 0x030400a1
+ if (unlikely(Py_TYPE(o)->tp_finalize) && !_PyGC_FINALIZED(o)) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->value);
+ Py_CLEAR(p->chrom);
+ Py_CLEAR(p->strand);
+ (*Py_TYPE(o)->tp_free)(o);
+}
+
+static int __pyx_tp_traverse_5skbio_8metadata_13_intersection_IntervalObj(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *p = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)o;
+ if (p->value) {
+ e = (*v)(p->value, a); if (e) return e;
+ }
+ if (p->chrom) {
+ e = (*v)(p->chrom, a); if (e) return e;
+ }
+ if (p->strand) {
+ e = (*v)(p->strand, a); if (e) return e;
+ }
+ return 0;
+}
+
+static int __pyx_tp_clear_5skbio_8metadata_13_intersection_IntervalObj(PyObject *o) {
+ PyObject* tmp;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *p = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj *)o;
+ tmp = ((PyObject*)p->value);
+ p->value = Py_None; Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ tmp = ((PyObject*)p->chrom);
+ p->chrom = Py_None; Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ tmp = ((PyObject*)p->strand);
+ p->strand = Py_None; Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ return 0;
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_start(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5start_1__get__(o);
+}
+
+static int __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_start(PyObject *o, PyObject *v, CYTHON_UNUSED void *x) {
+ if (v) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5start_3__set__(o, v);
+ }
+ else {
+ PyErr_SetString(PyExc_NotImplementedError, "__del__");
+ return -1;
+ }
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_end(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_3end_1__get__(o);
+}
+
+static int __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_end(PyObject *o, PyObject *v, CYTHON_UNUSED void *x) {
+ if (v) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_3end_3__set__(o, v);
+ }
+ else {
+ PyErr_SetString(PyExc_NotImplementedError, "__del__");
+ return -1;
+ }
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_value(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5value_1__get__(o);
+}
+
+static int __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_value(PyObject *o, PyObject *v, CYTHON_UNUSED void *x) {
+ if (v) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5value_3__set__(o, v);
+ }
+ else {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5value_5__del__(o);
+ }
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_chrom(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_1__get__(o);
+}
+
+static int __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_chrom(PyObject *o, PyObject *v, CYTHON_UNUSED void *x) {
+ if (v) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_3__set__(o, v);
+ }
+ else {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5chrom_5__del__(o);
+ }
+}
+
+static PyObject *__pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_strand(PyObject *o, CYTHON_UNUSED void *x) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_6strand_1__get__(o);
+}
+
+static int __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_strand(PyObject *o, PyObject *v, CYTHON_UNUSED void *x) {
+ if (v) {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_6strand_3__set__(o, v);
+ }
+ else {
+ return __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_6strand_5__del__(o);
+ }
+}
+
+static PyMethodDef __pyx_methods_5skbio_8metadata_13_intersection_IntervalObj[] = {
+ {0, 0, 0, 0}
+};
+
+static struct PyGetSetDef __pyx_getsets_5skbio_8metadata_13_intersection_IntervalObj[] = {
+ {(char *)"start", __pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_start, __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_start, (char *)0, 0},
+ {(char *)"end", __pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_end, __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_end, (char *)0, 0},
+ {(char *)"value", __pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_value, __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_value, (char *)0, 0},
+ {(char *)"chrom", __pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_chrom, __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_chrom, (char *)0, 0},
+ {(char *)"strand", __pyx_getprop_5skbio_8metadata_13_intersection_11IntervalObj_strand, __pyx_setprop_5skbio_8metadata_13_intersection_11IntervalObj_strand, (char *)0, 0},
+ {0, 0, 0, 0, 0}
+};
+
+static PyTypeObject __pyx_type_5skbio_8metadata_13_intersection_IntervalObj = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "skbio.metadata._intersection.IntervalObj", /*tp_name*/
+ sizeof(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalObj), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_5skbio_8metadata_13_intersection_IntervalObj, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ #if PY_MAJOR_VERSION < 3
+ 0, /*tp_compare*/
+ #endif
+ #if PY_MAJOR_VERSION >= 3
+ 0, /*tp_as_async*/
+ #endif
+ __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_3__repr__, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ "\n Basic feature, with required integer start and end properties.\n Also accepts optional strand as +1 or -1 (used for up/downstream queries),\n a name, and any arbitrary data is sent in on the info keyword argument\n\n >>> from bx.intervals.intersection import Interval\n\n >>> f1 = IntervalObj(23, 36)\n >>> f2 = IntervalObj(34, 48, value={'chr':12, 'anno':'transposon'})\n >>> f2\n IntervalObj(34, 48, value={'anno': 'transposon', 'chr': 12})\n\n ", /*tp_doc*/
+ __pyx_tp_traverse_5skbio_8metadata_13_intersection_IntervalObj, /*tp_traverse*/
+ __pyx_tp_clear_5skbio_8metadata_13_intersection_IntervalObj, /*tp_clear*/
+ __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_5__richcmp__, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ __pyx_methods_5skbio_8metadata_13_intersection_IntervalObj, /*tp_methods*/
+ 0, /*tp_members*/
+ __pyx_getsets_5skbio_8metadata_13_intersection_IntervalObj, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ __pyx_pw_5skbio_8metadata_13_intersection_11IntervalObj_1__init__, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_5skbio_8metadata_13_intersection_IntervalObj, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if PY_VERSION_HEX >= 0x030400a1
+ 0, /*tp_finalize*/
+ #endif
+};
+
+static PyObject *__pyx_tp_new_5skbio_8metadata_13_intersection_IntervalTree(PyTypeObject *t, CYTHON_UNUSED PyObject *a, CYTHON_UNUSED PyObject *k) {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *p;
+ PyObject *o;
+ if (likely((t->tp_flags & Py_TPFLAGS_IS_ABSTRACT) == 0)) {
+ o = (*t->tp_alloc)(t, 0);
+ } else {
+ o = (PyObject *) PyBaseObject_Type.tp_new(t, __pyx_empty_tuple, 0);
+ }
+ if (unlikely(!o)) return 0;
+ p = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)o);
+ p->root = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)Py_None); Py_INCREF(Py_None);
+ if (unlikely(__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_1__cinit__(o, __pyx_empty_tuple, NULL) < 0)) {
+ Py_DECREF(o); o = 0;
+ }
+ return o;
+}
+
+static void __pyx_tp_dealloc_5skbio_8metadata_13_intersection_IntervalTree(PyObject *o) {
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *p = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)o;
+ #if PY_VERSION_HEX >= 0x030400a1
+ if (unlikely(Py_TYPE(o)->tp_finalize) && !_PyGC_FINALIZED(o)) {
+ if (PyObject_CallFinalizerFromDealloc(o)) return;
+ }
+ #endif
+ PyObject_GC_UnTrack(o);
+ Py_CLEAR(p->root);
+ (*Py_TYPE(o)->tp_free)(o);
+}
+
+static int __pyx_tp_traverse_5skbio_8metadata_13_intersection_IntervalTree(PyObject *o, visitproc v, void *a) {
+ int e;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *p = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)o;
+ if (p->root) {
+ e = (*v)(((PyObject*)p->root), a); if (e) return e;
+ }
+ return 0;
+}
+
+static int __pyx_tp_clear_5skbio_8metadata_13_intersection_IntervalTree(PyObject *o) {
+ PyObject* tmp;
+ struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *p = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree *)o;
+ tmp = ((PyObject*)p->root);
+ p->root = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)Py_None); Py_INCREF(Py_None);
+ Py_XDECREF(tmp);
+ return 0;
+}
+
+static PyMethodDef __pyx_methods_5skbio_8metadata_13_intersection_IntervalTree[] = {
+ {"insert", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_3insert, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_2insert},
+ {"update", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_5update, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_4update},
+ {"find", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_7find, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_6find},
+ {"before", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_9before, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_8before},
+ {"after", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_11after, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_10after},
+ {"insert_interval", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_13insert_interval, METH_O, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_12insert_interval},
+ {"before_interval", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_15before_interval, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_14before_interval},
+ {"after_interval", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_17after_interval, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_16after_interval},
+ {"upstream_of_interval", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_19upstream_of_interval, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_18upstream_of_interval},
+ {"downstream_of_interval", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_21downstream_of_interval, METH_VARARGS|METH_KEYWORDS, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_20downstream_of_interval},
+ {"traverse", (PyCFunction)__pyx_pw_5skbio_8metadata_13_intersection_12IntervalTree_23traverse, METH_O, __pyx_doc_5skbio_8metadata_13_intersection_12IntervalTree_22traverse},
+ {0, 0, 0, 0}
+};
+
+static PyTypeObject __pyx_type_5skbio_8metadata_13_intersection_IntervalTree = {
+ PyVarObject_HEAD_INIT(0, 0)
+ "skbio.metadata._intersection.IntervalTree", /*tp_name*/
+ sizeof(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalTree), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ __pyx_tp_dealloc_5skbio_8metadata_13_intersection_IntervalTree, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ #if PY_MAJOR_VERSION < 3
+ 0, /*tp_compare*/
+ #endif
+ #if PY_MAJOR_VERSION >= 3
+ 0, /*tp_as_async*/
+ #endif
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_VERSION_TAG|Py_TPFLAGS_CHECKTYPES|Py_TPFLAGS_HAVE_NEWBUFFER|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
+ "\n Data structure for performing window intersect queries on a set of\n of possibly overlapping 1d intervals.\n\n Usage\n =====\n\n Create an empty IntervalTree\n\n >>> from bx.intervals.intersection import Interval, IntervalTree\n >>> intersecter = IntervalTree()\n\n An interval is a start and end position and a value (possibly None).\n You can add any object as an interval:\n\n >>> intersecter.insert( 0, 10, \"food\" )\n >>> intersecter.insert( 3, 7, [...]
+ __pyx_tp_traverse_5skbio_8metadata_13_intersection_IntervalTree, /*tp_traverse*/
+ __pyx_tp_clear_5skbio_8metadata_13_intersection_IntervalTree, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ __pyx_methods_5skbio_8metadata_13_intersection_IntervalTree, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ __pyx_tp_new_5skbio_8metadata_13_intersection_IntervalTree, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+ 0, /*tp_bases*/
+ 0, /*tp_mro*/
+ 0, /*tp_cache*/
+ 0, /*tp_subclasses*/
+ 0, /*tp_weaklist*/
+ 0, /*tp_del*/
+ 0, /*tp_version_tag*/
+ #if PY_VERSION_HEX >= 0x030400a1
+ 0, /*tp_finalize*/
+ #endif
+};
+
+static PyMethodDef __pyx_methods[] = {
+ {0, 0, 0, 0}
+};
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef __pyx_moduledef = {
+ #if PY_VERSION_HEX < 0x03020000
+ { PyObject_HEAD_INIT(NULL) NULL, 0, NULL },
+ #else
+ PyModuleDef_HEAD_INIT,
+ #endif
+ "_intersection",
+ __pyx_k_Data_structure_for_performing_i, /* m_doc */
+ -1, /* m_size */
+ __pyx_methods /* m_methods */,
+ NULL, /* m_reload */
+ NULL, /* m_traverse */
+ NULL, /* m_clear */
+ NULL /* m_free */
+};
+#endif
+
+static __Pyx_StringTabEntry __pyx_string_tab[] = {
+ {&__pyx_n_s_Intersecter, __pyx_k_Intersecter, sizeof(__pyx_k_Intersecter), 0, 0, 1, 1},
+ {&__pyx_kp_s_IntervalNode_i_i, __pyx_k_IntervalNode_i_i, sizeof(__pyx_k_IntervalNode_i_i), 0, 0, 1, 0},
+ {&__pyx_kp_s_IntervalObj_d_d, __pyx_k_IntervalObj_d_d, sizeof(__pyx_k_IntervalObj_d_d), 0, 0, 1, 0},
+ {&__pyx_kp_s__3, __pyx_k__3, sizeof(__pyx_k__3), 0, 0, 1, 0},
+ {&__pyx_kp_s__4, __pyx_k__4, sizeof(__pyx_k__4), 0, 0, 1, 0},
+ {&__pyx_n_s_add, __pyx_k_add, sizeof(__pyx_k_add), 0, 0, 1, 1},
+ {&__pyx_n_s_add_interval, __pyx_k_add_interval, sizeof(__pyx_k_add_interval), 0, 0, 1, 1},
+ {&__pyx_n_s_attrgetter, __pyx_k_attrgetter, sizeof(__pyx_k_attrgetter), 0, 0, 1, 1},
+ {&__pyx_n_s_chrom, __pyx_k_chrom, sizeof(__pyx_k_chrom), 0, 0, 1, 1},
+ {&__pyx_n_s_end, __pyx_k_end, sizeof(__pyx_k_end), 0, 0, 1, 1},
+ {&__pyx_n_s_find, __pyx_k_find, sizeof(__pyx_k_find), 0, 0, 1, 1},
+ {&__pyx_n_s_import, __pyx_k_import, sizeof(__pyx_k_import), 0, 0, 1, 1},
+ {&__pyx_n_s_insert, __pyx_k_insert, sizeof(__pyx_k_insert), 0, 0, 1, 1},
+ {&__pyx_n_s_insert_interval, __pyx_k_insert_interval, sizeof(__pyx_k_insert_interval), 0, 0, 1, 1},
+ {&__pyx_n_s_intersect, __pyx_k_intersect, sizeof(__pyx_k_intersect), 0, 0, 1, 1},
+ {&__pyx_n_s_interval, __pyx_k_interval, sizeof(__pyx_k_interval), 0, 0, 1, 1},
+ {&__pyx_n_s_key, __pyx_k_key, sizeof(__pyx_k_key), 0, 0, 1, 1},
+ {&__pyx_n_s_left, __pyx_k_left, sizeof(__pyx_k_left), 0, 0, 1, 1},
+ {&__pyx_n_s_main, __pyx_k_main, sizeof(__pyx_k_main), 0, 0, 1, 1},
+ {&__pyx_n_s_max_dist, __pyx_k_max_dist, sizeof(__pyx_k_max_dist), 0, 0, 1, 1},
+ {&__pyx_n_s_n, __pyx_k_n, sizeof(__pyx_k_n), 0, 0, 1, 1},
+ {&__pyx_n_s_new_feature, __pyx_k_new_feature, sizeof(__pyx_k_new_feature), 0, 0, 1, 1},
+ {&__pyx_n_s_num_intervals, __pyx_k_num_intervals, sizeof(__pyx_k_num_intervals), 0, 0, 1, 1},
+ {&__pyx_n_s_old_feature, __pyx_k_old_feature, sizeof(__pyx_k_old_feature), 0, 0, 1, 1},
+ {&__pyx_n_s_operator, __pyx_k_operator, sizeof(__pyx_k_operator), 0, 0, 1, 1},
+ {&__pyx_n_s_position, __pyx_k_position, sizeof(__pyx_k_position), 0, 0, 1, 1},
+ {&__pyx_n_s_pyx_vtable, __pyx_k_pyx_vtable, sizeof(__pyx_k_pyx_vtable), 0, 0, 1, 1},
+ {&__pyx_n_s_reverse, __pyx_k_reverse, sizeof(__pyx_k_reverse), 0, 0, 1, 1},
+ {&__pyx_n_s_right, __pyx_k_right, sizeof(__pyx_k_right), 0, 0, 1, 1},
+ {&__pyx_n_s_sort, __pyx_k_sort, sizeof(__pyx_k_sort), 0, 0, 1, 1},
+ {&__pyx_n_s_start, __pyx_k_start, sizeof(__pyx_k_start), 0, 0, 1, 1},
+ {&__pyx_kp_s_start_must_be_less_than_end, __pyx_k_start_must_be_less_than_end, sizeof(__pyx_k_start_must_be_less_than_end), 0, 0, 1, 0},
+ {&__pyx_n_s_strand, __pyx_k_strand, sizeof(__pyx_k_strand), 0, 0, 1, 1},
+ {&__pyx_n_s_test, __pyx_k_test, sizeof(__pyx_k_test), 0, 0, 1, 1},
+ {&__pyx_n_s_traverse, __pyx_k_traverse, sizeof(__pyx_k_traverse), 0, 0, 1, 1},
+ {&__pyx_n_s_update, __pyx_k_update, sizeof(__pyx_k_update), 0, 0, 1, 1},
+ {&__pyx_n_s_value, __pyx_k_value, sizeof(__pyx_k_value), 0, 0, 1, 1},
+ {&__pyx_kp_s_value_2, __pyx_k_value_2, sizeof(__pyx_k_value_2), 0, 0, 1, 0},
+ {0, 0, 0, 0, 0, 0, 0}
+};
+static int __Pyx_InitCachedBuiltins(void) {
+ return 0;
+}
+
+static int __Pyx_InitCachedConstants(void) {
+ __Pyx_RefNannyDeclarations
+ __Pyx_RefNannySetupContext("__Pyx_InitCachedConstants", 0);
+
+ /* "skbio/metadata/_intersection.pyx":275
+ * if len(results) == n: return results
+ * r = results
+ * r.sort(key=operator.attrgetter('end'), reverse=True) # <<<<<<<<<<<<<<
+ * return r[:n]
+ *
+ */
+ __pyx_tuple_ = PyTuple_Pack(1, __pyx_n_s_end); if (unlikely(!__pyx_tuple_)) __PYX_ERR(0, 275, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_tuple_);
+ __Pyx_GIVEREF(__pyx_tuple_);
+
+ /* "skbio/metadata/_intersection.pyx":290
+ * if len(results) == n: return results
+ * r = results
+ * r.sort(key=operator.attrgetter('start')) # <<<<<<<<<<<<<<
+ * return r[:n]
+ *
+ */
+ __pyx_tuple__2 = PyTuple_Pack(1, __pyx_n_s_start); if (unlikely(!__pyx_tuple__2)) __PYX_ERR(0, 290, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_tuple__2);
+ __Pyx_GIVEREF(__pyx_tuple__2);
+
+ /* "skbio/metadata/_intersection.pyx":301
+ * if self.cright is not EmptyNode: self.cright._traverse(func)
+ *
+ * cdef IntervalNode EmptyNode = IntervalNode( 0, 0, IntervalObj(0, 0)) # <<<<<<<<<<<<<<
+ *
+ * ## ---- Wrappers that retain the old interface -------------------------------
+ */
+ __pyx_tuple__5 = PyTuple_Pack(2, __pyx_int_0, __pyx_int_0); if (unlikely(!__pyx_tuple__5)) __PYX_ERR(0, 301, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_tuple__5);
+ __Pyx_GIVEREF(__pyx_tuple__5);
+ __Pyx_RefNannyFinishContext();
+ return 0;
+ __pyx_L1_error:;
+ __Pyx_RefNannyFinishContext();
+ return -1;
+}
+
+static int __Pyx_InitGlobals(void) {
+ if (__Pyx_InitStrings(__pyx_string_tab) < 0) __PYX_ERR(0, 1, __pyx_L1_error);
+ __pyx_int_0 = PyInt_FromLong(0); if (unlikely(!__pyx_int_0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_int_1 = PyInt_FromLong(1); if (unlikely(!__pyx_int_1)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_int_2 = PyInt_FromLong(2); if (unlikely(!__pyx_int_2)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_int_3 = PyInt_FromLong(3); if (unlikely(!__pyx_int_3)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_int_4 = PyInt_FromLong(4); if (unlikely(!__pyx_int_4)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_int_5 = PyInt_FromLong(5); if (unlikely(!__pyx_int_5)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_int_2500 = PyInt_FromLong(2500); if (unlikely(!__pyx_int_2500)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_int_neg_1 = PyInt_FromLong(-1); if (unlikely(!__pyx_int_neg_1)) __PYX_ERR(0, 1, __pyx_L1_error)
+ return 0;
+ __pyx_L1_error:;
+ return -1;
+}
+
+#if PY_MAJOR_VERSION < 3
+PyMODINIT_FUNC init_intersection(void); /*proto*/
+PyMODINIT_FUNC init_intersection(void)
+#else
+PyMODINIT_FUNC PyInit__intersection(void); /*proto*/
+PyMODINIT_FUNC PyInit__intersection(void)
+#endif
+{
+ PyObject *__pyx_t_1 = NULL;
+ float __pyx_t_2;
+ PyObject *__pyx_t_3 = NULL;
+ __Pyx_RefNannyDeclarations
+ #if CYTHON_REFNANNY
+ __Pyx_RefNanny = __Pyx_RefNannyImportAPI("refnanny");
+ if (!__Pyx_RefNanny) {
+ PyErr_Clear();
+ __Pyx_RefNanny = __Pyx_RefNannyImportAPI("Cython.Runtime.refnanny");
+ if (!__Pyx_RefNanny)
+ Py_FatalError("failed to import 'refnanny' module");
+ }
+ #endif
+ __Pyx_RefNannySetupContext("PyMODINIT_FUNC PyInit__intersection(void)", 0);
+ if (__Pyx_check_binary_version() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_empty_tuple = PyTuple_New(0); if (unlikely(!__pyx_empty_tuple)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_empty_bytes = PyBytes_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_bytes)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_empty_unicode = PyUnicode_FromStringAndSize("", 0); if (unlikely(!__pyx_empty_unicode)) __PYX_ERR(0, 1, __pyx_L1_error)
+ #ifdef __Pyx_CyFunction_USED
+ if (__pyx_CyFunction_init() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ #endif
+ #ifdef __Pyx_FusedFunction_USED
+ if (__pyx_FusedFunction_init() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ #endif
+ #ifdef __Pyx_Coroutine_USED
+ if (__pyx_Coroutine_init() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ #endif
+ #ifdef __Pyx_Generator_USED
+ if (__pyx_Generator_init() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ #endif
+ #ifdef __Pyx_StopAsyncIteration_USED
+ if (__pyx_StopAsyncIteration_init() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ #endif
+ /*--- Library function declarations ---*/
+ /*--- Threads initialization code ---*/
+ #if defined(__PYX_FORCE_INIT_THREADS) && __PYX_FORCE_INIT_THREADS
+ #ifdef WITH_THREAD /* Python build with threading support? */
+ PyEval_InitThreads();
+ #endif
+ #endif
+ /*--- Module creation code ---*/
+ #if PY_MAJOR_VERSION < 3
+ __pyx_m = Py_InitModule4("_intersection", __pyx_methods, __pyx_k_Data_structure_for_performing_i, 0, PYTHON_API_VERSION); Py_XINCREF(__pyx_m);
+ #else
+ __pyx_m = PyModule_Create(&__pyx_moduledef);
+ #endif
+ if (unlikely(!__pyx_m)) __PYX_ERR(0, 1, __pyx_L1_error)
+ __pyx_d = PyModule_GetDict(__pyx_m); if (unlikely(!__pyx_d)) __PYX_ERR(0, 1, __pyx_L1_error)
+ Py_INCREF(__pyx_d);
+ __pyx_b = PyImport_AddModule(__Pyx_BUILTIN_MODULE_NAME); if (unlikely(!__pyx_b)) __PYX_ERR(0, 1, __pyx_L1_error)
+ #if CYTHON_COMPILING_IN_PYPY
+ Py_INCREF(__pyx_b);
+ #endif
+ if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) __PYX_ERR(0, 1, __pyx_L1_error);
+ /*--- Initialize various global constants etc. ---*/
+ if (__Pyx_InitGlobals() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ #if PY_MAJOR_VERSION < 3 && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT)
+ if (__Pyx_init_sys_getdefaultencoding_params() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ #endif
+ if (__pyx_module_is_main_skbio__metadata___intersection) {
+ if (PyObject_SetAttrString(__pyx_m, "__name__", __pyx_n_s_main) < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ #if PY_MAJOR_VERSION >= 3
+ {
+ PyObject *modules = PyImport_GetModuleDict(); if (unlikely(!modules)) __PYX_ERR(0, 1, __pyx_L1_error)
+ if (!PyDict_GetItemString(modules, "skbio.metadata._intersection")) {
+ if (unlikely(PyDict_SetItemString(modules, "skbio.metadata._intersection", __pyx_m) < 0)) __PYX_ERR(0, 1, __pyx_L1_error)
+ }
+ }
+ #endif
+ /*--- Builtin init code ---*/
+ if (__Pyx_InitCachedBuiltins() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ /*--- Constants init code ---*/
+ if (__Pyx_InitCachedConstants() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ /*--- Global init code ---*/
+ __pyx_v_5skbio_8metadata_13_intersection_EmptyNode = ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)Py_None); Py_INCREF(Py_None);
+ /*--- Variable export code ---*/
+ /*--- Function export code ---*/
+ /*--- Type init code ---*/
+ __pyx_vtabptr_5skbio_8metadata_13_intersection_IntervalNode = &__pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode.insert = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *(*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, int, PyObject *, int __pyx_skip_dispatch))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_insert;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode.rotate_right = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *(*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_rotate_right;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode.rotate_left = (struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *(*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_rotate_left;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode.set_ends = (void (*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_set_ends;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode._intersect = (void (*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, int, PyObject *))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__intersect;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode.update = (void (*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, int, PyObject *, PyObject *, int __pyx_skip_dispatch))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_update;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode._seek_left = (void (*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, PyObject *, int, int))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__seek_left;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode._seek_right = (void (*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, int, PyObject *, int, int))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__seek_right;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode.left = (PyObject *(*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, PyObject *, int __pyx_skip_dispatch, struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_left *__pyx_optional_args))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_left;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode.right = (PyObject *(*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, PyObject *, int __pyx_skip_dispatch, struct __pyx_opt_args_5skbio_8metadata_13_intersection_12IntervalNode_right *__pyx_optional_args))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode_right;
+ __pyx_vtable_5skbio_8metadata_13_intersection_IntervalNode._traverse = (void (*)(struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *, PyObject *))__pyx_f_5skbio_8metadata_13_intersection_12IntervalNode__traverse;
+ if (PyType_Ready(&__pyx_type_5skbio_8metadata_13_intersection_IntervalNode) < 0) __PYX_ERR(0, 66, __pyx_L1_error)
+ __pyx_type_5skbio_8metadata_13_intersection_IntervalNode.tp_print = 0;
+ if (__Pyx_SetVtable(__pyx_type_5skbio_8metadata_13_intersection_IntervalNode.tp_dict, __pyx_vtabptr_5skbio_8metadata_13_intersection_IntervalNode) < 0) __PYX_ERR(0, 66, __pyx_L1_error)
+ if (PyObject_SetAttrString(__pyx_m, "IntervalNode", (PyObject *)&__pyx_type_5skbio_8metadata_13_intersection_IntervalNode) < 0) __PYX_ERR(0, 66, __pyx_L1_error)
+ __pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode = &__pyx_type_5skbio_8metadata_13_intersection_IntervalNode;
+ if (PyType_Ready(&__pyx_type_5skbio_8metadata_13_intersection_IntervalObj) < 0) __PYX_ERR(0, 305, __pyx_L1_error)
+ __pyx_type_5skbio_8metadata_13_intersection_IntervalObj.tp_print = 0;
+ if (PyObject_SetAttrString(__pyx_m, "IntervalObj", (PyObject *)&__pyx_type_5skbio_8metadata_13_intersection_IntervalObj) < 0) __PYX_ERR(0, 305, __pyx_L1_error)
+ __pyx_ptype_5skbio_8metadata_13_intersection_IntervalObj = &__pyx_type_5skbio_8metadata_13_intersection_IntervalObj;
+ if (PyType_Ready(&__pyx_type_5skbio_8metadata_13_intersection_IntervalTree) < 0) __PYX_ERR(0, 357, __pyx_L1_error)
+ __pyx_type_5skbio_8metadata_13_intersection_IntervalTree.tp_print = 0;
+ if (PyObject_SetAttrString(__pyx_m, "IntervalTree", (PyObject *)&__pyx_type_5skbio_8metadata_13_intersection_IntervalTree) < 0) __PYX_ERR(0, 357, __pyx_L1_error)
+ __pyx_ptype_5skbio_8metadata_13_intersection_IntervalTree = &__pyx_type_5skbio_8metadata_13_intersection_IntervalTree;
+ /*--- Type import code ---*/
+ /*--- Variable import code ---*/
+ /*--- Function import code ---*/
+ /*--- Execution code ---*/
+ #if defined(__Pyx_Generator_USED) || defined(__Pyx_Coroutine_USED)
+ if (__Pyx_patch_abc() < 0) __PYX_ERR(0, 1, __pyx_L1_error)
+ #endif
+
+ /* "skbio/metadata/_intersection.pyx":28
+ * #cython: cdivision=True
+ *
+ * import operator # <<<<<<<<<<<<<<
+ *
+ * cdef extern from "stdlib.h":
+ */
+ __pyx_t_1 = __Pyx_Import(__pyx_n_s_operator, 0, -1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 28, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (PyDict_SetItem(__pyx_d, __pyx_n_s_operator, __pyx_t_1) < 0) __PYX_ERR(0, 28, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":64
+ * return a
+ *
+ * cdef float nlog = -1.0 / log(0.5) # <<<<<<<<<<<<<<
+ *
+ * cdef class IntervalNode:
+ */
+ __pyx_t_2 = log(0.5);
+ if (unlikely(__pyx_t_2 == 0)) {
+ PyErr_SetString(PyExc_ZeroDivisionError, "float division");
+ __PYX_ERR(0, 64, __pyx_L1_error)
+ }
+ __pyx_v_5skbio_8metadata_13_intersection_nlog = (-1.0 / __pyx_t_2);
+
+ /* "skbio/metadata/_intersection.pyx":182
+ * return results
+ *
+ * find = intersect # <<<<<<<<<<<<<<
+ *
+ * cdef void _intersect( IntervalNode self, int start, int end, list results):
+ */
+ __pyx_t_1 = __Pyx_GetNameInClass((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode, __pyx_n_s_intersect); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 182, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (PyDict_SetItem((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode->tp_dict, __pyx_n_s_find, __pyx_t_1) < 0) __PYX_ERR(0, 182, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ PyType_Modified(__pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode);
+
+ /* "skbio/metadata/_intersection.pyx":301
+ * if self.cright is not EmptyNode: self.cright._traverse(func)
+ *
+ * cdef IntervalNode EmptyNode = IntervalNode( 0, 0, IntervalObj(0, 0)) # <<<<<<<<<<<<<<
+ *
+ * ## ---- Wrappers that retain the old interface -------------------------------
+ */
+ __pyx_t_1 = __Pyx_PyObject_Call(((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalObj), __pyx_tuple__5, NULL); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 301, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __pyx_t_3 = PyTuple_New(3); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 301, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_3);
+ __Pyx_INCREF(__pyx_int_0);
+ __Pyx_GIVEREF(__pyx_int_0);
+ PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_int_0);
+ __Pyx_INCREF(__pyx_int_0);
+ __Pyx_GIVEREF(__pyx_int_0);
+ PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_int_0);
+ __Pyx_GIVEREF(__pyx_t_1);
+ PyTuple_SET_ITEM(__pyx_t_3, 2, __pyx_t_1);
+ __pyx_t_1 = 0;
+ __pyx_t_1 = __Pyx_PyObject_Call(((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalNode), __pyx_t_3, NULL); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 301, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
+ __Pyx_XGOTREF(((PyObject *)__pyx_v_5skbio_8metadata_13_intersection_EmptyNode));
+ __Pyx_DECREF_SET(__pyx_v_5skbio_8metadata_13_intersection_EmptyNode, ((struct __pyx_obj_5skbio_8metadata_13_intersection_IntervalNode *)__pyx_t_1));
+ __Pyx_GIVEREF(__pyx_t_1);
+ __pyx_t_1 = 0;
+
+ /* "skbio/metadata/_intersection.pyx":429
+ * self.root = self.root.insert( start, end, value )
+ *
+ * add = insert # <<<<<<<<<<<<<<
+ *
+ *
+ */
+ __pyx_t_1 = __Pyx_GetNameInClass((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalTree, __pyx_n_s_insert); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 429, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (PyDict_SetItem((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalTree->tp_dict, __pyx_n_s_add, __pyx_t_1) < 0) __PYX_ERR(0, 429, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ PyType_Modified(__pyx_ptype_5skbio_8metadata_13_intersection_IntervalTree);
+
+ /* "skbio/metadata/_intersection.pyx":476
+ * self.insert( interval.start, interval.end, interval )
+ *
+ * add_interval = insert_interval # <<<<<<<<<<<<<<
+ *
+ * def before_interval( self, interval, num_intervals=1, max_dist=2500 ):
+ */
+ __pyx_t_1 = __Pyx_GetNameInClass((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalTree, __pyx_n_s_insert_interval); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 476, __pyx_L1_error)
+ __Pyx_GOTREF(__pyx_t_1);
+ if (PyDict_SetItem((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalTree->tp_dict, __pyx_n_s_add_interval, __pyx_t_1) < 0) __PYX_ERR(0, 476, __pyx_L1_error)
+ __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+ PyType_Modified(__pyx_ptype_5skbio_8metadata_13_intersection_IntervalTree);
+
+ /* "skbio/metadata/_intersection.pyx":529
+ *
+ * # For backward compatibility
+ * Intersecter = IntervalTree # <<<<<<<<<<<<<<
+ */
+ if (PyDict_SetItem(__pyx_d, __pyx_n_s_Intersecter, ((PyObject *)__pyx_ptype_5skbio_8metadata_13_intersection_IntervalTree)) < 0) __PYX_ERR(0, 529, __pyx_L1_error)
+
+ /* "skbio/metadata/_intersection.pyx":1
+ * # ----------------------------------------------------------------------------- # <<<<<<<<<<<<<<
+ * # This code is taken from bx-python project and added with a new function
+ * # `update` from line 195 to 211. The license for this code is included in
+ */
+ __pyx_t_1 = PyDict_New(); 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;
+
+ /*--- Wrapped vars code ---*/
+
+ goto __pyx_L0;
+ __pyx_L1_error:;
+ __Pyx_XDECREF(__pyx_t_1);
+ __Pyx_XDECREF(__pyx_t_3);
+ if (__pyx_m) {
+ if (__pyx_d) {
+ __Pyx_AddTraceback("init skbio.metadata._intersection", __pyx_clineno, __pyx_lineno, __pyx_filename);
+ }
+ Py_DECREF(__pyx_m); __pyx_m = 0;
+ } else if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ImportError, "init skbio.metadata._intersection");
+ }
+ __pyx_L0:;
+ __Pyx_RefNannyFinishContext();
+ #if PY_MAJOR_VERSION < 3
+ return;
+ #else
+ return __pyx_m;
+ #endif
+}
+
+/* --- Runtime support code --- */
+/* Refnanny */
+#if CYTHON_REFNANNY
+static __Pyx_RefNannyAPIStruct *__Pyx_RefNannyImportAPI(const char *modname) {
+ PyObject *m = NULL, *p = NULL;
+ void *r = NULL;
+ m = PyImport_ImportModule((char *)modname);
+ if (!m) goto end;
+ p = PyObject_GetAttrString(m, (char *)"RefNannyAPI");
+ if (!p) goto end;
+ r = PyLong_AsVoidPtr(p);
+end:
+ Py_XDECREF(p);
+ Py_XDECREF(m);
+ return (__Pyx_RefNannyAPIStruct *)r;
+}
+#endif
+
+/* RaiseArgTupleInvalid */
+static void __Pyx_RaiseArgtupleInvalid(
+ const char* func_name,
+ int exact,
+ Py_ssize_t num_min,
+ Py_ssize_t num_max,
+ Py_ssize_t num_found)
+{
+ Py_ssize_t num_expected;
+ const char *more_or_less;
+ if (num_found < num_min) {
+ num_expected = num_min;
+ more_or_less = "at least";
+ } else {
+ num_expected = num_max;
+ more_or_less = "at most";
+ }
+ if (exact) {
+ more_or_less = "exactly";
+ }
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes %.8s %" CYTHON_FORMAT_SSIZE_T "d positional argument%.1s (%" CYTHON_FORMAT_SSIZE_T "d given)",
+ func_name, more_or_less, num_expected,
+ (num_expected == 1) ? "" : "s", num_found);
+}
+
+/* RaiseDoubleKeywords */
+static void __Pyx_RaiseDoubleKeywordsError(
+ const char* func_name,
+ PyObject* kw_name)
+{
+ PyErr_Format(PyExc_TypeError,
+ #if PY_MAJOR_VERSION >= 3
+ "%s() got multiple values for keyword argument '%U'", func_name, kw_name);
+ #else
+ "%s() got multiple values for keyword argument '%s'", func_name,
+ PyString_AsString(kw_name));
+ #endif
+}
+
+/* ParseKeywords */
+static int __Pyx_ParseOptionalKeywords(
+ PyObject *kwds,
+ PyObject **argnames[],
+ PyObject *kwds2,
+ PyObject *values[],
+ Py_ssize_t num_pos_args,
+ const char* function_name)
+{
+ PyObject *key = 0, *value = 0;
+ Py_ssize_t pos = 0;
+ PyObject*** name;
+ PyObject*** first_kw_arg = argnames + num_pos_args;
+ while (PyDict_Next(kwds, &pos, &key, &value)) {
+ name = first_kw_arg;
+ while (*name && (**name != key)) name++;
+ if (*name) {
+ values[name-argnames] = value;
+ continue;
+ }
+ name = first_kw_arg;
+ #if PY_MAJOR_VERSION < 3
+ if (likely(PyString_CheckExact(key)) || likely(PyString_Check(key))) {
+ while (*name) {
+ if ((CYTHON_COMPILING_IN_PYPY || PyString_GET_SIZE(**name) == PyString_GET_SIZE(key))
+ && _PyString_Eq(**name, key)) {
+ values[name-argnames] = value;
+ break;
+ }
+ name++;
+ }
+ if (*name) continue;
+ else {
+ PyObject*** argname = argnames;
+ while (argname != first_kw_arg) {
+ if ((**argname == key) || (
+ (CYTHON_COMPILING_IN_PYPY || PyString_GET_SIZE(**argname) == PyString_GET_SIZE(key))
+ && _PyString_Eq(**argname, key))) {
+ goto arg_passed_twice;
+ }
+ argname++;
+ }
+ }
+ } else
+ #endif
+ if (likely(PyUnicode_Check(key))) {
+ while (*name) {
+ int cmp = (**name == key) ? 0 :
+ #if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3
+ (PyUnicode_GET_SIZE(**name) != PyUnicode_GET_SIZE(key)) ? 1 :
+ #endif
+ PyUnicode_Compare(**name, key);
+ if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad;
+ if (cmp == 0) {
+ values[name-argnames] = value;
+ break;
+ }
+ name++;
+ }
+ if (*name) continue;
+ else {
+ PyObject*** argname = argnames;
+ while (argname != first_kw_arg) {
+ int cmp = (**argname == key) ? 0 :
+ #if !CYTHON_COMPILING_IN_PYPY && PY_MAJOR_VERSION >= 3
+ (PyUnicode_GET_SIZE(**argname) != PyUnicode_GET_SIZE(key)) ? 1 :
+ #endif
+ PyUnicode_Compare(**argname, key);
+ if (cmp < 0 && unlikely(PyErr_Occurred())) goto bad;
+ if (cmp == 0) goto arg_passed_twice;
+ argname++;
+ }
+ }
+ } else
+ goto invalid_keyword_type;
+ if (kwds2) {
+ if (unlikely(PyDict_SetItem(kwds2, key, value))) goto bad;
+ } else {
+ goto invalid_keyword;
+ }
+ }
+ return 0;
+arg_passed_twice:
+ __Pyx_RaiseDoubleKeywordsError(function_name, key);
+ goto bad;
+invalid_keyword_type:
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() keywords must be strings", function_name);
+ goto bad;
+invalid_keyword:
+ PyErr_Format(PyExc_TypeError,
+ #if PY_MAJOR_VERSION < 3
+ "%.200s() got an unexpected keyword argument '%.200s'",
+ function_name, PyString_AsString(key));
+ #else
+ "%s() got an unexpected keyword argument '%U'",
+ function_name, key);
+ #endif
+bad:
+ return -1;
+}
+
+/* PyObjectCall */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw) {
+ PyObject *result;
+ ternaryfunc call = func->ob_type->tp_call;
+ if (unlikely(!call))
+ return PyObject_Call(func, arg, kw);
+ if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object")))
+ return NULL;
+ result = (*call)(func, arg, kw);
+ Py_LeaveRecursiveCall();
+ if (unlikely(!result) && unlikely(!PyErr_Occurred())) {
+ PyErr_SetString(
+ PyExc_SystemError,
+ "NULL result without error in PyObject_Call");
+ }
+ return result;
+}
+#endif
+
+/* ExtTypeTest */
+static CYTHON_INLINE int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type) {
+ if (unlikely(!type)) {
+ PyErr_SetString(PyExc_SystemError, "Missing type object");
+ return 0;
+ }
+ if (likely(PyObject_TypeCheck(obj, type)))
+ return 1;
+ PyErr_Format(PyExc_TypeError, "Cannot convert %.200s to %.200s",
+ Py_TYPE(obj)->tp_name, type->tp_name);
+ return 0;
+}
+
+/* PyErrFetchRestore */
+#if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE void __Pyx_ErrRestoreInState(PyThreadState *tstate, PyObject *type, PyObject *value, PyObject *tb) {
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+ tmp_type = tstate->curexc_type;
+ tmp_value = tstate->curexc_value;
+ tmp_tb = tstate->curexc_traceback;
+ tstate->curexc_type = type;
+ tstate->curexc_value = value;
+ tstate->curexc_traceback = tb;
+ Py_XDECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+}
+static CYTHON_INLINE void __Pyx_ErrFetchInState(PyThreadState *tstate, PyObject **type, PyObject **value, PyObject **tb) {
+ *type = tstate->curexc_type;
+ *value = tstate->curexc_value;
+ *tb = tstate->curexc_traceback;
+ tstate->curexc_type = 0;
+ tstate->curexc_value = 0;
+ tstate->curexc_traceback = 0;
+}
+#endif
+
+/* WriteUnraisableException */
+static void __Pyx_WriteUnraisable(const char *name, CYTHON_UNUSED int clineno,
+ CYTHON_UNUSED int lineno, CYTHON_UNUSED const char *filename,
+ int full_traceback, CYTHON_UNUSED int nogil) {
+ PyObject *old_exc, *old_val, *old_tb;
+ PyObject *ctx;
+ __Pyx_PyThreadState_declare
+#ifdef WITH_THREAD
+ PyGILState_STATE state;
+ if (nogil)
+ state = PyGILState_Ensure();
+#ifdef _MSC_VER
+ else state = (PyGILState_STATE)-1;
+#endif
+#endif
+ __Pyx_PyThreadState_assign
+ __Pyx_ErrFetch(&old_exc, &old_val, &old_tb);
+ if (full_traceback) {
+ Py_XINCREF(old_exc);
+ Py_XINCREF(old_val);
+ Py_XINCREF(old_tb);
+ __Pyx_ErrRestore(old_exc, old_val, old_tb);
+ PyErr_PrintEx(1);
+ }
+ #if PY_MAJOR_VERSION < 3
+ ctx = PyString_FromString(name);
+ #else
+ ctx = PyUnicode_FromString(name);
+ #endif
+ __Pyx_ErrRestore(old_exc, old_val, old_tb);
+ if (!ctx) {
+ PyErr_WriteUnraisable(Py_None);
+ } else {
+ PyErr_WriteUnraisable(ctx);
+ Py_DECREF(ctx);
+ }
+#ifdef WITH_THREAD
+ if (nogil)
+ PyGILState_Release(state);
+#endif
+}
+
+/* PyIntBinop */
+#if CYTHON_COMPILING_IN_CPYTHON
+static PyObject* __Pyx_PyInt_SubtractObjC(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, CYTHON_UNUSED int inplace) {
+ #if PY_MAJOR_VERSION < 3
+ if (likely(PyInt_CheckExact(op1))) {
+ const long b = intval;
+ long x;
+ long a = PyInt_AS_LONG(op1);
+ x = (long)((unsigned long)a - b);
+ if (likely((x^a) >= 0 || (x^~b) >= 0))
+ return PyInt_FromLong(x);
+ return PyLong_Type.tp_as_number->nb_subtract(op1, op2);
+ }
+ #endif
+ #if CYTHON_USE_PYLONG_INTERNALS && PY_MAJOR_VERSION >= 3
+ if (likely(PyLong_CheckExact(op1))) {
+ const long b = intval;
+ long a, x;
+ const PY_LONG_LONG llb = intval;
+ PY_LONG_LONG lla, llx;
+ const digit* digits = ((PyLongObject*)op1)->ob_digit;
+ const Py_ssize_t size = Py_SIZE(op1);
+ if (likely(__Pyx_sst_abs(size) <= 1)) {
+ a = likely(size) ? digits[0] : 0;
+ if (size == -1) a = -a;
+ } else {
+ switch (size) {
+ case -2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ a = -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) {
+ lla = -(PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case 2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ a = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case -3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ a = -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) {
+ lla = -(PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case 3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ a = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case -4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ a = -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) {
+ lla = -(PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case 4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ a = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ default: return PyLong_Type.tp_as_number->nb_subtract(op1, op2);
+ }
+ }
+ x = a - b;
+ return PyLong_FromLong(x);
+ long_long:
+ llx = lla - llb;
+ return PyLong_FromLongLong(llx);
+ }
+ #endif
+ if (PyFloat_CheckExact(op1)) {
+ const long b = intval;
+ double a = PyFloat_AS_DOUBLE(op1);
+ double result;
+ PyFPE_START_PROTECT("subtract", return NULL)
+ result = ((double)a) - (double)b;
+ PyFPE_END_PROTECT(result)
+ return PyFloat_FromDouble(result);
+ }
+ return (inplace ? PyNumber_InPlaceSubtract : PyNumber_Subtract)(op1, op2);
+}
+#endif
+
+/* GetBuiltinName */
+static PyObject *__Pyx_GetBuiltinName(PyObject *name) {
+ PyObject* result = __Pyx_PyObject_GetAttrStr(__pyx_b, name);
+ if (unlikely(!result)) {
+ PyErr_Format(PyExc_NameError,
+#if PY_MAJOR_VERSION >= 3
+ "name '%U' is not defined", name);
+#else
+ "name '%.200s' is not defined", PyString_AS_STRING(name));
+#endif
+ }
+ return result;
+}
+
+/* GetModuleGlobalName */
+static CYTHON_INLINE PyObject *__Pyx_GetModuleGlobalName(PyObject *name) {
+ PyObject *result;
+#if CYTHON_COMPILING_IN_CPYTHON
+ result = PyDict_GetItem(__pyx_d, name);
+ if (likely(result)) {
+ Py_INCREF(result);
+ } else {
+#else
+ result = PyObject_GetItem(__pyx_d, name);
+ if (!result) {
+ PyErr_Clear();
+#endif
+ result = __Pyx_GetBuiltinName(name);
+ }
+ return result;
+}
+
+/* SliceTupleAndList */
+ #if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE void __Pyx_crop_slice(Py_ssize_t* _start, Py_ssize_t* _stop, Py_ssize_t* _length) {
+ Py_ssize_t start = *_start, stop = *_stop, length = *_length;
+ if (start < 0) {
+ start += length;
+ if (start < 0)
+ start = 0;
+ }
+ if (stop < 0)
+ stop += length;
+ else if (stop > length)
+ stop = length;
+ *_length = stop - start;
+ *_start = start;
+ *_stop = stop;
+}
+static CYTHON_INLINE void __Pyx_copy_object_array(PyObject** CYTHON_RESTRICT src, PyObject** CYTHON_RESTRICT dest, Py_ssize_t length) {
+ PyObject *v;
+ Py_ssize_t i;
+ for (i = 0; i < length; i++) {
+ v = dest[i] = src[i];
+ Py_INCREF(v);
+ }
+}
+static CYTHON_INLINE PyObject* __Pyx_PyList_GetSlice(
+ PyObject* src, Py_ssize_t start, Py_ssize_t stop) {
+ PyObject* dest;
+ Py_ssize_t length = PyList_GET_SIZE(src);
+ __Pyx_crop_slice(&start, &stop, &length);
+ if (unlikely(length <= 0))
+ return PyList_New(0);
+ dest = PyList_New(length);
+ if (unlikely(!dest))
+ return NULL;
+ __Pyx_copy_object_array(
+ ((PyListObject*)src)->ob_item + start,
+ ((PyListObject*)dest)->ob_item,
+ length);
+ return dest;
+}
+static CYTHON_INLINE PyObject* __Pyx_PyTuple_GetSlice(
+ PyObject* src, Py_ssize_t start, Py_ssize_t stop) {
+ PyObject* dest;
+ Py_ssize_t length = PyTuple_GET_SIZE(src);
+ __Pyx_crop_slice(&start, &stop, &length);
+ if (unlikely(length <= 0))
+ return PyTuple_New(0);
+ dest = PyTuple_New(length);
+ if (unlikely(!dest))
+ return NULL;
+ __Pyx_copy_object_array(
+ ((PyTupleObject*)src)->ob_item + start,
+ ((PyTupleObject*)dest)->ob_item,
+ length);
+ return dest;
+}
+#endif
+
+/* PyIntBinop */
+ #if CYTHON_COMPILING_IN_CPYTHON
+static PyObject* __Pyx_PyInt_AddObjC(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, CYTHON_UNUSED int inplace) {
+ #if PY_MAJOR_VERSION < 3
+ if (likely(PyInt_CheckExact(op1))) {
+ const long b = intval;
+ long x;
+ long a = PyInt_AS_LONG(op1);
+ x = (long)((unsigned long)a + b);
+ if (likely((x^a) >= 0 || (x^b) >= 0))
+ return PyInt_FromLong(x);
+ return PyLong_Type.tp_as_number->nb_add(op1, op2);
+ }
+ #endif
+ #if CYTHON_USE_PYLONG_INTERNALS && PY_MAJOR_VERSION >= 3
+ if (likely(PyLong_CheckExact(op1))) {
+ const long b = intval;
+ long a, x;
+ const PY_LONG_LONG llb = intval;
+ PY_LONG_LONG lla, llx;
+ const digit* digits = ((PyLongObject*)op1)->ob_digit;
+ const Py_ssize_t size = Py_SIZE(op1);
+ if (likely(__Pyx_sst_abs(size) <= 1)) {
+ a = likely(size) ? digits[0] : 0;
+ if (size == -1) a = -a;
+ } else {
+ switch (size) {
+ case -2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ a = -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) {
+ lla = -(PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case 2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ a = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 2 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case -3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ a = -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) {
+ lla = -(PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case 3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ a = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 3 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((((unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case -4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ a = -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) {
+ lla = -(PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ case 4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ a = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ } else if (8 * sizeof(PY_LONG_LONG) - 1 > 4 * PyLong_SHIFT) {
+ lla = (PY_LONG_LONG) (((((((((unsigned PY_LONG_LONG)digits[3]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[2]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[1]) << PyLong_SHIFT) | (unsigned PY_LONG_LONG)digits[0]));
+ goto long_long;
+ }
+ default: return PyLong_Type.tp_as_number->nb_add(op1, op2);
+ }
+ }
+ x = a + b;
+ return PyLong_FromLong(x);
+ long_long:
+ llx = lla + llb;
+ return PyLong_FromLongLong(llx);
+ }
+ #endif
+ if (PyFloat_CheckExact(op1)) {
+ const long b = intval;
+ double a = PyFloat_AS_DOUBLE(op1);
+ double result;
+ PyFPE_START_PROTECT("add", return NULL)
+ result = ((double)a) + (double)b;
+ PyFPE_END_PROTECT(result)
+ return PyFloat_FromDouble(result);
+ }
+ return (inplace ? PyNumber_InPlaceAdd : PyNumber_Add)(op1, op2);
+}
+#endif
+
+/* PyObjectCallMethO */
+ #if CYTHON_COMPILING_IN_CPYTHON
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg) {
+ PyObject *self, *result;
+ PyCFunction cfunc;
+ cfunc = PyCFunction_GET_FUNCTION(func);
+ self = PyCFunction_GET_SELF(func);
+ if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object")))
+ return NULL;
+ result = cfunc(self, arg);
+ Py_LeaveRecursiveCall();
+ if (unlikely(!result) && unlikely(!PyErr_Occurred())) {
+ PyErr_SetString(
+ PyExc_SystemError,
+ "NULL result without error in PyObject_Call");
+ }
+ return result;
+}
+#endif
+
+/* PyObjectCallOneArg */
+ #if CYTHON_COMPILING_IN_CPYTHON
+static PyObject* __Pyx__PyObject_CallOneArg(PyObject *func, PyObject *arg) {
+ PyObject *result;
+ PyObject *args = PyTuple_New(1);
+ if (unlikely(!args)) return NULL;
+ Py_INCREF(arg);
+ PyTuple_SET_ITEM(args, 0, arg);
+ result = __Pyx_PyObject_Call(func, args, NULL);
+ Py_DECREF(args);
+ return result;
+}
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) {
+#ifdef __Pyx_CyFunction_USED
+ if (likely(PyCFunction_Check(func) || PyObject_TypeCheck(func, __pyx_CyFunctionType))) {
+#else
+ if (likely(PyCFunction_Check(func))) {
+#endif
+ if (likely(PyCFunction_GET_FLAGS(func) & METH_O)) {
+ return __Pyx_PyObject_CallMethO(func, arg);
+ }
+ }
+ return __Pyx__PyObject_CallOneArg(func, arg);
+}
+#else
+static CYTHON_INLINE PyObject* __Pyx_PyObject_CallOneArg(PyObject *func, PyObject *arg) {
+ PyObject *result;
+ PyObject *args = PyTuple_Pack(1, arg);
+ if (unlikely(!args)) return NULL;
+ result = __Pyx_PyObject_Call(func, args, NULL);
+ Py_DECREF(args);
+ return result;
+}
+#endif
+
+/* PyIntBinop */
+ #if CYTHON_COMPILING_IN_CPYTHON
+static PyObject* __Pyx_PyInt_EqObjC(PyObject *op1, PyObject *op2, CYTHON_UNUSED long intval, CYTHON_UNUSED int inplace) {
+ if (op1 == op2) {
+ Py_RETURN_TRUE;
+ }
+ #if PY_MAJOR_VERSION < 3
+ if (likely(PyInt_CheckExact(op1))) {
+ const long b = intval;
+ long a = PyInt_AS_LONG(op1);
+ if (a == b) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ }
+ #endif
+ #if CYTHON_USE_PYLONG_INTERNALS && PY_MAJOR_VERSION >= 3
+ if (likely(PyLong_CheckExact(op1))) {
+ const long b = intval;
+ long a;
+ const digit* digits = ((PyLongObject*)op1)->ob_digit;
+ const Py_ssize_t size = Py_SIZE(op1);
+ if (likely(__Pyx_sst_abs(size) <= 1)) {
+ a = likely(size) ? digits[0] : 0;
+ if (size == -1) a = -a;
+ } else {
+ switch (size) {
+ case -2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ a = -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ }
+ case 2:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ a = (long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ }
+ case -3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ a = -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ }
+ case 3:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ a = (long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ }
+ case -4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ a = -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ }
+ case 4:
+ if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ a = (long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0]));
+ break;
+ }
+ #if PyLong_SHIFT < 30 && PyLong_SHIFT != 15
+ default: return PyLong_Type.tp_richcompare(op1, op2, Py_EQ);
+ #else
+ default: Py_RETURN_FALSE;
+ #endif
+ }
+ }
+ if (a == b) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ }
+ #endif
+ if (PyFloat_CheckExact(op1)) {
+ const long b = intval;
+ double a = PyFloat_AS_DOUBLE(op1);
+ if ((double)a == (double)b) {
+ Py_RETURN_TRUE;
+ } else {
+ Py_RETURN_FALSE;
+ }
+ }
+ return PyObject_RichCompare(op1, op2, Py_EQ);
+}
+#endif
+
+/* KeywordStringCheck */
+ static CYTHON_INLINE int __Pyx_CheckKeywordStrings(
+ PyObject *kwdict,
+ const char* function_name,
+ int kw_allowed)
+{
+ PyObject* key = 0;
+ Py_ssize_t pos = 0;
+#if CYTHON_COMPILING_IN_PYPY
+ if (!kw_allowed && PyDict_Next(kwdict, &pos, &key, 0))
+ goto invalid_keyword;
+ return 1;
+#else
+ while (PyDict_Next(kwdict, &pos, &key, 0)) {
+ #if PY_MAJOR_VERSION < 3
+ if (unlikely(!PyString_CheckExact(key)) && unlikely(!PyString_Check(key)))
+ #endif
+ if (unlikely(!PyUnicode_Check(key)))
+ goto invalid_keyword_type;
+ }
+ if ((!kw_allowed) && unlikely(key))
+ goto invalid_keyword;
+ return 1;
+invalid_keyword_type:
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() keywords must be strings", function_name);
+ return 0;
+#endif
+invalid_keyword:
+ PyErr_Format(PyExc_TypeError,
+ #if PY_MAJOR_VERSION < 3
+ "%.200s() got an unexpected keyword argument '%.200s'",
+ function_name, PyString_AsString(key));
+ #else
+ "%s() got an unexpected keyword argument '%U'",
+ function_name, key);
+ #endif
+ return 0;
+}
+
+/* BytesEquals */
+ static CYTHON_INLINE int __Pyx_PyBytes_Equals(PyObject* s1, PyObject* s2, int equals) {
+#if CYTHON_COMPILING_IN_PYPY
+ return PyObject_RichCompareBool(s1, s2, equals);
+#else
+ if (s1 == s2) {
+ return (equals == Py_EQ);
+ } else if (PyBytes_CheckExact(s1) & PyBytes_CheckExact(s2)) {
+ const char *ps1, *ps2;
+ Py_ssize_t length = PyBytes_GET_SIZE(s1);
+ if (length != PyBytes_GET_SIZE(s2))
+ return (equals == Py_NE);
+ ps1 = PyBytes_AS_STRING(s1);
+ ps2 = PyBytes_AS_STRING(s2);
+ if (ps1[0] != ps2[0]) {
+ return (equals == Py_NE);
+ } else if (length == 1) {
+ return (equals == Py_EQ);
+ } else {
+ int result = memcmp(ps1, ps2, (size_t)length);
+ return (equals == Py_EQ) ? (result == 0) : (result != 0);
+ }
+ } else if ((s1 == Py_None) & PyBytes_CheckExact(s2)) {
+ return (equals == Py_NE);
+ } else if ((s2 == Py_None) & PyBytes_CheckExact(s1)) {
+ return (equals == Py_NE);
+ } else {
+ int result;
+ PyObject* py_result = PyObject_RichCompare(s1, s2, equals);
+ if (!py_result)
+ return -1;
+ result = __Pyx_PyObject_IsTrue(py_result);
+ Py_DECREF(py_result);
+ return result;
+ }
+#endif
+}
+
+/* UnicodeEquals */
+ static CYTHON_INLINE int __Pyx_PyUnicode_Equals(PyObject* s1, PyObject* s2, int equals) {
+#if CYTHON_COMPILING_IN_PYPY
+ return PyObject_RichCompareBool(s1, s2, equals);
+#else
+#if PY_MAJOR_VERSION < 3
+ PyObject* owned_ref = NULL;
+#endif
+ int s1_is_unicode, s2_is_unicode;
+ if (s1 == s2) {
+ goto return_eq;
+ }
+ s1_is_unicode = PyUnicode_CheckExact(s1);
+ s2_is_unicode = PyUnicode_CheckExact(s2);
+#if PY_MAJOR_VERSION < 3
+ if ((s1_is_unicode & (!s2_is_unicode)) && PyString_CheckExact(s2)) {
+ owned_ref = PyUnicode_FromObject(s2);
+ if (unlikely(!owned_ref))
+ return -1;
+ s2 = owned_ref;
+ s2_is_unicode = 1;
+ } else if ((s2_is_unicode & (!s1_is_unicode)) && PyString_CheckExact(s1)) {
+ owned_ref = PyUnicode_FromObject(s1);
+ if (unlikely(!owned_ref))
+ return -1;
+ s1 = owned_ref;
+ s1_is_unicode = 1;
+ } else if (((!s2_is_unicode) & (!s1_is_unicode))) {
+ return __Pyx_PyBytes_Equals(s1, s2, equals);
+ }
+#endif
+ if (s1_is_unicode & s2_is_unicode) {
+ Py_ssize_t length;
+ int kind;
+ void *data1, *data2;
+ if (unlikely(__Pyx_PyUnicode_READY(s1) < 0) || unlikely(__Pyx_PyUnicode_READY(s2) < 0))
+ return -1;
+ length = __Pyx_PyUnicode_GET_LENGTH(s1);
+ if (length != __Pyx_PyUnicode_GET_LENGTH(s2)) {
+ goto return_ne;
+ }
+ kind = __Pyx_PyUnicode_KIND(s1);
+ if (kind != __Pyx_PyUnicode_KIND(s2)) {
+ goto return_ne;
+ }
+ data1 = __Pyx_PyUnicode_DATA(s1);
+ data2 = __Pyx_PyUnicode_DATA(s2);
+ if (__Pyx_PyUnicode_READ(kind, data1, 0) != __Pyx_PyUnicode_READ(kind, data2, 0)) {
+ goto return_ne;
+ } else if (length == 1) {
+ goto return_eq;
+ } else {
+ int result = memcmp(data1, data2, (size_t)(length * kind));
+ #if PY_MAJOR_VERSION < 3
+ Py_XDECREF(owned_ref);
+ #endif
+ return (equals == Py_EQ) ? (result == 0) : (result != 0);
+ }
+ } else if ((s1 == Py_None) & s2_is_unicode) {
+ goto return_ne;
+ } else if ((s2 == Py_None) & s1_is_unicode) {
+ goto return_ne;
+ } else {
+ int result;
+ PyObject* py_result = PyObject_RichCompare(s1, s2, equals);
+ if (!py_result)
+ return -1;
+ result = __Pyx_PyObject_IsTrue(py_result);
+ Py_DECREF(py_result);
+ return result;
+ }
+return_eq:
+ #if PY_MAJOR_VERSION < 3
+ Py_XDECREF(owned_ref);
+ #endif
+ return (equals == Py_EQ);
+return_ne:
+ #if PY_MAJOR_VERSION < 3
+ Py_XDECREF(owned_ref);
+ #endif
+ return (equals == Py_NE);
+#endif
+}
+
+/* SetVTable */
+ static int __Pyx_SetVtable(PyObject *dict, void *vtable) {
+#if PY_VERSION_HEX >= 0x02070000
+ PyObject *ob = PyCapsule_New(vtable, 0, 0);
+#else
+ PyObject *ob = PyCObject_FromVoidPtr(vtable, 0);
+#endif
+ if (!ob)
+ goto bad;
+ if (PyDict_SetItem(dict, __pyx_n_s_pyx_vtable, ob) < 0)
+ goto bad;
+ Py_DECREF(ob);
+ return 0;
+bad:
+ Py_XDECREF(ob);
+ return -1;
+}
+
+/* Import */
+ static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list, int level) {
+ PyObject *empty_list = 0;
+ PyObject *module = 0;
+ PyObject *global_dict = 0;
+ PyObject *empty_dict = 0;
+ PyObject *list;
+ #if PY_VERSION_HEX < 0x03030000
+ PyObject *py_import;
+ py_import = __Pyx_PyObject_GetAttrStr(__pyx_b, __pyx_n_s_import);
+ if (!py_import)
+ goto bad;
+ #endif
+ if (from_list)
+ list = from_list;
+ else {
+ empty_list = PyList_New(0);
+ if (!empty_list)
+ goto bad;
+ list = empty_list;
+ }
+ global_dict = PyModule_GetDict(__pyx_m);
+ if (!global_dict)
+ goto bad;
+ empty_dict = PyDict_New();
+ if (!empty_dict)
+ goto bad;
+ {
+ #if PY_MAJOR_VERSION >= 3
+ if (level == -1) {
+ if (strchr(__Pyx_MODULE_NAME, '.')) {
+ #if PY_VERSION_HEX < 0x03030000
+ PyObject *py_level = PyInt_FromLong(1);
+ if (!py_level)
+ goto bad;
+ module = PyObject_CallFunctionObjArgs(py_import,
+ name, global_dict, empty_dict, list, py_level, NULL);
+ Py_DECREF(py_level);
+ #else
+ module = PyImport_ImportModuleLevelObject(
+ name, global_dict, empty_dict, list, 1);
+ #endif
+ if (!module) {
+ if (!PyErr_ExceptionMatches(PyExc_ImportError))
+ goto bad;
+ PyErr_Clear();
+ }
+ }
+ level = 0;
+ }
+ #endif
+ if (!module) {
+ #if PY_VERSION_HEX < 0x03030000
+ PyObject *py_level = PyInt_FromLong(level);
+ if (!py_level)
+ goto bad;
+ module = PyObject_CallFunctionObjArgs(py_import,
+ name, global_dict, empty_dict, list, py_level, NULL);
+ Py_DECREF(py_level);
+ #else
+ module = PyImport_ImportModuleLevelObject(
+ name, global_dict, empty_dict, list, level);
+ #endif
+ }
+ }
+bad:
+ #if PY_VERSION_HEX < 0x03030000
+ Py_XDECREF(py_import);
+ #endif
+ Py_XDECREF(empty_list);
+ Py_XDECREF(empty_dict);
+ return module;
+}
+
+/* GetNameInClass */
+ static PyObject *__Pyx_GetNameInClass(PyObject *nmspace, PyObject *name) {
+ PyObject *result;
+ result = __Pyx_PyObject_GetAttrStr(nmspace, name);
+ if (!result)
+ result = __Pyx_GetModuleGlobalName(name);
+ return result;
+}
+
+/* CodeObjectCache */
+ static int __pyx_bisect_code_objects(__Pyx_CodeObjectCacheEntry* entries, int count, int code_line) {
+ int start = 0, mid = 0, end = count - 1;
+ if (end >= 0 && code_line > entries[end].code_line) {
+ return count;
+ }
+ while (start < end) {
+ mid = start + (end - start) / 2;
+ if (code_line < entries[mid].code_line) {
+ end = mid;
+ } else if (code_line > entries[mid].code_line) {
+ start = mid + 1;
+ } else {
+ return mid;
+ }
+ }
+ if (code_line <= entries[mid].code_line) {
+ return mid;
+ } else {
+ return mid + 1;
+ }
+}
+static PyCodeObject *__pyx_find_code_object(int code_line) {
+ PyCodeObject* code_object;
+ int pos;
+ if (unlikely(!code_line) || unlikely(!__pyx_code_cache.entries)) {
+ return NULL;
+ }
+ pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line);
+ if (unlikely(pos >= __pyx_code_cache.count) || unlikely(__pyx_code_cache.entries[pos].code_line != code_line)) {
+ return NULL;
+ }
+ code_object = __pyx_code_cache.entries[pos].code_object;
+ Py_INCREF(code_object);
+ return code_object;
+}
+static void __pyx_insert_code_object(int code_line, PyCodeObject* code_object) {
+ int pos, i;
+ __Pyx_CodeObjectCacheEntry* entries = __pyx_code_cache.entries;
+ if (unlikely(!code_line)) {
+ return;
+ }
+ if (unlikely(!entries)) {
+ entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Malloc(64*sizeof(__Pyx_CodeObjectCacheEntry));
+ if (likely(entries)) {
+ __pyx_code_cache.entries = entries;
+ __pyx_code_cache.max_count = 64;
+ __pyx_code_cache.count = 1;
+ entries[0].code_line = code_line;
+ entries[0].code_object = code_object;
+ Py_INCREF(code_object);
+ }
+ return;
+ }
+ pos = __pyx_bisect_code_objects(__pyx_code_cache.entries, __pyx_code_cache.count, code_line);
+ if ((pos < __pyx_code_cache.count) && unlikely(__pyx_code_cache.entries[pos].code_line == code_line)) {
+ PyCodeObject* tmp = entries[pos].code_object;
+ entries[pos].code_object = code_object;
+ Py_DECREF(tmp);
+ return;
+ }
+ if (__pyx_code_cache.count == __pyx_code_cache.max_count) {
+ int new_max = __pyx_code_cache.max_count + 64;
+ entries = (__Pyx_CodeObjectCacheEntry*)PyMem_Realloc(
+ __pyx_code_cache.entries, (size_t)new_max*sizeof(__Pyx_CodeObjectCacheEntry));
+ if (unlikely(!entries)) {
+ return;
+ }
+ __pyx_code_cache.entries = entries;
+ __pyx_code_cache.max_count = new_max;
+ }
+ for (i=__pyx_code_cache.count; i>pos; i--) {
+ entries[i] = entries[i-1];
+ }
+ entries[pos].code_line = code_line;
+ entries[pos].code_object = code_object;
+ __pyx_code_cache.count++;
+ Py_INCREF(code_object);
+}
+
+/* AddTraceback */
+ #include "compile.h"
+#include "frameobject.h"
+#include "traceback.h"
+static PyCodeObject* __Pyx_CreateCodeObjectForTraceback(
+ const char *funcname, int c_line,
+ int py_line, const char *filename) {
+ PyCodeObject *py_code = 0;
+ PyObject *py_srcfile = 0;
+ PyObject *py_funcname = 0;
+ #if PY_MAJOR_VERSION < 3
+ py_srcfile = PyString_FromString(filename);
+ #else
+ py_srcfile = PyUnicode_FromString(filename);
+ #endif
+ if (!py_srcfile) goto bad;
+ if (c_line) {
+ #if PY_MAJOR_VERSION < 3
+ py_funcname = PyString_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
+ #else
+ py_funcname = PyUnicode_FromFormat( "%s (%s:%d)", funcname, __pyx_cfilenm, c_line);
+ #endif
+ }
+ else {
+ #if PY_MAJOR_VERSION < 3
+ py_funcname = PyString_FromString(funcname);
+ #else
+ py_funcname = PyUnicode_FromString(funcname);
+ #endif
+ }
+ if (!py_funcname) goto bad;
+ py_code = __Pyx_PyCode_New(
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ __pyx_empty_bytes, /*PyObject *code,*/
+ __pyx_empty_tuple, /*PyObject *consts,*/
+ __pyx_empty_tuple, /*PyObject *names,*/
+ __pyx_empty_tuple, /*PyObject *varnames,*/
+ __pyx_empty_tuple, /*PyObject *freevars,*/
+ __pyx_empty_tuple, /*PyObject *cellvars,*/
+ py_srcfile, /*PyObject *filename,*/
+ py_funcname, /*PyObject *name,*/
+ py_line,
+ __pyx_empty_bytes /*PyObject *lnotab*/
+ );
+ Py_DECREF(py_srcfile);
+ Py_DECREF(py_funcname);
+ return py_code;
+bad:
+ Py_XDECREF(py_srcfile);
+ Py_XDECREF(py_funcname);
+ return NULL;
+}
+static void __Pyx_AddTraceback(const char *funcname, int c_line,
+ int py_line, const char *filename) {
+ PyCodeObject *py_code = 0;
+ PyFrameObject *py_frame = 0;
+ py_code = __pyx_find_code_object(c_line ? c_line : py_line);
+ if (!py_code) {
+ py_code = __Pyx_CreateCodeObjectForTraceback(
+ funcname, c_line, py_line, filename);
+ if (!py_code) goto bad;
+ __pyx_insert_code_object(c_line ? c_line : py_line, py_code);
+ }
+ py_frame = PyFrame_New(
+ PyThreadState_GET(), /*PyThreadState *tstate,*/
+ py_code, /*PyCodeObject *code,*/
+ __pyx_d, /*PyObject *globals,*/
+ 0 /*PyObject *locals*/
+ );
+ if (!py_frame) goto bad;
+ py_frame->f_lineno = py_line;
+ PyTraceBack_Here(py_frame);
+bad:
+ Py_XDECREF(py_code);
+ Py_XDECREF(py_frame);
+}
+
+/* CIntFromPyVerify */
+ #define __PYX_VERIFY_RETURN_INT(target_type, func_type, func_value)\
+ __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 0)
+#define __PYX_VERIFY_RETURN_INT_EXC(target_type, func_type, func_value)\
+ __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, 1)
+#define __PYX__VERIFY_RETURN_INT(target_type, func_type, func_value, exc)\
+ {\
+ func_type value = func_value;\
+ if (sizeof(target_type) < sizeof(func_type)) {\
+ if (unlikely(value != (func_type) (target_type) value)) {\
+ func_type zero = 0;\
+ if (exc && unlikely(value == (func_type)-1 && PyErr_Occurred()))\
+ return (target_type) -1;\
+ if (is_unsigned && unlikely(value < zero))\
+ goto raise_neg_overflow;\
+ else\
+ goto raise_overflow;\
+ }\
+ }\
+ return (target_type) value;\
+ }
+
+/* CIntToPy */
+ static CYTHON_INLINE PyObject* __Pyx_PyInt_From_int(int value) {
+ const int neg_one = (int) -1, const_zero = (int) 0;
+ const int is_unsigned = neg_one > const_zero;
+ if (is_unsigned) {
+ if (sizeof(int) < sizeof(long)) {
+ return PyInt_FromLong((long) value);
+ } else if (sizeof(int) <= sizeof(unsigned long)) {
+ return PyLong_FromUnsignedLong((unsigned long) value);
+ } else if (sizeof(int) <= sizeof(unsigned PY_LONG_LONG)) {
+ return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value);
+ }
+ } else {
+ if (sizeof(int) <= sizeof(long)) {
+ return PyInt_FromLong((long) value);
+ } else if (sizeof(int) <= sizeof(PY_LONG_LONG)) {
+ return PyLong_FromLongLong((PY_LONG_LONG) value);
+ }
+ }
+ {
+ int one = 1; int little = (int)*(unsigned char *)&one;
+ unsigned char *bytes = (unsigned char *)&value;
+ return _PyLong_FromByteArray(bytes, sizeof(int),
+ little, !is_unsigned);
+ }
+}
+
+/* CIntFromPy */
+ static CYTHON_INLINE int __Pyx_PyInt_As_int(PyObject *x) {
+ const int neg_one = (int) -1, const_zero = (int) 0;
+ const int is_unsigned = neg_one > const_zero;
+#if PY_MAJOR_VERSION < 3
+ if (likely(PyInt_Check(x))) {
+ if (sizeof(int) < sizeof(long)) {
+ __PYX_VERIFY_RETURN_INT(int, long, PyInt_AS_LONG(x))
+ } else {
+ long val = PyInt_AS_LONG(x);
+ if (is_unsigned && unlikely(val < 0)) {
+ goto raise_neg_overflow;
+ }
+ return (int) val;
+ }
+ } else
+#endif
+ if (likely(PyLong_Check(x))) {
+ if (is_unsigned) {
+#if CYTHON_USE_PYLONG_INTERNALS
+ const digit* digits = ((PyLongObject*)x)->ob_digit;
+ switch (Py_SIZE(x)) {
+ case 0: return (int) 0;
+ case 1: __PYX_VERIFY_RETURN_INT(int, digit, digits[0])
+ case 2:
+ if (8 * sizeof(int) > 1 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(int) >= 2 * PyLong_SHIFT) {
+ return (int) (((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0]));
+ }
+ }
+ break;
+ case 3:
+ if (8 * sizeof(int) > 2 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 3 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(int) >= 3 * PyLong_SHIFT) {
+ return (int) (((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]));
+ }
+ }
+ break;
+ case 4:
+ if (8 * sizeof(int) > 3 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 4 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(int) >= 4 * PyLong_SHIFT) {
+ return (int) (((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0]));
+ }
+ }
+ break;
+ }
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (unlikely(Py_SIZE(x) < 0)) {
+ goto raise_neg_overflow;
+ }
+#else
+ {
+ int result = PyObject_RichCompareBool(x, Py_False, Py_LT);
+ if (unlikely(result < 0))
+ return (int) -1;
+ if (unlikely(result == 1))
+ goto raise_neg_overflow;
+ }
+#endif
+ if (sizeof(int) <= sizeof(unsigned long)) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, unsigned long, PyLong_AsUnsignedLong(x))
+ } else if (sizeof(int) <= sizeof(unsigned PY_LONG_LONG)) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x))
+ }
+ } else {
+#if CYTHON_USE_PYLONG_INTERNALS
+ const digit* digits = ((PyLongObject*)x)->ob_digit;
+ switch (Py_SIZE(x)) {
+ case 0: return (int) 0;
+ case -1: __PYX_VERIFY_RETURN_INT(int, sdigit, (sdigit) (-(sdigit)digits[0]))
+ case 1: __PYX_VERIFY_RETURN_INT(int, digit, +digits[0])
+ case -2:
+ if (8 * sizeof(int) - 1 > 1 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(int) - 1 > 2 * PyLong_SHIFT) {
+ return (int) (((int)-1)*(((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case 2:
+ if (8 * sizeof(int) > 1 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(int) - 1 > 2 * PyLong_SHIFT) {
+ return (int) ((((((int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case -3:
+ if (8 * sizeof(int) - 1 > 2 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 3 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(int) - 1 > 3 * PyLong_SHIFT) {
+ return (int) (((int)-1)*(((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case 3:
+ if (8 * sizeof(int) > 2 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 3 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(int) - 1 > 3 * PyLong_SHIFT) {
+ return (int) ((((((((int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case -4:
+ if (8 * sizeof(int) - 1 > 3 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 4 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(int, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(int) - 1 > 4 * PyLong_SHIFT) {
+ return (int) (((int)-1)*(((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ case 4:
+ if (8 * sizeof(int) > 3 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 4 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(int, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(int) - 1 > 4 * PyLong_SHIFT) {
+ return (int) ((((((((((int)digits[3]) << PyLong_SHIFT) | (int)digits[2]) << PyLong_SHIFT) | (int)digits[1]) << PyLong_SHIFT) | (int)digits[0])));
+ }
+ }
+ break;
+ }
+#endif
+ if (sizeof(int) <= sizeof(long)) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, long, PyLong_AsLong(x))
+ } else if (sizeof(int) <= sizeof(PY_LONG_LONG)) {
+ __PYX_VERIFY_RETURN_INT_EXC(int, PY_LONG_LONG, PyLong_AsLongLong(x))
+ }
+ }
+ {
+#if CYTHON_COMPILING_IN_PYPY && !defined(_PyLong_AsByteArray)
+ PyErr_SetString(PyExc_RuntimeError,
+ "_PyLong_AsByteArray() not available in PyPy, cannot convert large numbers");
+#else
+ int val;
+ PyObject *v = __Pyx_PyNumber_IntOrLong(x);
+ #if PY_MAJOR_VERSION < 3
+ if (likely(v) && !PyLong_Check(v)) {
+ PyObject *tmp = v;
+ v = PyNumber_Long(tmp);
+ Py_DECREF(tmp);
+ }
+ #endif
+ if (likely(v)) {
+ int one = 1; int is_little = (int)*(unsigned char *)&one;
+ unsigned char *bytes = (unsigned char *)&val;
+ int ret = _PyLong_AsByteArray((PyLongObject *)v,
+ bytes, sizeof(val),
+ is_little, !is_unsigned);
+ Py_DECREF(v);
+ if (likely(!ret))
+ return val;
+ }
+#endif
+ return (int) -1;
+ }
+ } else {
+ int val;
+ PyObject *tmp = __Pyx_PyNumber_IntOrLong(x);
+ if (!tmp) return (int) -1;
+ val = __Pyx_PyInt_As_int(tmp);
+ Py_DECREF(tmp);
+ return val;
+ }
+raise_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "value too large to convert to int");
+ return (int) -1;
+raise_neg_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "can't convert negative value to int");
+ return (int) -1;
+}
+
+/* CIntToPy */
+ static CYTHON_INLINE PyObject* __Pyx_PyInt_From_long(long value) {
+ const long neg_one = (long) -1, const_zero = (long) 0;
+ const int is_unsigned = neg_one > const_zero;
+ if (is_unsigned) {
+ if (sizeof(long) < sizeof(long)) {
+ return PyInt_FromLong((long) value);
+ } else if (sizeof(long) <= sizeof(unsigned long)) {
+ return PyLong_FromUnsignedLong((unsigned long) value);
+ } else if (sizeof(long) <= sizeof(unsigned PY_LONG_LONG)) {
+ return PyLong_FromUnsignedLongLong((unsigned PY_LONG_LONG) value);
+ }
+ } else {
+ if (sizeof(long) <= sizeof(long)) {
+ return PyInt_FromLong((long) value);
+ } else if (sizeof(long) <= sizeof(PY_LONG_LONG)) {
+ return PyLong_FromLongLong((PY_LONG_LONG) value);
+ }
+ }
+ {
+ int one = 1; int little = (int)*(unsigned char *)&one;
+ unsigned char *bytes = (unsigned char *)&value;
+ return _PyLong_FromByteArray(bytes, sizeof(long),
+ little, !is_unsigned);
+ }
+}
+
+/* CIntFromPy */
+ static CYTHON_INLINE long __Pyx_PyInt_As_long(PyObject *x) {
+ const long neg_one = (long) -1, const_zero = (long) 0;
+ const int is_unsigned = neg_one > const_zero;
+#if PY_MAJOR_VERSION < 3
+ if (likely(PyInt_Check(x))) {
+ if (sizeof(long) < sizeof(long)) {
+ __PYX_VERIFY_RETURN_INT(long, long, PyInt_AS_LONG(x))
+ } else {
+ long val = PyInt_AS_LONG(x);
+ if (is_unsigned && unlikely(val < 0)) {
+ goto raise_neg_overflow;
+ }
+ return (long) val;
+ }
+ } else
+#endif
+ if (likely(PyLong_Check(x))) {
+ if (is_unsigned) {
+#if CYTHON_USE_PYLONG_INTERNALS
+ const digit* digits = ((PyLongObject*)x)->ob_digit;
+ switch (Py_SIZE(x)) {
+ case 0: return (long) 0;
+ case 1: __PYX_VERIFY_RETURN_INT(long, digit, digits[0])
+ case 2:
+ if (8 * sizeof(long) > 1 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(long) >= 2 * PyLong_SHIFT) {
+ return (long) (((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0]));
+ }
+ }
+ break;
+ case 3:
+ if (8 * sizeof(long) > 2 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 3 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(long) >= 3 * PyLong_SHIFT) {
+ return (long) (((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]));
+ }
+ }
+ break;
+ case 4:
+ if (8 * sizeof(long) > 3 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 4 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(long) >= 4 * PyLong_SHIFT) {
+ return (long) (((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0]));
+ }
+ }
+ break;
+ }
+#endif
+#if CYTHON_COMPILING_IN_CPYTHON
+ if (unlikely(Py_SIZE(x) < 0)) {
+ goto raise_neg_overflow;
+ }
+#else
+ {
+ int result = PyObject_RichCompareBool(x, Py_False, Py_LT);
+ if (unlikely(result < 0))
+ return (long) -1;
+ if (unlikely(result == 1))
+ goto raise_neg_overflow;
+ }
+#endif
+ if (sizeof(long) <= sizeof(unsigned long)) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, unsigned long, PyLong_AsUnsignedLong(x))
+ } else if (sizeof(long) <= sizeof(unsigned PY_LONG_LONG)) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong(x))
+ }
+ } else {
+#if CYTHON_USE_PYLONG_INTERNALS
+ const digit* digits = ((PyLongObject*)x)->ob_digit;
+ switch (Py_SIZE(x)) {
+ case 0: return (long) 0;
+ case -1: __PYX_VERIFY_RETURN_INT(long, sdigit, (sdigit) (-(sdigit)digits[0]))
+ case 1: __PYX_VERIFY_RETURN_INT(long, digit, +digits[0])
+ case -2:
+ if (8 * sizeof(long) - 1 > 1 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ return (long) (((long)-1)*(((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case 2:
+ if (8 * sizeof(long) > 1 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 2 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ return (long) ((((((long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case -3:
+ if (8 * sizeof(long) - 1 > 2 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 3 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ return (long) (((long)-1)*(((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case 3:
+ if (8 * sizeof(long) > 2 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 3 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ return (long) ((((((((long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case -4:
+ if (8 * sizeof(long) - 1 > 3 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 4 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(long, long, -(long) (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ return (long) (((long)-1)*(((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ case 4:
+ if (8 * sizeof(long) > 3 * PyLong_SHIFT) {
+ if (8 * sizeof(unsigned long) > 4 * PyLong_SHIFT) {
+ __PYX_VERIFY_RETURN_INT(long, unsigned long, (((((((((unsigned long)digits[3]) << PyLong_SHIFT) | (unsigned long)digits[2]) << PyLong_SHIFT) | (unsigned long)digits[1]) << PyLong_SHIFT) | (unsigned long)digits[0])))
+ } else if (8 * sizeof(long) - 1 > 4 * PyLong_SHIFT) {
+ return (long) ((((((((((long)digits[3]) << PyLong_SHIFT) | (long)digits[2]) << PyLong_SHIFT) | (long)digits[1]) << PyLong_SHIFT) | (long)digits[0])));
+ }
+ }
+ break;
+ }
+#endif
+ if (sizeof(long) <= sizeof(long)) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, long, PyLong_AsLong(x))
+ } else if (sizeof(long) <= sizeof(PY_LONG_LONG)) {
+ __PYX_VERIFY_RETURN_INT_EXC(long, PY_LONG_LONG, PyLong_AsLongLong(x))
+ }
+ }
+ {
+#if CYTHON_COMPILING_IN_PYPY && !defined(_PyLong_AsByteArray)
+ PyErr_SetString(PyExc_RuntimeError,
+ "_PyLong_AsByteArray() not available in PyPy, cannot convert large numbers");
+#else
+ long val;
+ PyObject *v = __Pyx_PyNumber_IntOrLong(x);
+ #if PY_MAJOR_VERSION < 3
+ if (likely(v) && !PyLong_Check(v)) {
+ PyObject *tmp = v;
+ v = PyNumber_Long(tmp);
+ Py_DECREF(tmp);
+ }
+ #endif
+ if (likely(v)) {
+ int one = 1; int is_little = (int)*(unsigned char *)&one;
+ unsigned char *bytes = (unsigned char *)&val;
+ int ret = _PyLong_AsByteArray((PyLongObject *)v,
+ bytes, sizeof(val),
+ is_little, !is_unsigned);
+ Py_DECREF(v);
+ if (likely(!ret))
+ return val;
+ }
+#endif
+ return (long) -1;
+ }
+ } else {
+ long val;
+ PyObject *tmp = __Pyx_PyNumber_IntOrLong(x);
+ if (!tmp) return (long) -1;
+ val = __Pyx_PyInt_As_long(tmp);
+ Py_DECREF(tmp);
+ return val;
+ }
+raise_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "value too large to convert to long");
+ return (long) -1;
+raise_neg_overflow:
+ PyErr_SetString(PyExc_OverflowError,
+ "can't convert negative value to long");
+ return (long) -1;
+}
+
+/* CheckBinaryVersion */
+ static int __Pyx_check_binary_version(void) {
+ char ctversion[4], rtversion[4];
+ PyOS_snprintf(ctversion, 4, "%d.%d", PY_MAJOR_VERSION, PY_MINOR_VERSION);
+ PyOS_snprintf(rtversion, 4, "%s", Py_GetVersion());
+ if (ctversion[0] != rtversion[0] || ctversion[2] != rtversion[2]) {
+ char message[200];
+ PyOS_snprintf(message, sizeof(message),
+ "compiletime version %s of module '%.100s' "
+ "does not match runtime version %s",
+ ctversion, __Pyx_MODULE_NAME, rtversion);
+ return PyErr_WarnEx(NULL, message, 1);
+ }
+ return 0;
+}
+
+/* InitStrings */
+ static int __Pyx_InitStrings(__Pyx_StringTabEntry *t) {
+ while (t->p) {
+ #if PY_MAJOR_VERSION < 3
+ if (t->is_unicode) {
+ *t->p = PyUnicode_DecodeUTF8(t->s, t->n - 1, NULL);
+ } else if (t->intern) {
+ *t->p = PyString_InternFromString(t->s);
+ } else {
+ *t->p = PyString_FromStringAndSize(t->s, t->n - 1);
+ }
+ #else
+ if (t->is_unicode | t->is_str) {
+ if (t->intern) {
+ *t->p = PyUnicode_InternFromString(t->s);
+ } else if (t->encoding) {
+ *t->p = PyUnicode_Decode(t->s, t->n - 1, t->encoding, NULL);
+ } else {
+ *t->p = PyUnicode_FromStringAndSize(t->s, t->n - 1);
+ }
+ } else {
+ *t->p = PyBytes_FromStringAndSize(t->s, t->n - 1);
+ }
+ #endif
+ if (!*t->p)
+ return -1;
+ ++t;
+ }
+ return 0;
+}
+
+static CYTHON_INLINE PyObject* __Pyx_PyUnicode_FromString(const char* c_str) {
+ return __Pyx_PyUnicode_FromStringAndSize(c_str, (Py_ssize_t)strlen(c_str));
+}
+static CYTHON_INLINE char* __Pyx_PyObject_AsString(PyObject* o) {
+ Py_ssize_t ignore;
+ return __Pyx_PyObject_AsStringAndSize(o, &ignore);
+}
+static CYTHON_INLINE char* __Pyx_PyObject_AsStringAndSize(PyObject* o, Py_ssize_t *length) {
+#if CYTHON_COMPILING_IN_CPYTHON && (__PYX_DEFAULT_STRING_ENCODING_IS_ASCII || __PYX_DEFAULT_STRING_ENCODING_IS_DEFAULT)
+ if (
+#if PY_MAJOR_VERSION < 3 && __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ __Pyx_sys_getdefaultencoding_not_ascii &&
+#endif
+ PyUnicode_Check(o)) {
+#if PY_VERSION_HEX < 0x03030000
+ char* defenc_c;
+ PyObject* defenc = _PyUnicode_AsDefaultEncodedString(o, NULL);
+ if (!defenc) return NULL;
+ defenc_c = PyBytes_AS_STRING(defenc);
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ {
+ char* end = defenc_c + PyBytes_GET_SIZE(defenc);
+ char* c;
+ for (c = defenc_c; c < end; c++) {
+ if ((unsigned char) (*c) >= 128) {
+ PyUnicode_AsASCIIString(o);
+ return NULL;
+ }
+ }
+ }
+#endif
+ *length = PyBytes_GET_SIZE(defenc);
+ return defenc_c;
+#else
+ if (__Pyx_PyUnicode_READY(o) == -1) return NULL;
+#if __PYX_DEFAULT_STRING_ENCODING_IS_ASCII
+ if (PyUnicode_IS_ASCII(o)) {
+ *length = PyUnicode_GET_LENGTH(o);
+ return PyUnicode_AsUTF8(o);
+ } else {
+ PyUnicode_AsASCIIString(o);
+ return NULL;
+ }
+#else
+ return PyUnicode_AsUTF8AndSize(o, length);
+#endif
+#endif
+ } else
+#endif
+#if (!CYTHON_COMPILING_IN_PYPY) || (defined(PyByteArray_AS_STRING) && defined(PyByteArray_GET_SIZE))
+ if (PyByteArray_Check(o)) {
+ *length = PyByteArray_GET_SIZE(o);
+ return PyByteArray_AS_STRING(o);
+ } else
+#endif
+ {
+ char* result;
+ int r = PyBytes_AsStringAndSize(o, &result, length);
+ if (unlikely(r < 0)) {
+ return NULL;
+ } else {
+ return result;
+ }
+ }
+}
+static CYTHON_INLINE int __Pyx_PyObject_IsTrue(PyObject* x) {
+ int is_true = x == Py_True;
+ if (is_true | (x == Py_False) | (x == Py_None)) return is_true;
+ else return PyObject_IsTrue(x);
+}
+static CYTHON_INLINE PyObject* __Pyx_PyNumber_IntOrLong(PyObject* x) {
+ PyNumberMethods *m;
+ const char *name = NULL;
+ PyObject *res = NULL;
+#if PY_MAJOR_VERSION < 3
+ if (PyInt_Check(x) || PyLong_Check(x))
+#else
+ if (PyLong_Check(x))
+#endif
+ return __Pyx_NewRef(x);
+ m = Py_TYPE(x)->tp_as_number;
+#if PY_MAJOR_VERSION < 3
+ if (m && m->nb_int) {
+ name = "int";
+ res = PyNumber_Int(x);
+ }
+ else if (m && m->nb_long) {
+ name = "long";
+ res = PyNumber_Long(x);
+ }
+#else
+ if (m && m->nb_int) {
+ name = "int";
+ res = PyNumber_Long(x);
+ }
+#endif
+ if (res) {
+#if PY_MAJOR_VERSION < 3
+ if (!PyInt_Check(res) && !PyLong_Check(res)) {
+#else
+ if (!PyLong_Check(res)) {
+#endif
+ PyErr_Format(PyExc_TypeError,
+ "__%.4s__ returned non-%.4s (type %.200s)",
+ name, name, Py_TYPE(res)->tp_name);
+ Py_DECREF(res);
+ return NULL;
+ }
+ }
+ else if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_TypeError,
+ "an integer is required");
+ }
+ return res;
+}
+static CYTHON_INLINE Py_ssize_t __Pyx_PyIndex_AsSsize_t(PyObject* b) {
+ Py_ssize_t ival;
+ PyObject *x;
+#if PY_MAJOR_VERSION < 3
+ if (likely(PyInt_CheckExact(b))) {
+ if (sizeof(Py_ssize_t) >= sizeof(long))
+ return PyInt_AS_LONG(b);
+ else
+ return PyInt_AsSsize_t(x);
+ }
+#endif
+ if (likely(PyLong_CheckExact(b))) {
+ #if CYTHON_USE_PYLONG_INTERNALS
+ const digit* digits = ((PyLongObject*)b)->ob_digit;
+ const Py_ssize_t size = Py_SIZE(b);
+ if (likely(__Pyx_sst_abs(size) <= 1)) {
+ ival = likely(size) ? digits[0] : 0;
+ if (size == -1) ival = -ival;
+ return ival;
+ } else {
+ switch (size) {
+ case 2:
+ if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
+ return (Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case -2:
+ if (8 * sizeof(Py_ssize_t) > 2 * PyLong_SHIFT) {
+ return -(Py_ssize_t) (((((size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case 3:
+ if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) {
+ return (Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case -3:
+ if (8 * sizeof(Py_ssize_t) > 3 * PyLong_SHIFT) {
+ return -(Py_ssize_t) (((((((size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case 4:
+ if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) {
+ return (Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ case -4:
+ if (8 * sizeof(Py_ssize_t) > 4 * PyLong_SHIFT) {
+ return -(Py_ssize_t) (((((((((size_t)digits[3]) << PyLong_SHIFT) | (size_t)digits[2]) << PyLong_SHIFT) | (size_t)digits[1]) << PyLong_SHIFT) | (size_t)digits[0]));
+ }
+ break;
+ }
+ }
+ #endif
+ return PyLong_AsSsize_t(b);
+ }
+ x = PyNumber_Index(b);
+ if (!x) return -1;
+ ival = PyInt_AsSsize_t(x);
+ Py_DECREF(x);
+ return ival;
+}
+static CYTHON_INLINE PyObject * __Pyx_PyInt_FromSize_t(size_t ival) {
+ return PyInt_FromSize_t(ival);
+}
+
+
+#endif /* Py_PYTHON_H */
diff --git a/skbio/metadata/_intersection.pyx b/skbio/metadata/_intersection.pyx
new file mode 100644
index 0000000..9d58368
--- /dev/null
+++ b/skbio/metadata/_intersection.pyx
@@ -0,0 +1,529 @@
+# -----------------------------------------------------------------------------
+# This code is taken from bx-python project and added with a new function
+# `update` from line 195 to 211. The license for this code is included in
+# licenses/bx_python.txt.
+# -----------------------------------------------------------------------------
+
+"""
+Data structure for performing intersect queries on a set of intervals which
+preserves all information about the intervals (unlike bitset projection methods).
+
+:Authors: James Taylor (james at jamestaylor.org),
+ Ian Schenk (ian.schenck at gmail.com),
+ Brent Pedersen (bpederse at gmail.com)
+"""
+
+# Historical note:
+# This module original contained an implementation based on sorted endpoints
+# and a binary search, using an idea from Scott Schwartz and Piotr Berman.
+# Later an interval tree implementation was implemented by Ian for Galaxy's
+# join tool (see `bx.intervals.operations.quicksect.py`). This was then
+# converted to Cython by Brent, who also added support for
+# upstream/downstream/neighbor queries. This was modified by James to
+# handle half-open intervals strictly, to maintain sort order, and to
+# implement the same interface as the original Intersecter.
+
+#cython: cdivision=True
+
+import operator
+
+cdef extern from "stdlib.h":
+ int ceil(float f)
+ float log(float f)
+ int RAND_MAX
+ int rand()
+ int strlen(char *)
+ int iabs(int)
+
+cdef inline int imax2(int a, int b):
+ if b > a: return b
+ return a
+
+cdef inline int imax3(int a, int b, int c):
+ if b > a:
+ if c > b:
+ return c
+ return b
+ if a > c:
+ return a
+ return c
+
+cdef inline int imin3(int a, int b, int c):
+ if b < a:
+ if c < b:
+ return c
+ return b
+ if a < c:
+ return a
+ return c
+
+cdef inline int imin2(int a, int b):
+ if b < a: return b
+ return a
+
+cdef float nlog = -1.0 / log(0.5)
+
+cdef class IntervalNode:
+ """
+ A single node of an `IntervalTree`.
+
+ NOTE: Unless you really know what you are doing, you probably should us
+ `IntervalTree` rather than using this directly.
+ """
+ cdef float priority
+ cdef public object interval
+ cdef public int start, end
+ cdef int minend, maxend, minstart
+ cdef IntervalNode cleft, cright, croot
+
+ property left_node:
+ def __get__(self):
+ return self.cleft if self.cleft is not EmptyNode else None
+ property right_node:
+ def __get__(self):
+ return self.cright if self.cright is not EmptyNode else None
+ property root_node:
+ def __get__(self):
+ return self.croot if self.croot is not EmptyNode else None
+
+ def __repr__(self):
+ return "IntervalNode(%i, %i)" % (self.start, self.end)
+
+ def __cinit__(IntervalNode self, int start, int end, object interval):
+ # Python lacks the binomial distribution, so we convert a
+ # uniform into a binomial because it naturally scales with
+ # tree size. Also, python's uniform is perfect since the
+ # upper limit is not inclusive, which gives us undefined here.
+ self.priority = ceil(nlog * log(-1.0/(1.0 * rand()/RAND_MAX - 1)))
+ self.start = start
+ self.end = end
+ self.interval = interval
+ self.maxend = end
+ self.minstart = start
+ self.minend = end
+ self.cleft = EmptyNode
+ self.cright = EmptyNode
+ self.croot = EmptyNode
+
+ cpdef IntervalNode insert(IntervalNode self, int start, int end, object interval):
+ """
+ Insert a new IntervalNode into the tree of which this node is
+ currently the root. The return value is the new root of the tree (which
+ may or may not be this node!)
+ """
+ cdef IntervalNode croot = self
+ # If starts are the same, decide which to add interval to based on
+ # end, thus maintaining sortedness relative to start/end
+ cdef int decision_endpoint = start
+ if start == self.start:
+ decision_endpoint = end
+
+ if decision_endpoint > self.start:
+ # insert to cright tree
+ if self.cright is not EmptyNode:
+ self.cright = self.cright.insert( start, end, interval )
+ else:
+ self.cright = IntervalNode( start, end, interval )
+ # rebalance tree
+ if self.priority < self.cright.priority:
+ croot = self.rotate_left()
+ else:
+ # insert to cleft tree
+ if self.cleft is not EmptyNode:
+ self.cleft = self.cleft.insert( start, end, interval)
+ else:
+ self.cleft = IntervalNode( start, end, interval)
+ # rebalance tree
+ if self.priority < self.cleft.priority:
+ croot = self.rotate_right()
+
+ croot.set_ends()
+ self.cleft.croot = croot
+ self.cright.croot = croot
+ return croot
+
+ cdef IntervalNode rotate_right(IntervalNode self):
+ cdef IntervalNode croot = self.cleft
+ self.cleft = self.cleft.cright
+ croot.cright = self
+ self.set_ends()
+ return croot
+
+ cdef IntervalNode rotate_left(IntervalNode self):
+ cdef IntervalNode croot = self.cright
+ self.cright = self.cright.cleft
+ croot.cleft = self
+ self.set_ends()
+ return croot
+
+ cdef inline void set_ends(IntervalNode self):
+ if self.cright is not EmptyNode and self.cleft is not EmptyNode:
+ self.maxend = imax3(self.end, self.cright.maxend, self.cleft.maxend)
+ self.minend = imin3(self.end, self.cright.minend, self.cleft.minend)
+ self.minstart = imin3(self.start, self.cright.minstart, self.cleft.minstart)
+ elif self.cright is not EmptyNode:
+ self.maxend = imax2(self.end, self.cright.maxend)
+ self.minend = imin2(self.end, self.cright.minend)
+ self.minstart = imin2(self.start, self.cright.minstart)
+ elif self.cleft is not EmptyNode:
+ self.maxend = imax2(self.end, self.cleft.maxend)
+ self.minend = imin2(self.end, self.cleft.minend)
+ self.minstart = imin2(self.start, self.cleft.minstart)
+
+ def intersect( self, int start, int end, sort=True ):
+ """
+ given a start and a end, return a list of features
+ falling within that range
+ """
+ cdef list results = []
+ self._intersect( start, end, results )
+ return results
+
+ find = intersect
+
+ cdef void _intersect( IntervalNode self, int start, int end, list results):
+ cdef int send, qend
+
+ # Left subtree
+ if self.cleft is not EmptyNode and self.cleft.maxend >= start:
+ self.cleft._intersect( start, end, results )
+ # This interval
+ if start == end:
+ qend = end
+ else:
+ qend = end - 1
+ if self.end == self.start:
+ send = self.end
+ else:
+ send = self.end - 1
+ if ( send >= start ) and ( self.start <= qend ):
+ results.append( self.interval )
+ # Right subtree
+ if self.cright is not EmptyNode and self.start <= qend:
+ self.cright._intersect( start, end, results )
+
+ cpdef void update(IntervalNode self, int start, int end,
+ object old_feature, object new_feature):
+ """
+ given a start and end, replace all objects that
+ match the old_feature with new_feature.
+ """
+
+ # Left subtree
+ if self.cleft is not EmptyNode and self.cleft.maxend > start:
+ self.cleft.update( start, end, old_feature, new_feature )
+ # This interval
+ if ( self.end > start ) and ( self.start < end ):
+ if self.interval == old_feature:
+ self.interval = new_feature
+ # Right subtree
+ if self.cright is not EmptyNode and self.start < end:
+ self.cright.update( start, end, old_feature, new_feature )
+
+ cdef void _seek_left(IntervalNode self, int position, list results, int n, int max_dist):
+ # we know we can bail in these 2 cases.
+ if self.maxend + max_dist < position:
+ return
+ if self.minstart > position:
+ return
+
+ # the ordering of these 3 blocks makes it so the results are
+ # ordered nearest to farest from the query position
+ if self.cright is not EmptyNode:
+ self.cright._seek_left(position, results, n, max_dist)
+
+ if -1 < position - self.end < max_dist:
+ results.append(self.interval)
+
+ # TODO: can these conditionals be more stringent?
+ if self.cleft is not EmptyNode:
+ self.cleft._seek_left(position, results, n, max_dist)
+
+
+
+ cdef void _seek_right(IntervalNode self, int position, list results, int n, int max_dist):
+ # we know we can bail in these 2 cases.
+ if self.maxend < position: return
+ if self.minstart - max_dist > position: return
+
+ #print "SEEK_RIGHT:",self, self.cleft, self.maxend, self.minstart, position
+
+ # the ordering of these 3 blocks makes it so the results are
+ # ordered nearest to farest from the query position
+ if self.cleft is not EmptyNode:
+ self.cleft._seek_right(position, results, n, max_dist)
+
+ if -1 < self.start - position < max_dist:
+ results.append(self.interval)
+
+ if self.cright is not EmptyNode:
+ self.cright._seek_right(position, results, n, max_dist)
+
+
+ cpdef left(self, position, int n=1, int max_dist=2500):
+ """
+ find n features with a start > than `position`
+ f: a Interval object (or anything with an `end` attribute)
+ n: the number of features to return
+ max_dist: the maximum distance to look before giving up.
+ """
+ cdef list results = []
+ # use start - 1 becuase .left() assumes strictly left-of
+ self._seek_left( position - 1, results, n, max_dist )
+ if len(results) == n: return results
+ r = results
+ r.sort(key=operator.attrgetter('end'), reverse=True)
+ return r[:n]
+
+ cpdef right(self, position, int n=1, int max_dist=2500):
+ """
+ find n features with a end < than position
+ f: a Interval object (or anything with a `start` attribute)
+ n: the number of features to return
+ max_dist: the maximum distance to look before giving up.
+ """
+ cdef list results = []
+ # use end + 1 becuase .right() assumes strictly right-of
+ self._seek_right(position + 1, results, n, max_dist)
+ if len(results) == n: return results
+ r = results
+ r.sort(key=operator.attrgetter('start'))
+ return r[:n]
+
+ def traverse(self, func):
+ self._traverse(func)
+
+ cdef void _traverse(IntervalNode self, object func):
+ if self.cleft is not EmptyNode: self.cleft._traverse(func)
+ func(self)
+ if self.cright is not EmptyNode: self.cright._traverse(func)
+
+cdef IntervalNode EmptyNode = IntervalNode( 0, 0, IntervalObj(0, 0))
+
+## ---- Wrappers that retain the old interface -------------------------------
+
+cdef class IntervalObj:
+ """
+ Basic feature, with required integer start and end properties.
+ Also accepts optional strand as +1 or -1 (used for up/downstream queries),
+ a name, and any arbitrary data is sent in on the info keyword argument
+
+ >>> from bx.intervals.intersection import Interval
+
+ >>> f1 = IntervalObj(23, 36)
+ >>> f2 = IntervalObj(34, 48, value={'chr':12, 'anno':'transposon'})
+ >>> f2
+ IntervalObj(34, 48, value={'anno': 'transposon', 'chr': 12})
+
+ """
+ cdef public int start, end
+ cdef public object value, chrom, strand
+
+ def __init__(self, int start, int end, object value=None, object chrom=None, object strand=None ):
+ assert start <= end, "start must be less than end"
+ self.start = start
+ self.end = end
+ self.value = value
+ self.chrom = chrom
+ self.strand = strand
+
+ def __repr__(self):
+ fstr = "IntervalObj(%d, %d" % (self.start, self.end)
+ if not self.value is None:
+ fstr += ", value=" + str(self.value)
+ fstr += ")"
+ return fstr
+
+ def __richcmp__(self, other, op):
+ if op == 0:
+ # <
+ return self.start < other.start or self.end < other.end
+ elif op == 1:
+ # <=
+ return self == other or self < other
+ elif op == 2:
+ # ==
+ return self.start == other.start and self.end == other.end
+ elif op == 3:
+ # !=
+ return self.start != other.start or self.end != other.end
+ elif op == 4:
+ # >
+ return self.start > other.start or self.end > other.end
+ elif op == 5:
+ # >=
+ return self == other or self > other
+
+cdef class IntervalTree:
+ """
+ Data structure for performing window intersect queries on a set of
+ of possibly overlapping 1d intervals.
+
+ Usage
+ =====
+
+ Create an empty IntervalTree
+
+ >>> from bx.intervals.intersection import Interval, IntervalTree
+ >>> intersecter = IntervalTree()
+
+ An interval is a start and end position and a value (possibly None).
+ You can add any object as an interval:
+
+ >>> intersecter.insert( 0, 10, "food" )
+ >>> intersecter.insert( 3, 7, dict(foo='bar') )
+
+ >>> intersecter.find( 2, 5 )
+ ['food', {'foo': 'bar'}]
+
+ If the object has start and end attributes (like the Interval class) there
+ is are some shortcuts:
+
+ >>> intersecter = IntervalTree()
+ >>> intersecter.insert_interval( IntervalObj( 0, 10 ) )
+ >>> intersecter.insert_interval( IntervalObj( 3, 7 ) )
+ >>> intersecter.insert_interval( IntervalObj( 3, 40 ) )
+ >>> intersecter.insert_interval( IntervalObj( 13, 50 ) )
+
+ >>> intersecter.find( 30, 50 )
+ [IntervalObj(3, 40), IntervalObj(13, 50)]
+ >>> intersecter.find( 100, 200 )
+ []
+
+ Before/after for intervals
+
+ >>> intersecter.before_interval( IntervalObj( 10, 20 ) )
+ [IntervalObj(3, 7)]
+ >>> intersecter.before_interval( IntervalObj( 5, 20 ) )
+ []
+
+ Upstream/downstream
+
+ >>> intersecter.upstream_of_interval(IntervalObj(11, 12))
+ [IntervalObj(0, 10)]
+ >>> intersecter.upstream_of_interval(IntervalObj(11, 12, strand="-"))
+ [IntervalObj(13, 50)]
+
+ >>> intersecter.upstream_of_interval(IntervalObj(1, 2, strand="-"), num_intervals=3)
+ [IntervalObj(3, 7), IntervalObj(3, 40), IntervalObj(13, 50)]
+
+
+ """
+
+ cdef IntervalNode root
+
+ def __cinit__( self ):
+ root = None
+
+ # ---- Position based interfaces -----------------------------------------
+
+ def insert( self, int start, int end, object value=None ):
+ """
+ Insert the interval [start,end) associated with value `value`.
+ """
+ if self.root is None:
+ self.root = IntervalNode( start, end, value )
+ else:
+ self.root = self.root.insert( start, end, value )
+
+ add = insert
+
+
+ def update( self, start, end, old_feature, new_feature):
+ """
+ Given an interval [start, end), replace all objects that
+ match the `old_feature` with `new_feature`.
+ """
+ if self.root is not None:
+ self.root.update(start, end, old_feature, new_feature)
+
+
+ def find( self, start, end ):
+ """
+ Return a sorted list of all intervals overlapping [start,end).
+ """
+ if self.root is None:
+ return []
+ return self.root.find( start, end )
+
+ def before( self, position, num_intervals=1, max_dist=2500 ):
+ """
+ Find `num_intervals` intervals that lie before `position` and are no
+ further than `max_dist` positions away
+ """
+ if self.root is None:
+ return []
+ return self.root.left( position, num_intervals, max_dist )
+
+ def after( self, position, num_intervals=1, max_dist=2500 ):
+ """
+ Find `num_intervals` intervals that lie after `position` and are no
+ further than `max_dist` positions away
+ """
+ if self.root is None:
+ return []
+ return self.root.right( position, num_intervals, max_dist )
+
+ # ---- Interval-like object based interfaces -----------------------------
+
+ def insert_interval( self, interval ):
+ """
+ Insert an "interval" like object (one with at least start and end
+ attributes)
+ """
+ self.insert( interval.start, interval.end, interval )
+
+ add_interval = insert_interval
+
+ def before_interval( self, interval, num_intervals=1, max_dist=2500 ):
+ """
+ Find `num_intervals` intervals that lie completely before `interval`
+ and are no further than `max_dist` positions away
+ """
+ if self.root is None:
+ return []
+ return self.root.left( interval.start, num_intervals, max_dist )
+
+ def after_interval( self, interval, num_intervals=1, max_dist=2500 ):
+ """
+ Find `num_intervals` intervals that lie completely after `interval` and
+ are no further than `max_dist` positions away
+ """
+ if self.root is None:
+ return []
+ return self.root.right( interval.end, num_intervals, max_dist )
+
+ def upstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ):
+ """
+ Find `num_intervals` intervals that lie completely upstream of
+ `interval` and are no further than `max_dist` positions away
+ """
+ if self.root is None:
+ return []
+ if interval.strand == -1 or interval.strand == "-":
+ return self.root.right( interval.end, num_intervals, max_dist )
+ else:
+ return self.root.left( interval.start, num_intervals, max_dist )
+
+ def downstream_of_interval( self, interval, num_intervals=1, max_dist=2500 ):
+ """
+ Find `num_intervals` intervals that lie completely downstream of
+ `interval` and are no further than `max_dist` positions away
+ """
+ if self.root is None:
+ return []
+ if interval.strand == -1 or interval.strand == "-":
+ return self.root.left( interval.start, num_intervals, max_dist )
+ else:
+ return self.root.right( interval.end, num_intervals, max_dist )
+
+ def traverse(self, fn):
+ """
+ call fn for each element in the tree
+ """
+ if self.root is None:
+ return None
+ return self.root.traverse(fn)
+
+# For backward compatibility
+Intersecter = IntervalTree
diff --git a/skbio/metadata/_interval.py b/skbio/metadata/_interval.py
new file mode 100644
index 0000000..62f791d
--- /dev/null
+++ b/skbio/metadata/_interval.py
@@ -0,0 +1,978 @@
+# ----------------------------------------------------------------------------
+# Copyright (c) 2013--, scikit-bio development team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+# ----------------------------------------------------------------------------
+
+import operator
+import copy
+import functools
+
+from ._intersection import IntervalTree
+from skbio.util._decorator import experimental, classonlymethod
+
+
+class Interval:
+ """Stores the bounds and metadata of an interval feature.
+
+ This class stores an interval feature. An interval feature
+ is defined as a sub-region of a biological sequence or sequence
+ alignment that is a functional entity, e.g., a gene, a riboswitch,
+ an exon, etc. It can span a single contiguous region or multiple
+ non-contiguous regions (e.g. multiple exons in a transcript, or
+ multiple genes in an operon).
+
+ Parameters
+ ----------
+ interval_metadata : object
+ A reference to the ``IntervalMetadata`` object that this
+ ``Interval`` object is associated to.
+ bounds : iterable of tuple of int
+ Tuples representing start and end coordinates. It is *zero-based*
+ numbering. It is always inclusive on start bound and exclusive on
+ end bound.
+ fuzzy : iterable of tuple of bool, optional
+ Tuples representing the fuzziness of each bound coordinates.
+ If this isn't specified, then the fuzziness of all bound
+ coordinates are ``False``. If any of the coordinate fuzziness
+ is ``True``, it indicates that the exact bound point of a
+ interval feature is unknown. The bound may begin or end at
+ some points outside the specified coordinates. This
+ accommodates the location format [1]_ of INSDC.
+ metadata : dict, optional
+ Dictionary of attributes storing information of the feature
+ such as "strand", "gene_name", or "product".
+
+ See Also
+ --------
+ skbio.metadata.IntervalMetadata
+
+ Notes
+ -----
+ While the construction of an ``Interval`` object automatically add
+ itself to its associated ``IntervalMetadata`` object,
+ ``IntervalMetadata.add`` is the typical/easier way to
+ create and add it to ``IntervalMetadata``.
+
+ References
+ ----------
+ .. [1] ftp://ftp.ebi.ac.uk/pub/databases/embl/doc/FT_current.html#3.4.3
+
+ Examples
+ --------
+ Hypothetically, let's say we have a gene called "genA" with 10 nt
+ as shown in the following diagram. The second row represents the
+ two exons (indicated by "=") on this gene:
+
+ ::
+
+ TGGATTCTGC
+ -====--==-
+ 0123456789
+
+ We can create an ``Interval`` object to represent the exons of the gene:
+
+ >>> from skbio.metadata import Interval, IntervalMetadata
+ >>> interval_metadata = IntervalMetadata(10)
+
+ Remember the coordinates are inclusive in lower bound and exclusive on
+ upper bound:
+
+ >>> gene = Interval(interval_metadata,
+ ... bounds=[(1, 5), (7, 9)],
+ ... metadata={'name': 'genA'})
+ >>> gene # doctest: +ELLIPSIS
+ Interval(interval_metadata=..., bounds=[(1, 5), (7, 9)], \
+fuzzy=[(False, False), (False, False)], metadata={'name': 'genA'})
+
+ """
+ def __init__(self, interval_metadata, bounds,
+ fuzzy=None, metadata=None):
+ if not isinstance(interval_metadata, IntervalMetadata):
+ raise TypeError('You need to provide an IntervalMetadata'
+ 'object, not %r' % interval_metadata)
+ # Intervals
+ self._interval_metadata = interval_metadata
+
+ self._bounds_fuzzy_setter(bounds, fuzzy)
+
+ # Metadata
+ if metadata is None:
+ metadata = {}
+ self.metadata = metadata
+
+ # add this interval feature to the associated IntervalMetadata
+ self._add()
+
+ def _add(self):
+ """Add the current ``Interval`` to the IntervalMetadata object."""
+ for bound in self.bounds:
+ start, end = bound
+ self._interval_metadata._interval_tree.add(start, end, self)
+ self._interval_metadata._intervals.append(self)
+
+ @experimental(as_of='0.5.1')
+ def __eq__(self, other):
+ '''Test if this ``Interval`` object is equal to another.
+
+ The equality is performed by checking if the ``metadata``,
+ ``bounds`` and ``fuzzy`` are equal. Since the ``bounds``
+ and the ``fuzzy`` are sorted, the permutations of them during
+ the ``Interval`` construction or assignment won't matter.
+
+ Parameters
+ ----------
+ other : Interval
+ Interval to test for equality against.
+
+ Returns
+ -------
+ bool
+ Indicates if the two objects are equal.
+ '''
+ return ((self.metadata == other.metadata) and
+ (self.bounds == other.bounds) and
+ (self.fuzzy == other.fuzzy))
+
+ @experimental(as_of='0.5.1')
+ def __ne__(self, other):
+ '''Test if this ``Interval`` object is not equal to another.
+
+ Parameters
+ ----------
+ other : Interval
+ Interval to test for inequality against.
+
+ Returns
+ -------
+ bool
+ Indicates if the two objects are not equal.
+ '''
+ return not (self == other)
+
+ @experimental(as_of='0.5.1')
+ def __repr__(self):
+ '''Return a string representation of this ``Interval`` object.
+
+ Returns
+ -------
+ str
+ String representation of this ``Interval`` object.
+ '''
+ if self.dropped:
+ s = ('{}(dropped=True, bounds={!r}, '
+ 'fuzzy={!r}, metadata={!r})')
+ return s.format(self.__class__.__name__,
+ self.bounds, self.fuzzy, self.metadata)
+ else:
+ s = ('{}(interval_metadata=<{!r}>, bounds={!r}, '
+ 'fuzzy={!r}, metadata={!r})')
+ return s.format(self.__class__.__name__,
+ id(self._interval_metadata),
+ self.bounds, self.fuzzy, self.metadata)
+
+ @experimental(as_of='0.5.1')
+ def drop(self):
+ '''Drop this ``Interval`` object from the interval metadata it links to.
+
+ If the ``Interval`` object is dropped, you can still get values of
+ ``bounds``, ``fuzzy``, and ``metadata`` attributes, but you
+ can not change their values with the setters.
+
+ See Also
+ --------
+ skbio.metadata.IntervalMetadata.drop
+ '''
+ if not self.dropped:
+ self._interval_metadata.drop([self])
+
+ def _bounds_fuzzy_setter(self, bounds=None, fuzzy=None):
+ if self.dropped:
+ raise RuntimeError('Cannot change `bounds` or `fuzzy` '
+ 'on a dropped Interval object.')
+ # Casts to `list`, validation, sorting, and setting of `bounds`
+ # and `fuzzy` happen here.
+ if bounds is not None:
+ # check iterability
+ try:
+ # check iterability
+ bounds = list(bounds)
+ except TypeError:
+ raise TypeError('Cannot give an non-iterable (%r) '
+ 'to `bounds`.' % bounds)
+
+ # check it is not empty
+ if not bounds:
+ raise ValueError('Cannot give empty `bounds`.')
+ # check each contiguous span is in right format
+ for bound in bounds:
+ _assert_valid_bound(bound)
+
+ spans = len(bounds)
+ else:
+ spans = len(self.bounds)
+
+ if fuzzy is not None:
+ try:
+ fuzzy = list(fuzzy)
+ except TypeError:
+ raise TypeError(
+ 'Cannot give a non-iterable (%r) '
+ 'to `fuzzy`.' % fuzzy)
+
+ if len(fuzzy) != spans:
+ raise ValueError(
+ 'The length of fuzzy must '
+ 'be equal to the length of bounds.')
+
+ for fuzzy_i in fuzzy:
+ _assert_valid_fuzzy(fuzzy_i)
+
+ if bounds is None:
+ # `bounds` and `fuzzy` cannot both be omitted.
+ if fuzzy is None:
+ raise ValueError('Cannot give `None` to both `bounds` '
+ 'and `fuzzy`.')
+ # If only `fuzzy` is provided, set `self.fuzzy` and don't
+ # change `self.bounds`.
+ else:
+ self._fuzzy = fuzzy
+ else:
+ # If only `bounds` is provided, reset `self.fuzzy` to
+ # all `False`.
+ if fuzzy is None:
+ bounds.sort()
+ self._check_bounds(bounds)
+ self._bounds = bounds
+ # reset all the fuzzy to False!!
+ del self.fuzzy
+
+ # If both `bounds` and `fuzzy` are provided, set
+ # `self.bounds` and `self.fuzzy`.
+ else:
+ bounds, fuzzy = [
+ list(e) for e in zip(*sorted(zip(bounds, fuzzy)))]
+ self._check_bounds(bounds)
+ self._bounds = bounds
+ self._fuzzy = fuzzy
+
+ self._interval_metadata._is_stale_tree = True
+
+ def _check_bounds(self, bounds):
+ '''input `bounds` must be sorted.'''
+ upper_bound = self._interval_metadata.upper_bound
+ lower_bound = self._interval_metadata.lower_bound
+ if bounds[-1][-1] > upper_bound or bounds[0][0] < lower_bound:
+ raise ValueError('Cannot set `bounds` (%r) with coordinate '
+ 'larger than upper bound (%r) or smaller than '
+ 'lower bound (%r).' %
+ (bounds, upper_bound, lower_bound))
+
+ @property
+ @experimental(as_of='0.5.1')
+ def fuzzy(self):
+ '''The openness of each coordinate.
+
+ This indicates that the exact bound of a interval feature
+ is unknown. The bound may begin or end at some points outside
+ the specified coordinates. This accommodates the bound format [1]_
+ of INSDC.
+
+ References
+ ----------
+ .. [1] ftp://ftp.ebi.ac.uk/pub/databases/embl/doc/FT_current.html#3.4.3
+ '''
+ return self._fuzzy
+
+ @fuzzy.setter
+ @experimental(as_of='0.5.1')
+ def fuzzy(self, value):
+ '''Set ``fuzzy``.
+
+ The ``value`` should an iterable matching ``self.bounds``.
+ '''
+ self._bounds_fuzzy_setter(fuzzy=value)
+
+ @fuzzy.deleter
+ @experimental(as_of='0.5.1')
+ def fuzzy(self):
+ '''Delete ``fuzzy``.
+
+ This set all fuzzy to be ``False``.
+ '''
+ if self.dropped:
+ raise RuntimeError('Cannot change fuzzy on dropped '
+ 'Interval object.')
+ self._fuzzy = [(False, False)] * len(self.bounds)
+
+ @property
+ @experimental(as_of='0.5.1')
+ def bounds(self):
+ '''The coordinates of the interval feature.
+
+ It should be a list of tuples of int pair. Each tuple stores
+ the start and end coordinates of a span of the interval
+ feature. The coordinates are *zero-based*. They are inclusive on
+ the start and exclusive on the end.
+ '''
+ return self._bounds
+
+ @bounds.setter
+ @experimental(as_of='0.5.1')
+ def bounds(self, value):
+ '''Set ``bounds``.
+
+ WARNING: setting ``bounds`` will reset ``fuzzy`` value to ``False``.
+ This is not totally surprising because it is justifiable your old
+ ``fuzzy`` don't fit the new bounds.
+ '''
+ self._bounds_fuzzy_setter(bounds=value)
+
+ @property
+ @experimental(as_of='0.5.1')
+ def metadata(self):
+ '''The metadata of the interval feature.
+
+ It stores the metadata (eg. gene name, function, ID, etc.) of
+ the interval feature as a ``dict``.
+ '''
+ return self._metadata
+
+ @metadata.setter
+ @experimental(as_of='0.5.1')
+ def metadata(self, value):
+ if self.dropped:
+ raise RuntimeError('Cannot change metadata on dropped '
+ 'Interval object.')
+ if not isinstance(value, dict):
+ raise TypeError("metadata must be a dict, not %r" % value)
+ self._metadata = value
+
+ @metadata.deleter
+ @experimental(as_of='0.5.1')
+ def metadata(self):
+ '''Delete metadata.
+
+ This sets metadata to be empty dict.
+ '''
+ if self.dropped:
+ raise RuntimeError('Cannot change metadata to dropped '
+ 'Interval object.')
+ self._metadata = {}
+
+ @property
+ @experimental(as_of='0.5.1')
+ def dropped(self):
+ '''Boolean value indicating if the ``Interval`` object is dropped.
+
+ If it is dropped, it means it is not associated with IntervalMetadata
+ object any more.
+
+ Notes
+ -----
+ This property is not writable.
+
+ See Also
+ --------
+ skbio.metadata.Interval.drop
+ skbio.metadata.IntervalMetadata.drop
+ '''
+ return self._interval_metadata is None
+
+
+class IntervalMetadata():
+ """Stores the interval features.
+
+ ``IntervalMetadata`` object allows storage, modification, and
+ querying of interval features covering a region of a single coordinate
+ system. For instance, this can be used to store functional annotations
+ about genes across a genome. This object is also applied to the sequence
+ alignment.
+
+ This object is typically coupled with another object, such as a
+ ``Sequence`` object (or its child class), or a ``TabularMSA`` object.
+
+ Parameters
+ ----------
+ upper_bound : int
+ Defines the exclusive upper bound of the interval features. No
+ coordinate can be greater than it.
+
+ Notes
+ -----
+ This class stores coordinates of all feature bounds into a interval
+ tree. It allows the speed up of query-by-bound. The building of
+ interval tree is deferred until necessary to save computation. It is
+ updated from all coordinates only when you need to fetch info from
+ the interval tree.
+
+ When you add a method into this class and if you method need to fetch
+ info from ``IntervalMetadata._interval_tree``, you should decorate it with
+ ``_rebuild_tree``. This decorator will check if the current interval tree
+ is stale and will update it if so. Additionally, if your method add,
+ delete, or changes the coordinates of any interval features, you should
+ set ``self._is_stale_tree`` to ``True`` at the end of your method to
+ indicate the interval tree becomes stale.
+
+ See Also
+ --------
+ skbio.metadata.Interval
+
+ Examples
+ --------
+ Let's say we have a sequence of length 10 and want to add annotation
+ to it. Create an ``IntervalMetadata`` object:
+
+ >>> from skbio.metadata import Interval, IntervalMetadata
+ >>> im = IntervalMetadata(10)
+
+ Let's add annotations of 3 genes:
+
+ >>> im.add(bounds=[(3, 9)],
+ ... metadata={'gene': 'sagB'}) # doctest: +ELLIPSIS
+ Interval(interval_metadata=..., bounds=[(3, 9)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagB'})
+ >>> im.add(bounds=[(3, 7)],
+ ... metadata={'gene': 'sagC'}) # doctest: +ELLIPSIS
+ Interval(interval_metadata=..., bounds=[(3, 7)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagC'})
+ >>> im.add(bounds=[(1, 2), (4, 7)],
+ ... metadata={'gene': 'sagA'}) # doctest: +ELLIPSIS
+ Interval(interval_metadata=..., bounds=[(1, 2), (4, 7)], \
+fuzzy=[(False, False), (False, False)], metadata={'gene': 'sagA'})
+
+ Show the object representation:
+
+ >>> im # doctest: +ELLIPSIS
+ 3 interval features
+ -------------------
+ Interval(interval_metadata=..., bounds=[(3, 9)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagB'})
+ Interval(interval_metadata=..., bounds=[(3, 7)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagC'})
+ Interval(interval_metadata=..., bounds=[(1, 2), (4, 7)], \
+fuzzy=[(False, False), (False, False)], metadata={'gene': 'sagA'})
+
+ We can sort the genes by their bounds:
+
+ >>> im.sort()
+ >>> im # doctest: +ELLIPSIS
+ 3 interval features
+ -------------------
+ Interval(interval_metadata=..., bounds=[(1, 2), (4, 7)], \
+fuzzy=[(False, False), (False, False)], metadata={'gene': 'sagA'})
+ Interval(interval_metadata=..., bounds=[(3, 7)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagC'})
+ Interval(interval_metadata=..., bounds=[(3, 9)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagB'})
+
+ Query the genes by bound and/or metadata:
+
+ >>> intvls = im.query([(1, 2)], metadata={'gene': 'foo'})
+ >>> list(intvls)
+ []
+ >>> intvls = im.query([(7, 9)])
+ >>> list(intvls) # doctest: +ELLIPSIS
+ [Interval(interval_metadata=..., bounds=[(3, 9)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagB'})]
+ >>> intvls = im.query(metadata={'gene': 'sagA'})
+ >>> intvls = list(intvls)
+ >>> intvls # doctest: +ELLIPSIS
+ [Interval(interval_metadata=..., bounds=[(1, 2), (4, 7)], \
+fuzzy=[(False, False), (False, False)], metadata={'gene': 'sagA'})]
+
+ Drop the gene(s) we get from query:
+
+ >>> im.drop(intvls)
+ >>> im.sort()
+ >>> im # doctest: +ELLIPSIS
+ 2 interval features
+ -------------------
+ Interval(interval_metadata=..., bounds=[(3, 7)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagC'})
+ Interval(interval_metadata=..., bounds=[(3, 9)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagB'})
+
+ """
+ def __init__(self, upper_bound):
+ self._upper_bound = upper_bound
+ if self.upper_bound < self.lower_bound:
+ raise ValueError('Cannot set `upper_bound` (%r) '
+ 'smaller than `lower_bound` (%r)'
+ % (self.upper_bound, self.lower_bound))
+
+ # List of Interval objects.
+ self._intervals = []
+
+ # IntervalTree object to allow faster querying of interval objects.
+ self._interval_tree = IntervalTree()
+
+ # Indicates if the IntervalTree needs to be rebuilt.
+ self._is_stale_tree = False
+
+ @property
+ @experimental(as_of='0.5.1')
+ def upper_bound(self):
+ '''The exclusive upper bound of interval features.'''
+ return self._upper_bound
+
+ @property
+ @experimental(as_of='0.5.1')
+ def lower_bound(self):
+ '''The inclusive lower bound of interval features.'''
+ return 0
+
+ @property
+ @experimental(as_of='0.5.1')
+ def num_interval_features(self):
+ '''The total number of interval features.'''
+ return len(self._intervals)
+
+ def _rebuild_tree(method):
+ """Rebuild the IntervalTree."""
+ @functools.wraps(method)
+ def inner(self, *args, **kwargs):
+ if self._is_stale_tree is False:
+ return method(self, *args, **kwargs)
+ self._interval_tree = IntervalTree()
+ for f in self._intervals:
+ for start, end in f.bounds:
+ self._interval_tree.add(start, end, f)
+ self._is_stale_tree = False
+ return method(self, *args, **kwargs)
+ return inner
+
+ def _reverse(self):
+ """Reverse ``IntervalMetadata`` object.
+
+ This operation reverses all of the interval coordinates.
+ For instance, this can be used to compare coordinates
+ in the forward strand to coordinates in the reversal strand.
+ """
+ for f in self._intervals:
+ intvls = [(self.upper_bound - x[1], self.upper_bound - x[0])
+ for x in reversed(f.bounds)]
+ f.bounds = intvls
+
+ # DONT' forget this!!!
+ self._is_stale_tree = True
+
+ @classonlymethod
+ @experimental(as_of="0.5.1")
+ def concat(cls, interval_metadata):
+ '''Concatenate an iterable of ``IntervalMetadata`` objects.
+
+ It concatenates the multiple ``IntervalMetadata`` objects into
+ one coordinate space. The order of the objects in the input
+ iterable matters. The coordinate of the second
+ ``InterableMetadata`` will be shifted up with the length of
+ the first ``IntervalMetadata`` object.
+
+ This function is useful when you concatenate multiple sequences.
+
+ Parameters
+ ----------
+ interval_metadata : Iterable (IntervalMetadata)
+ The interval metadata to concatenate.
+
+ Returns
+ -------
+ IntervalMetadata
+ Concatenated interval metadata.
+
+ Examples
+ --------
+ >>> from skbio.metadata import IntervalMetadata
+
+ Create two ``IntervalMetadata`` objects:
+
+ >>> im1 = IntervalMetadata(3)
+ >>> _ = im1.add([(0, 2)], [(True, False)], {'gene': 'sagA'})
+ >>> im2 = IntervalMetadata(4)
+ >>> _ = im2.add([(1, 4)], [(True, True)], {'gene': 'sagB'})
+
+ Concatenate them into a single coordinate space. The second
+ ``IntervalMetadata``'s interval features are all shifted
+ up. The resulting ``IntervalMetadata``'s upper bound is the
+ sum of upper bounds of concatenated objects:
+
+ >>> im = IntervalMetadata.concat([im1, im2])
+ >>> im # doctest: +ELLIPSIS
+ 2 interval features
+ -------------------
+ Interval(interval_metadata=<...>, bounds=[(0, 2)], \
+fuzzy=[(True, False)], metadata={'gene': 'sagA'})
+ Interval(interval_metadata=<...>, bounds=[(4, 7)], \
+fuzzy=[(True, True)], metadata={'gene': 'sagB'})
+ >>> im.upper_bound
+ 7
+
+ '''
+ interval_metadata = list(interval_metadata)
+
+ if len(interval_metadata) == 0:
+ return cls(0)
+
+ upper_bound = 0
+ for im in interval_metadata:
+ upper_bound += im.upper_bound
+ new = cls(upper_bound)
+
+ length = 0
+ for i, im in enumerate(interval_metadata):
+ for intvl in im._intervals:
+ bounds = intvl.bounds
+ fuzzy = intvl.fuzzy
+ if i != 0:
+ bounds = [(start + length, end + length)
+ for start, end in bounds]
+ new.add(bounds, fuzzy, intvl.metadata)
+ length += im.upper_bound
+
+ return new
+
+ @experimental(as_of='0.5.1')
+ def merge(self, other):
+ '''Merge the interval features of another ``IntervalMetadata`` object.
+
+ It adds all the interval features of the other object into
+ ``self``. Note this will not check if there are any duplicates
+ of interval features after merge.
+
+ Parameters
+ ----------
+ other : ``IntervalMetadata``
+ The other ``IntervalMetadata`` to be merged.
+
+ '''
+ if self.upper_bound != other.upper_bound:
+ raise ValueError(
+ 'The upper bounds of the two IntervalMetadata objects '
+ 'are not equal (%d != %d)' % (
+ self.upper_bound, other.upper_bound))
+ if self.lower_bound != other.lower_bound:
+ raise ValueError(
+ 'The lower bounds of the two IntervalMetadata objects '
+ 'are not equal (%d != %d)' % (
+ self.lower_bound, other.lower_bound))
+ for intvl in other._intervals:
+ self.add(intvl.bounds, intvl.fuzzy, intvl.metadata)
+
+ @experimental(as_of='0.5.1')
+ def sort(self, ascending=True):
+ '''Sort interval features by their coordinates.
+
+ It sorts by the start coordinate first. If they are the same between
+ two interval features, they will be sorted by comparing their end
+ coordinates. For example, an interval feature with [(1, 2), (4, 7)]
+ will be sorted in front of another one with [(1, 2), (3, 8)].
+
+ Parameters
+ ----------
+ ascending : bool, optional
+ sort in ascending or descending coordinates.
+ '''
+ self._intervals.sort(
+ key=lambda i: [i.bounds[0][0], i.bounds[-1][1]],
+ reverse=not ascending)
+
+ @experimental(as_of='0.5.1')
+ def add(self, bounds, fuzzy=None, metadata=None):
+ """Create and add an ``Interval`` to this ``IntervalMetadata``.
+
+ This method creates an ``Interval`` object and inserts it into
+ the ``IntervalMetadata`` object.
+
+ Parameters
+ ----------
+ bounds : iterable of tuple of ints
+ Tuples representing start and end coordinates. It is *zero-based*
+ numbering. It is always inclusive on start bound and exclusive on
+ end bound.
+ fuzzy : iterable of tuple of bool, optional
+ Tuples representing the fuzziness of each bound coordinates.
+ metadata : dict, optional
+ A dictionary of key-value pairs associated with the
+ ``Interval`` object.
+
+ Returns
+ -------
+ Interval
+ The ``Interval`` object added.
+
+ See Also
+ --------
+ skbio.metadata.Interval
+ """
+ # Add an interval to the tree. Note that the add functionality is
+ # built within the Interval constructor.
+ return Interval(interval_metadata=self,
+ bounds=bounds,
+ fuzzy=fuzzy,
+ metadata=metadata)
+
+ @_rebuild_tree
+ def _query_interval(self, bound):
+ """Yield ``Interval`` objects that overlap with the bound."""
+ _assert_valid_bound(bound)
+
+ start, end = bound
+ intvls = self._interval_tree.find(start, end)
+ # if a ``Interval`` has many non-contiguous spans and
+ # multiple of them overlap with the bound, then
+ # this ``Interval`` object will be returned
+ # multiple times. So we need to remove duplicates.
+ seen = set()
+ for intvl in intvls:
+ if id(intvl) not in seen:
+ seen.add(id(intvl))
+ yield intvl
+
+ def _query_attribute(self, metadata, intervals=None):
+ """Yield ``Interval`` objects based on query attributes.
+
+ Parameters
+ ----------
+ metadata : dict or ``None``
+ If it is ``None``, return empty iterator; if it is
+ ``{}``, return an interator of all the ``Interval``
+ objects.
+ intervals : an iterable of ``Interval`` objects
+ """
+ if metadata is None:
+ return
+
+ if intervals is None:
+ intervals = self._intervals
+
+ for intvl in intervals:
+ for (key, value) in metadata.items():
+ if (key not in intvl.metadata or
+ intvl.metadata[key] != value):
+ break
+ else:
+ yield intvl
+
+ @experimental(as_of='0.5.1')
+ @_rebuild_tree
+ def query(self, bounds=None, metadata=None):
+ """Yield ``Interval`` object with the bounds and attributes.
+
+ The ``Interval`` objects must meet both requirements: 1) overlap
+ with any of the spans specified by ``bounds``; 2) satisfy
+ ``metadata`` specification. For instance, you can identify
+ all the recA genes that overlap with (10, 100) or (900, 1000)
+ with this code ``interval_metadata.query([(10, 100),
+ (900, 1000)], {'gene': 'recA'})``.
+
+ Parameters
+ ----------
+ bounds : iterable of tuples of int pair, optional
+ Specifies bounds to look for the ``Interval``
+ objects. An satisfying interval feature only need to overlap with
+ one bound. Default (``None``) means all ``Intervals`` meet
+ this requirement.
+
+ metadata : dict, optional
+ A dictionary of key word attributes associated with the
+ ``Interval`` object. It specifies what metadata keywords and
+ values to look for. Default (``None``) means all ``Intervals``
+ meet this requirement.
+
+ Yields
+ ------
+ Interval
+ ``Interval`` object satisfying the search criteria.
+ """
+ if bounds is None:
+ for intvl in self._query_attribute(metadata):
+ yield intvl
+ else:
+ for loc in bounds:
+ intvls = self._query_interval(loc)
+ if metadata is None:
+ metadata = {}
+ for intvl in self._query_attribute(metadata, intvls):
+ yield intvl
+
+ @experimental(as_of='0.5.1')
+ def drop(self, intervals):
+ """Drops Interval objects.
+
+ The given ``Interval`` objects will be removed and their
+ associated ``IntervalMetadata`` will be set to ``None``.
+
+ Parameters
+ ----------
+ intervals : iterable of ``Interval``
+ ``Interval`` objects to drop from this object.
+
+ """
+ to_delete = {id(f) for f in intervals}
+
+ new_intvls = []
+ # iterate through queries and drop them
+ for intvl in self._intervals:
+ if id(intvl) in to_delete:
+ intvl._interval_metadata = None
+ else:
+ new_intvls.append(intvl)
+
+ self._intervals = new_intvls
+ self._is_stale_tree = True
+
+ @experimental(as_of='0.5.1')
+ def __eq__(self, other):
+ '''Test if this object is equal to another.
+
+ It checks if the coordinate spaces are the same between the
+ two objects. If so, then check if all the interval features
+ are equal between the two objects after sorting them by
+ bounds.
+
+ Parameters
+ ----------
+ other : IntervalMetadata
+ Interval metadata to test for equality against.
+
+ Returns
+ -------
+ bool
+ Indicates if the two objects are equal.
+ '''
+ if self.upper_bound != other.upper_bound or \
+ self.lower_bound != other.lower_bound:
+ return False
+ else:
+ self_intervals = sorted(self._intervals,
+ key=operator.attrgetter('bounds'))
+ other_intervals = sorted(other._intervals,
+ key=operator.attrgetter('bounds'))
+ return self_intervals == other_intervals
+
+ @experimental(as_of='0.5.1')
+ def __ne__(self, other):
+ '''Test if this object is not equal to another.
+
+ Parameters
+ ----------
+ other : IntervalMetadata
+ Interval metadata to test for inequality against.
+
+ Returns
+ -------
+ bool
+ Indicates if the two objects are not equal.
+
+ See Also
+ --------
+ skbio.metadata.IntervalMetadata.__eq__
+ '''
+ return not (self == other)
+
+ @experimental(as_of='0.5.1')
+ def __repr__(self):
+ '''Return a string representation of this object.
+
+ Returns
+ -------
+ str
+ String representation of this ``IntervalMetadata`` object.
+ '''
+ n = self.num_interval_features
+ l1 = '{} interval feature'.format(n)
+ if n != 1:
+ l1 += 's'
+ l2 = '-' * len(l1)
+
+ if n <= 5:
+ items = [repr(i) for i in self._intervals]
+ else:
+ # intentionally overwrite items[2] to make code cleaner
+ items = [repr(self._intervals[i]) for i in [0, 1, 2, n-2, n-1]]
+ items[2] = '...'
+
+ return '\n'.join([l1, l2] + items)
+
+ @experimental(as_of='0.5.1')
+ def __copy__(self):
+ '''Return a shallow copy.
+
+ Notes
+ -----
+ The ``IntervalMetadata`` copy will have copies of the
+ ``Interval`` objects present in this object. The ``metadata``
+ dictionary of each ``Interval`` object will be a shallow copy.
+
+ See Also
+ --------
+ __deepcopy__
+ '''
+ return self._copy(False, {})
+
+ @experimental(as_of='0.5.1')
+ def __deepcopy__(self, memo):
+ '''Return a deep copy.
+
+ Notes
+ -----
+ The ``IntervalMetadata`` copy will have copies of the
+ ``Interval`` objects present in this object. The ``metadata``
+ dictionary of each ``Interval`` object will be a deep copy.
+
+ See Also
+ --------
+ __copy__
+ '''
+ return self._copy(True, memo)
+
+ def _copy(self, deep, memo):
+ cp = IntervalMetadata(self.upper_bound)
+
+ for interval in self._intervals:
+ # Only need to shallow-copy `bounds` and `fuzzy`
+ # because their elements are immutable.
+ bounds_cp = interval.bounds[:]
+ fuzzy_cp = interval.fuzzy[:]
+ if deep:
+ metadata_cp = copy.deepcopy(interval.metadata, memo)
+ else:
+ metadata_cp = copy.copy(interval.metadata)
+
+ cp.add(bounds_cp,
+ fuzzy=fuzzy_cp,
+ metadata=metadata_cp)
+
+ return cp
+
+
+def _assert_valid_bound(bound):
+ if isinstance(bound, tuple):
+ try:
+ start, end = bound
+ except ValueError:
+ raise ValueError("A `bound` must be a tuple of exactly "
+ "two coordinates, not {!r}".format(bound))
+ if not (isinstance(start, int) and
+ isinstance(end, int)) or start > end:
+ raise ValueError('`start` (%r) cannot be a larger int '
+ 'than `end` (%r).' % (start, end))
+ else:
+ raise TypeError("Each `bound` must be a tuple, not {!r}".format(
+ bound))
+
+
+def _assert_valid_fuzzy(fuzzy):
+ if isinstance(fuzzy, tuple):
+ try:
+ start, end = fuzzy
+ except ValueError:
+ raise ValueError("A `fuzzy` must be a tuple of exactly "
+ "two, not {!r}".format(fuzzy))
+ if not (isinstance(start, bool) and isinstance(end, bool)):
+ raise TypeError('A `fuzzy` must be a tuple of two booleans')
+ else:
+ raise TypeError("Each `fuzzy` must be a tuple, not {!r}".format(
+ fuzzy))
diff --git a/skbio/metadata/_mixin.py b/skbio/metadata/_mixin.py
index a6f5648..c2d9372 100644
--- a/skbio/metadata/_mixin.py
+++ b/skbio/metadata/_mixin.py
@@ -11,7 +11,8 @@ import copy
import pandas as pd
-from skbio.util._decorator import stable
+from skbio.util._decorator import stable, experimental
+from skbio.metadata import IntervalMetadata
class MetadataMixin(metaclass=abc.ABCMeta):
@@ -406,3 +407,135 @@ class PositionalMetadataMixin(metaclass=abc.ABCMeta):
"""
return (self._positional_metadata is not None and
len(self.positional_metadata.columns) > 0)
+
+
+class IntervalMetadataMixin(metaclass=abc.ABCMeta):
+ @abc.abstractmethod
+ def _interval_metadata_axis_len_(self):
+ '''Return length of axis that interval metadata applies to.
+
+ Returns
+ -------
+ int
+ Interval metadata axis length.
+
+ '''
+ raise NotImplementedError
+
+ @abc.abstractmethod
+ def __init__(self, interval_metadata=None):
+ raise NotImplementedError
+
+ def _init_(self, interval_metadata=None):
+ if interval_metadata is None:
+ # Could use deleter but this is less overhead and needs to be fast.
+ self._interval_metadata = None
+ else:
+ # Use setter for validation and copy.
+ self.interval_metadata = interval_metadata
+
+ @property
+ @experimental(as_of="0.5.1")
+ def interval_metadata(self):
+ '''``IntervalMetadata`` object containing info about interval features.
+
+ Notes
+ -----
+ This property can be set and deleted. When setting new
+ interval metadata, a shallow copy of the ``IntervalMetadata``
+ object is made.
+
+ '''
+ if self._interval_metadata is None:
+ # Not using setter to avoid copy.
+ self._interval_metadata = IntervalMetadata(
+ self._interval_metadata_axis_len_())
+ return self._interval_metadata
+
+ @interval_metadata.setter
+ def interval_metadata(self, interval_metadata):
+ if isinstance(interval_metadata, IntervalMetadata):
+ upper_bound = interval_metadata.upper_bound
+ lower_bound = interval_metadata.lower_bound
+ axis_len = self._interval_metadata_axis_len_()
+ if lower_bound != 0:
+ raise ValueError(
+ 'The lower bound for the interval features (%d) '
+ 'must be zero.' % lower_bound)
+ if upper_bound != axis_len:
+ raise ValueError(
+ 'The upper bound for the interval features (%d) '
+ 'must match the interval metadata axis length (%d)'
+ % (upper_bound, axis_len))
+ # copy all the data to the mixin
+ self._interval_metadata = copy.copy(interval_metadata)
+ else:
+ raise TypeError('You must provide `IntervalMetadata` object, '
+ 'not type %s.' % type(interval_metadata).__name__)
+
+ @interval_metadata.deleter
+ def interval_metadata(self):
+ self._interval_metadata = None
+
+ @experimental(as_of="0.5.1")
+ def has_interval_metadata(self):
+ """Determine if the object has interval metadata.
+
+ An object has interval metadata if its ``interval_metadata``
+ has at least one ```Interval`` objects.
+
+ Returns
+ -------
+ bool
+ Indicates whether the object has interval metadata.
+
+ """
+ return (self._interval_metadata is not None and
+ self.interval_metadata.num_interval_features > 0)
+
+ @abc.abstractmethod
+ def __eq__(self, other):
+ raise NotImplementedError
+
+ def _eq_(self, other):
+ # We're not simply comparing self.interval_metadata to
+ # other.interval_metadata in order to avoid creating "empty"
+ # interval metadata representations on the objects if they don't have
+ # interval metadata.
+ if self.has_interval_metadata() and other.has_interval_metadata():
+ return self.interval_metadata == other.interval_metadata
+ elif not (self.has_interval_metadata() or
+ other.has_interval_metadata()):
+ # Both don't have interval metadata.
+ return (self._interval_metadata_axis_len_() ==
+ other._interval_metadata_axis_len_())
+ else:
+ # One has interval metadata while the other does not.
+ return False
+
+ @abc.abstractmethod
+ def __ne__(self, other):
+ raise NotImplementedError
+
+ def _ne_(self, other):
+ return not (self == other)
+
+ @abc.abstractmethod
+ def __copy__(self):
+ raise NotImplementedError
+
+ def _copy_(self):
+ if self.has_interval_metadata():
+ return copy.copy(self.interval_metadata)
+ else:
+ return None
+
+ @abc.abstractmethod
+ def __deepcopy__(self, memo):
+ raise NotImplementedError
+
+ def _deepcopy_(self, memo):
+ if self.has_interval_metadata():
+ return copy.deepcopy(self.interval_metadata, memo)
+ else:
+ return None
diff --git a/skbio/metadata/_repr.py b/skbio/metadata/_repr.py
index 7830e23..ddb12d9 100644
--- a/skbio/metadata/_repr.py
+++ b/skbio/metadata/_repr.py
@@ -15,8 +15,11 @@ from skbio._base import ElasticLines
class _MetadataReprBuilder(metaclass=ABCMeta):
- """Abstract base class for building a repr for an object containing
- metadata and/or positional metadata.
+ """An ABC for building a repr from an object containing metadata.
+
+ This abstract base class constructs a repr string for an
+ object which contains metadata, positional metadata and/or
+ interval metadata.
Parameters
----------
@@ -26,6 +29,7 @@ class _MetadataReprBuilder(metaclass=ABCMeta):
Maximum width of the repr.
indent : int
Number of spaces to use for indented lines.
+
"""
def __init__(self, obj, width, indent):
self._obj = obj
@@ -50,6 +54,7 @@ class _MetadataReprBuilder(metaclass=ABCMeta):
self._process_header()
self._process_metadata()
self._process_positional_metadata()
+ self._process_interval_metadata()
self._process_stats()
self._process_data()
@@ -126,6 +131,18 @@ class _MetadataReprBuilder(metaclass=ABCMeta):
dtype_fmt = '<dtype: %s>' % str(dtype)
return self._wrap_text_with_indent(dtype_fmt, key_fmt, 1)
+ def _process_interval_metadata(self):
+ # TODO: this hasattr check can be removed once all the relevant
+ # classes have interval_metadata added to it.
+ if (hasattr(self._obj, "has_interval_metadata") and
+ self._obj.has_interval_metadata()):
+ self._lines.add_line('Interval metadata:')
+ n = self._obj.interval_metadata.num_interval_features
+ line = self._indent + '%d interval feature' % n
+ if n > 1:
+ line += 's'
+ self._lines.add_line(line)
+
def _format_key(self, key):
"""Format metadata key.
diff --git a/skbio/metadata/_testing.py b/skbio/metadata/_testing.py
index 9787957..8193d7d 100644
--- a/skbio/metadata/_testing.py
+++ b/skbio/metadata/_testing.py
@@ -13,6 +13,7 @@ import numpy as np
import numpy.testing as npt
from skbio.util._testing import assert_data_frame_almost_equal
+from skbio.metadata import IntervalMetadata
class MetadataMixinTests:
@@ -904,3 +905,317 @@ class PositionalMetadataMixinTests:
obj = self._positional_metadata_constructor_(
2, positional_metadata={'foo': [1, 2], 'bar': ['abc', 'def']})
self.assertTrue(obj.has_positional_metadata())
+
+
+class IntervalMetadataMixinTests:
+ def _set_up(self):
+ self.upper_bound = 9
+ self.im = IntervalMetadata(self.upper_bound)
+ self.intvls = [
+ {'bounds': [(0, 1), (2, 9)], 'metadata': {'gene': 'sagA'}},
+ {'bounds': [(0, 1)], 'metadata': {'gene': ['a'],
+ 'product': 'foo'}}]
+
+ def test_constructor_invalid(self):
+ with self.assertRaisesRegex(TypeError,
+ 'You must provide `IntervalMetadata` '
+ 'object.'):
+ self._interval_metadata_constructor_(0, '')
+
+ def test_constructor_interval_metadata_len_mismatch(self):
+ for i in [0, 1, 3, 100]:
+ with self.assertRaisesRegex(
+ ValueError, '\(%d\).*\(%d\)' % (self.upper_bound, i)):
+ self._interval_metadata_constructor_(i, self.im)
+
+ def test_constructor_interval_metadata_len(self):
+ for n in 1, 2, 3:
+ im = IntervalMetadata(n)
+ im.add([(0, 1)], metadata={'a': 'b'})
+ obj = self._interval_metadata_constructor_(n, im)
+ self.assertTrue(obj.has_interval_metadata())
+ self.assertIsInstance(obj.interval_metadata, IntervalMetadata)
+
+ def test_constructor_interval_metadata_len_0(self):
+ im = IntervalMetadata(0)
+ obj = self._interval_metadata_constructor_(0, im)
+ self.assertFalse(obj.has_interval_metadata())
+
+ def test_constructor_no_interval_metadata(self):
+ for i, im in [(0, None), (self.upper_bound, self.im)]:
+ obj = self._interval_metadata_constructor_(i, im)
+ self.assertFalse(obj.has_interval_metadata())
+ self.assertIsInstance(obj.interval_metadata, IntervalMetadata)
+
+ def test_constructor_handles_missing_interval_metadata_efficiently(self):
+ obj = self._interval_metadata_constructor_(self.upper_bound)
+ self.assertIsNone(obj._interval_metadata)
+
+ obj = self._interval_metadata_constructor_(
+ self.upper_bound, interval_metadata=None)
+ self.assertIsNone(obj._interval_metadata)
+
+ def test_constructor_makes_shallow_copy_of_interval_metadata(self):
+ intvl = self.im.add(**self.intvls[1])
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+
+ self.assertEqual(obj.interval_metadata, self.im)
+ self.assertIsNot(obj.interval_metadata, self.im)
+
+ # Changing mutable value of metadata of the old interval
+ # also changes obj.
+ intvl.metadata['gene'].append('b')
+ self.assertEqual(obj.interval_metadata, self.im)
+
+ # Changing old interval doesn't change obj
+ intvl.bounds = [(3, 6)]
+ self.assertNotEqual(obj.interval_metadata, self.im)
+
+ def test_eq_basic(self):
+ im1 = IntervalMetadata(self.upper_bound)
+ im1.add(**self.intvls[0])
+ obj1 = self._interval_metadata_constructor_(self.upper_bound, im1)
+
+ im2 = IntervalMetadata(self.upper_bound)
+ im2.add(**self.intvls[0])
+ obj2 = self._interval_metadata_constructor_(self.upper_bound, im2)
+
+ self.assertReallyEqual(obj1, obj2)
+
+ def test_eq_populated_differently(self):
+ im1 = IntervalMetadata(self.upper_bound)
+ im1.add(**self.intvls[0])
+ obj1 = self._interval_metadata_constructor_(self.upper_bound, im1)
+
+ obj2 = self._interval_metadata_constructor_(self.upper_bound)
+ obj2.interval_metadata.add(**self.intvls[0])
+
+ self.assertReallyEqual(obj1, obj2)
+
+ def test_eq_handles_missing_positional_metadata_efficiently(self):
+ obj1 = self._interval_metadata_constructor_(self.upper_bound)
+ obj2 = self._interval_metadata_constructor_(self.upper_bound)
+ self.assertReallyEqual(obj1, obj2)
+
+ self.assertIsNone(obj1._interval_metadata)
+ self.assertIsNone(obj2._interval_metadata)
+
+ def test_ne_diff_len(self):
+ obj1 = self._interval_metadata_constructor_(0)
+ obj2 = self._interval_metadata_constructor_(self.upper_bound)
+ self.assertReallyNotEqual(obj1, obj2)
+
+ def test_ne_only_one_is_empty(self):
+ im1 = IntervalMetadata(self.upper_bound)
+ im1.add(**self.intvls[0])
+ obj1 = self._interval_metadata_constructor_(self.upper_bound, im1)
+
+ obj2 = self._interval_metadata_constructor_(self.upper_bound)
+
+ self.assertReallyNotEqual(obj1, obj2)
+
+ def test_ne(self):
+ im1 = IntervalMetadata(self.upper_bound)
+ im1.add(**self.intvls[0])
+ obj1 = self._interval_metadata_constructor_(self.upper_bound, im1)
+
+ im2 = IntervalMetadata(self.upper_bound)
+ im2.add(**self.intvls[1])
+ obj2 = self._interval_metadata_constructor_(self.upper_bound, im2)
+
+ self.assertReallyNotEqual(obj1, obj2)
+
+ def test_copy_interval_metadata_empty(self):
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+ obj_copy = copy.copy(obj)
+
+ self.assertEqual(obj, obj_copy)
+ self.assertIsNot(obj, obj_copy)
+
+ self.assertIsNone(obj_copy._interval_metadata)
+ self.assertEqual(obj._interval_metadata, self.im)
+
+ def test_copy_interval_metadata_none(self):
+ obj = self._interval_metadata_constructor_(self.upper_bound)
+ obj_copy = copy.copy(obj)
+
+ self.assertEqual(obj, obj_copy)
+ self.assertIsNot(obj, obj_copy)
+
+ self.assertIsNone(obj._interval_metadata)
+ self.assertIsNone(obj_copy._interval_metadata)
+
+ def test_copy_interval_metadata(self):
+ self.im.add(**self.intvls[1])
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+ obj_copy = copy.copy(obj)
+
+ self.assertEqual(obj, obj_copy)
+ self.assertIsNot(obj, obj_copy)
+
+ self.assertIsNot(obj.interval_metadata,
+ obj_copy.interval_metadata)
+ self.assertIsNot(obj.interval_metadata._intervals,
+ obj_copy.interval_metadata._intervals)
+ for i, j in zip(obj.interval_metadata._intervals,
+ obj_copy.interval_metadata._intervals):
+ self.assertIsNot(i, j)
+ self.assertIsNot(i.metadata, j.metadata)
+ for k in i.metadata:
+ self.assertIs(i.metadata[k], j.metadata[k])
+
+ def test_deepcopy_interval_metadata(self):
+ self.im.add(**self.intvls[1])
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+ obj_copy = copy.deepcopy(obj)
+
+ self.assertEqual(obj, obj_copy)
+ self.assertIsNot(obj, obj_copy)
+
+ self.assertIsNot(obj.interval_metadata,
+ obj_copy.interval_metadata)
+ self.assertIsNot(obj.interval_metadata._intervals,
+ obj_copy.interval_metadata._intervals)
+ for i, j in zip(obj.interval_metadata._intervals,
+ obj_copy.interval_metadata._intervals):
+ self.assertIsNot(i, j)
+ self.assertIsNot(i.metadata, j.metadata)
+ self.assertIsNot(i.metadata['gene'], j.metadata['gene'])
+ self.assertIs(i.metadata['product'], j.metadata['product'])
+
+ def test_deepcopy_interval_metadata_empty(self):
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+ obj_copy = copy.deepcopy(obj)
+
+ self.assertEqual(obj, obj_copy)
+ self.assertIsNot(obj, obj_copy)
+
+ self.assertIsNone(obj_copy._interval_metadata)
+ self.assertEqual(obj._interval_metadata, self.im)
+
+ def test_deepcopy_interval_metadata_none(self):
+ obj = self._interval_metadata_constructor_(self.upper_bound, None)
+ obj_copy = copy.deepcopy(obj)
+
+ self.assertEqual(obj, obj_copy)
+ self.assertIsNot(obj, obj_copy)
+
+ self.assertIsNone(obj._interval_metadata)
+ self.assertIsNone(obj_copy._interval_metadata)
+
+ def test_deepcopy_memo_is_respected(self):
+ # Basic test to ensure deepcopy's memo is passed through to recursive
+ # deepcopy calls.
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+ memo = {}
+ copy.deepcopy(obj, memo)
+ self.assertGreater(len(memo), 1)
+
+ def test_interval_metadata_getter(self):
+ self.im.add(**self.intvls[0])
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+ self.assertIsInstance(obj.interval_metadata, IntervalMetadata)
+ self.assertEqual(self.im, obj.interval_metadata)
+
+ # Update existing metadata.
+ obj.interval_metadata._intervals[0].metadata['gene'] = 'sagB'
+ self.assertNotEqual(obj.interval_metadata, self.im)
+ self.im._intervals[0].metadata['gene'] = 'sagB'
+ self.assertEqual(obj.interval_metadata, self.im)
+
+ # Add new interval feature.
+ obj.interval_metadata.add(**self.intvls[1])
+ self.im.add(**self.intvls[1])
+ self.assertEqual(obj.interval_metadata, self.im)
+
+ def test_interval_metadata_getter_no_interval_metadata(self):
+ obj = self._interval_metadata_constructor_(self.upper_bound)
+ self.assertIsNone(obj._interval_metadata)
+ self.assertIsInstance(obj.interval_metadata, IntervalMetadata)
+ self.assertEqual(obj.interval_metadata, self.im)
+ self.assertIsNotNone(obj._interval_metadata)
+
+ def test_interval_metadata_setter(self):
+ obj = self._interval_metadata_constructor_(self.upper_bound)
+
+ self.assertFalse(obj.has_interval_metadata())
+
+ obj.interval_metadata = self.im
+ self.assertFalse(obj.has_interval_metadata())
+ self.assertEqual(obj.interval_metadata, self.im)
+
+ self.im.add(**self.intvls[1])
+ obj.interval_metadata = self.im
+ self.assertTrue(obj.has_interval_metadata())
+ self.assertEqual(obj.interval_metadata, self.im)
+
+ def test_interval_metadata_setter_makes_copy(self):
+ intvl = self.im.add(**self.intvls[1])
+ obj = self._interval_metadata_constructor_(self.upper_bound)
+ obj.interval_metadata = self.im
+
+ self.assertEqual(obj.interval_metadata, self.im)
+ self.assertIsNot(obj.interval_metadata, self.im)
+
+ # Changing mutable value of metadata of the old interval
+ # also changes obj.
+ intvl.metadata['gene'].append('b')
+ self.assertEqual(obj.interval_metadata, self.im)
+
+ # Changing old interval doesn't change obj
+ intvl.bounds = [(3, 6)]
+ self.assertNotEqual(obj.interval_metadata, self.im)
+
+ def test_interval_metadata_setter_len_mismatch(self):
+ self.im.add(**self.intvls[1])
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+
+ for i in 0, 1, 3, 100:
+ with self.assertRaisesRegex(
+ ValueError, '\(%d\).*\(%d\)' % (i, self.upper_bound)):
+ obj.interval_metadata = IntervalMetadata(i)
+
+ self.assertEqual(obj.interval_metadata, self.im)
+
+ def test_interval_metadata_setter_invalid_type(self):
+ self.im.add(**self.intvls[0])
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+
+ for i in [2, None, '', {}, []]:
+ with self.assertRaisesRegex(
+ TypeError,
+ 'You must provide `IntervalMetadata` object'):
+ obj.interval_metadata = i
+
+ self.assertEqual(self.im, obj.interval_metadata)
+
+ def test_interval_metadata_deleter_empty(self):
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+
+ del obj.interval_metadata
+ self.assertIsNone(obj._interval_metadata)
+ self.assertFalse(obj.has_interval_metadata())
+
+ # Delete again. test idempotent
+ del obj.interval_metadata
+ self.assertIsNone(obj._interval_metadata)
+ self.assertFalse(obj.has_interval_metadata())
+
+ def test_interval_metadata_deleter(self):
+ self.im.add(**self.intvls[0])
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+
+ del obj.interval_metadata
+ self.assertIsNone(obj._interval_metadata)
+ self.assertFalse(obj.has_interval_metadata())
+
+ def test_has_interval_metadata(self):
+ obj = self._interval_metadata_constructor_(self.upper_bound)
+ self.assertFalse(obj.has_interval_metadata())
+
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+ self.assertFalse(obj.has_interval_metadata())
+
+ self.im.add([(0, 1)])
+ obj = self._interval_metadata_constructor_(self.upper_bound, self.im)
+ self.assertTrue(obj.has_interval_metadata())
diff --git a/skbio/metadata/tests/test_intersection.py b/skbio/metadata/tests/test_intersection.py
new file mode 100644
index 0000000..9eb6375
--- /dev/null
+++ b/skbio/metadata/tests/test_intersection.py
@@ -0,0 +1,241 @@
+# checklist.py:CopyrightHeadersValidator IGNORE
+# ----------------------------------------------------------------------------
+# This code is taken from bx-python project and added with a test for a new
+# function `update` from line 195 to 211. The license for this code is
+# included in licenses/bx_python.txt.
+# ----------------------------------------------------------------------------
+
+import sys
+import unittest
+import random
+
+from skbio.metadata._intersection import IntervalObj
+from skbio.metadata._intersection import IntervalNode
+from skbio.metadata._intersection import IntervalTree
+
+
+# Note: More bounds checking on input parameters are performed
+# within test_interval.py
+class NeighborTestCase(unittest.TestCase):
+
+ def setUp(self):
+ iv = IntervalNode(50, 59, IntervalObj(50, 59))
+ for i in range(0, 110, 10):
+ if i == 50:
+ continue
+ f = IntervalObj(i, i + 9)
+ iv = iv.insert(f.start, f.end, f)
+ self.intervals = iv
+
+ def test_left(self):
+ iv = self.intervals
+ self.assertEqual(str(iv.left(60, n=2)),
+ str([IntervalObj(50, 59), IntervalObj(40, 49)]))
+
+ for i in range(10, 100, 10):
+ r = iv.left(i, max_dist=10, n=1)
+ self.assertEqual(r[0].end, i - 1)
+
+ def test_toomany(self):
+ iv = self.intervals
+ self.assertEqual(len(iv.left(60, n=200)), 6)
+
+ def test_right(self):
+ iv = self.intervals
+ self.assertEqual(str(iv.left(60, n=2)),
+ str([IntervalObj(50, 59), IntervalObj(40, 49)]))
+
+ def get_right_start(b10):
+ r = iv.right(b10+1, n=1)
+ assert len(r) == 1
+ return r[0].start
+
+ for i in range(10, 100, 10):
+ self.assertEqual(get_right_start(i), i + 10)
+
+ for i in range(0, 100, 10):
+ r = iv.right(i-1, max_dist=10, n=1)
+ self.assertEqual(r[0].start, i)
+
+
+class UpDownStreamTestCase(unittest.TestCase):
+
+ def setUp(self):
+ iv = IntervalTree()
+ iv.add_interval(IntervalObj(50, 59))
+ for i in range(0, 110, 10):
+ if i == 50:
+ continue
+ f = IntervalObj(i, i + 9)
+ iv.add_interval(f)
+ self.intervals = iv
+
+ def test_upstream(self):
+ iv = self.intervals
+ upstreams = iv.upstream_of_interval(IntervalObj(59, 60),
+ num_intervals=200)
+
+ for u in upstreams:
+ self.assertTrue(u.end < 59)
+
+ upstreams = iv.upstream_of_interval(IntervalObj(60, 70, strand=-1),
+ num_intervals=200)
+ for u in upstreams:
+ self.assertTrue(u.start > 70)
+
+ upstreams = iv.upstream_of_interval(IntervalObj(58, 58, strand=-1),
+ num_intervals=200)
+ for u in upstreams:
+ self.assertTrue(u.start > 59)
+
+ def test_downstream(self):
+ iv = self.intervals
+ downstreams = iv.downstream_of_interval(IntervalObj(59, 60),
+ num_intervals=200)
+ for d in downstreams:
+ self.assertTrue(d.start > 60)
+
+ downstreams = iv.downstream_of_interval(IntervalObj(59, 60, strand=-1),
+ num_intervals=200)
+ for d in downstreams:
+ self.assertTrue(d.start < 59)
+
+ def test_n(self):
+ iv = self.intervals
+ for i in range(0, 90, 10):
+ r = iv.after(i, max_dist=20, num_intervals=2)
+ self.assertEqual(r[0].start, i + 10)
+ self.assertEqual(r[1].start, i + 20)
+
+ r = iv.after_interval(IntervalObj(i, i),
+ max_dist=20, num_intervals=2)
+ self.assertEqual(r[0].start, i + 10)
+ self.assertEqual(r[1].start, i + 20)
+
+
+class LotsaTestCase(unittest.TestCase):
+ """ put lotsa data in the tree and make sure it works"""
+ def setUp(self):
+ iv = IntervalNode(1, 2, IntervalObj(1, 2))
+ self.max = 1000000
+ for i in range(0, self.max, 10):
+ f = IntervalObj(i, i)
+ iv = iv.insert(f.start, f.end, f)
+
+ for i in range(600):
+ iv = iv.insert(0, 1, IntervalObj(0, 1))
+ self.intervals = iv
+
+ def test_count(self):
+ iv = self.intervals
+
+ r = iv.right(1, n=33)
+ self.assertEqual(len(r), 33)
+
+ l = iv.left(1, n=33)
+ self.assertEqual(len(l), 1)
+
+ u = iv.right(1, n=9999)
+ self.assertEqual(len(u), 250)
+
+ # now increase max_dist
+ u = iv.right(1, n=9999, max_dist=99999)
+ self.assertEqual(len(u), 9999)
+
+ def test_max_dist(self):
+ iv = self.intervals
+ r = iv.right(1, max_dist=0, n=10)
+ self.assertEqual(len(r), 0)
+
+ for n, d in enumerate(range(10, 1000, 10)):
+ r = iv.right(1, max_dist=d, n=10000)
+ self.assertEqual(len(r), n + 1)
+
+ def test_find(self):
+ iv = self.intervals
+ path = sys.path[:]
+ sys.path = sys.path[2:]
+ sys.path = path
+ for t in range(25):
+ start = random.randint(0, self.max - 10000)
+ end = start + random.randint(100, 10000)
+ results = iv.find(start, end)
+ for feat in results:
+ self.assertTrue(
+ (feat.end >= start and feat.end <= end) or
+ (feat.start <= end and feat.start >= start))
+
+
+class IntervalTreeTest(unittest.TestCase):
+ def setUp(self):
+ iv = IntervalTree()
+ n = 0
+ for i in range(1, 1000, 80):
+ iv.insert(i, i + 10, dict(value=i*i))
+ # add is synonym for insert.
+ iv.add(i + 20, i + 30, dict(astr=str(i*i)))
+
+ # or insert/add an interval object with start, end attrs.
+ iv.insert_interval(IntervalObj(i + 40, i + 50,
+ value=dict(astr=str(i*i))))
+ iv.add_interval(IntervalObj(i + 60, i + 70,
+ value=dict(astr=str(i*i))))
+
+ n += 4
+ self.intervals = self.iv = iv
+ self.nintervals = n
+
+ def test_find(self):
+ r = self.iv.find(100, 200)
+ self.assertEqual(len(r), 5)
+
+ def test_edge_cases(self):
+ iv = IntervalTree()
+ iv.insert(1, 1, 'foo')
+ iv.insert(3, 7, 'spam')
+ iv.insert(8, 8, 'abc')
+ self.assertEqual(iv.find(0, 1), [])
+ self.assertEqual(iv.find(1, 1), ['foo'])
+ self.assertEqual(iv.find(1, 2), ['foo'])
+ self.assertEqual(iv.find(2, 3), [])
+ self.assertEqual(iv.find(3, 3), ['spam'])
+ self.assertEqual(iv.find(3, 4), ['spam'])
+ self.assertEqual(iv.find(6, 7), ['spam'])
+ self.assertEqual(iv.find(7, 7), [])
+ self.assertEqual(iv.find(0, 8), ['foo', 'spam'])
+ self.assertEqual(iv.find(8, 8), ['abc'])
+ self.assertEqual(iv.find(8, 9), ['abc'])
+ self.assertEqual(iv.find(9, 9), [])
+ self.assertEqual(iv.find(0, 10), ['foo', 'spam', 'abc'])
+ self.assertEqual(iv.find(6, 10), ['spam', 'abc'])
+ self.assertEqual(iv.find(7, 9), ['abc'])
+
+ def test_traverse(self):
+ a = []
+ fn = a.append
+
+ self.iv.traverse(fn)
+ self.assertEqual(len(a), self.nintervals)
+
+ def test_empty(self):
+ iv = IntervalTree()
+ self.assertEqual([], iv.find(100, 300))
+ self.assertEqual([], iv.after(100))
+ self.assertEqual([], iv.before(100))
+ self.assertEqual([], iv.after_interval(100))
+ self.assertEqual([], iv.before_interval(100))
+ self.assertEqual([], iv.upstream_of_interval(100))
+ self.assertEqual([], iv.downstream_of_interval(100))
+ self.assertEqual(None, iv.traverse(lambda x: x.append(1)))
+
+ def test_public_interval(self):
+ self.iv.traverse(lambda ival: self.assertTrue(ival.interval))
+
+ def test_update(self):
+ i = 1
+ self.iv.update(i, i + 10, dict(value=i*i), dict(value=-1))
+ self.assertEqual([dict(value=-1)], self.iv.find(i, i+10))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/skbio/metadata/tests/test_interval.py b/skbio/metadata/tests/test_interval.py
new file mode 100644
index 0000000..235ba92
--- /dev/null
+++ b/skbio/metadata/tests/test_interval.py
@@ -0,0 +1,718 @@
+# ----------------------------------------------------------------------------
+# Copyright (c) 2013--, scikit-bio development team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+# ----------------------------------------------------------------------------
+
+import unittest
+from copy import deepcopy, copy
+
+from skbio.metadata._interval import (_assert_valid_bound,
+ _assert_valid_fuzzy)
+from skbio.metadata import Interval
+from skbio.metadata import IntervalMetadata
+from skbio.metadata._intersection import IntervalTree
+from skbio.util._testing import ReallyEqualMixin
+
+
+class TestInterval(unittest.TestCase, ReallyEqualMixin):
+ def setUp(self):
+ self.upper_bound = 100
+ self.im = IntervalMetadata(self.upper_bound)
+
+ def test_init_default(self):
+ f = Interval(self.im, bounds=[(0, 2), (4, self.upper_bound)])
+
+ self.assertTrue(f._interval_metadata is not None)
+ self.assertListEqual(f.bounds, [(0, 2), (4, self.upper_bound)])
+ self.assertListEqual(f.fuzzy, [(False, False), (False, False)])
+ self.assertDictEqual(f.metadata, {})
+
+ def test_init(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ self.assertTrue(f._interval_metadata is not None)
+ self.assertListEqual(f.bounds, [(1, 2), (4, 7)])
+ self.assertListEqual(f.fuzzy, [(True, False), (False, False)])
+ self.assertDictEqual(f.metadata, {'name': 'sagA',
+ 'function': 'transport'})
+
+ def test_init_iterables(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=((1, 2), (4, 7)),
+ fuzzy=((True, False), (False, False)),
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ self.assertTrue(f._interval_metadata is not None)
+ self.assertListEqual(f.bounds, [(1, 2), (4, 7)])
+ self.assertListEqual(f.fuzzy, [(True, False), (False, False)])
+ self.assertDictEqual(f.metadata, {'name': 'sagA',
+ 'function': 'transport'})
+
+ def test_init_generator(self):
+ def gen():
+ for x in [(1, 2), (4, 7)]:
+ yield x
+
+ f = Interval(interval_metadata=self.im,
+ bounds=gen(),
+ fuzzy=((True, False), (False, False)),
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ self.assertTrue(f._interval_metadata is not None)
+ self.assertListEqual(f.bounds, [(1, 2), (4, 7)])
+ self.assertListEqual(f.fuzzy, [(True, False), (False, False)])
+ self.assertDictEqual(f.metadata, {'name': 'sagA',
+ 'function': 'transport'})
+
+ def test_init_bounds_scrambled(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(4, 7), (1, 2)],
+ fuzzy=[(True, False), (False, True)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ self.assertTrue(f._interval_metadata is not None)
+ self.assertListEqual(f.bounds, [(1, 2), (4, 7)])
+ self.assertListEqual(f.fuzzy, [(False, True), (True, False)])
+ self.assertDictEqual(f.metadata, {'name': 'sagA',
+ 'function': 'transport'})
+
+ def test_init_no_interval_metadata(self):
+ with self.assertRaises(TypeError):
+ Interval(interval_metadata=None,
+ bounds=[(4, 7)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ def test_init_empty_metadata(self):
+ for i in 0, 1:
+ # test that no exception is raised
+ Interval(interval_metadata=self.im, bounds=[(i, i)])
+
+ def test_init_out_of_bounds(self):
+ with self.assertRaises(ValueError):
+ Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 101)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ with self.assertRaises(ValueError):
+ Interval(interval_metadata=self.im,
+ bounds=[(-1, 2), (4, 6)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ def test_init_bad_bounds(self):
+ with self.assertRaises(TypeError):
+ Interval(interval_metadata=self.im,
+ bounds=[1, (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ def test_init_bad_fuzzy(self):
+ with self.assertRaises(ValueError):
+ Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ def test_repr(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2)],
+ metadata={'name': 'sagA'})
+ exp = (r"Interval\(interval_metadata=<[0-9]+>, bounds=\[\(1, 2\)\],"
+ " fuzzy=\[\(False, False\)\], metadata={'name': 'sagA'}\)")
+ obs = repr(f)
+ self.assertRegex(obs, exp)
+ # test for dropped
+ f.drop()
+ exp = (r"Interval\(dropped=True, bounds=\[\(1, 2\)\],"
+ " fuzzy=\[\(False, False\)\], metadata={'name': 'sagA'}\)")
+ obs = repr(f)
+ self.assertRegex(obs, exp)
+
+ def test_drop(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2)],
+ metadata={'name': 'sagA'})
+ f.drop()
+ self.assertTrue(f._interval_metadata is None)
+ self.assertTrue(f.dropped)
+ self.assertTrue(f.bounds, [(1, 2)])
+ self.assertTrue(f.metadata, {'name': 'sagA'})
+ # test the idempotence
+ f.drop()
+ self.assertTrue(f._interval_metadata is None)
+ self.assertTrue(f.dropped)
+ self.assertTrue(f.bounds, [(1, 2)])
+ self.assertTrue(f.metadata, {'name': 'sagA'})
+
+ def test_eq(self):
+ f0 = Interval(interval_metadata=self.im,
+ bounds=[(4, 7), (1, 2)],
+ fuzzy=[(False, False), (True, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ f1 = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ f2 = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ f3 = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, True), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ f4 = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 8)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+
+ f5 = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagB', 'function': 'transport'})
+
+ # scramble bounds/fuzzy
+ self.assertReallyEqual(f0, f1)
+ self.assertReallyEqual(f2, f1)
+ # diff fuzzy
+ self.assertReallyNotEqual(f1, f3)
+ # diff bounds
+ self.assertReallyNotEqual(f1, f4)
+ # diff metadata
+ self.assertReallyNotEqual(f1, f5)
+
+ def test_get_bounds(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ self.assertEqual(f.bounds, [(1, 2), (4, 7)])
+ self.assertEqual(self.im._is_stale_tree, True)
+
+ def test_set_bounds(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ f.bounds = [(4, 7), (1, 3)]
+ self.assertEqual(f.bounds, [(1, 3), (4, 7)])
+ self.assertEqual(f.fuzzy, [(False, False), (False, False)])
+ self.assertEqual(self.im._is_stale_tree, True)
+
+ def test_set_bounds_bad(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ for value in [1, 's']:
+ with self.assertRaises(TypeError):
+ f.bounds = value
+
+ for value in [[(-1, 2)], # start < lower_bound
+ [(1, 101)], # end > upper_bound
+ [(3, 1)], # start < end
+ [('s', 1)], (), None]: # invalid values
+ with self.assertRaises(ValueError):
+ f.bounds = value
+
+ def test_get_fuzzy(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ self.assertEqual(f.fuzzy, [(True, False), (False, False)])
+
+ def test_set_fuzzy(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ f.fuzzy = [(False, False), (False, False)]
+ self.assertEqual(f.fuzzy, [(False, False), (False, False)])
+
+ def test_set_fuzzy_bad(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ for value in [[(False, False)], (), None]:
+ with self.assertRaises(ValueError):
+ f.fuzzy = value
+ for value in [1, True]:
+ with self.assertRaises(TypeError):
+ f.fuzzy = value
+
+ def test_delete_fuzzy(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ del f.fuzzy
+ self.assertEqual(f.fuzzy, [(False, False), (False, False)])
+ # delete again
+ del f.fuzzy
+ self.assertEqual(f.fuzzy, [(False, False), (False, False)])
+
+ def test_get_metadata(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ f.metadata['name'] = 'sagB'
+ self.assertEqual(f.metadata, {'name': 'sagB', 'function': 'transport'})
+
+ def test_set_metadata(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ f.metadata = {'name': 'sagB', 'function': 'transport'}
+ self.assertDictEqual(f.metadata,
+ {'name': 'sagB', 'function': 'transport'})
+ f.metadata = {}
+ self.assertDictEqual(f.metadata, {})
+
+ def test_set_metadata_bad(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ for value in [1, '', None]:
+ with self.assertRaises(TypeError):
+ f.metadata = value
+
+ def test_delete_metadata(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2), (4, 7)],
+ fuzzy=[(True, False), (False, False)],
+ metadata={'name': 'sagA', 'function': 'transport'})
+ del f.metadata
+ self.assertEqual(f.metadata, {})
+
+ def test_set_delete_on_dropped(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2)],
+ fuzzy=[(True, False)],
+ metadata={'name': 'sagA'})
+ f.drop()
+ with self.assertRaises(RuntimeError):
+ f.fuzzy = None
+ with self.assertRaises(RuntimeError):
+ f.bounds = [(1, 2)]
+ with self.assertRaises(RuntimeError):
+ f.metadata = {}
+ with self.assertRaises(RuntimeError):
+ del f.fuzzy
+ with self.assertRaises(RuntimeError):
+ del f.metadata
+
+ def test_get_on_dropped(self):
+ f = Interval(interval_metadata=self.im,
+ bounds=[(1, 2)],
+ fuzzy=[(True, False)],
+ metadata={'name': 'sagA'})
+ f.drop()
+
+ self.assertEqual(f.fuzzy, [(True, False)])
+ self.assertEqual(f.bounds, [(1, 2)])
+ self.assertEqual(f.metadata, {'name': 'sagA'})
+
+
+class TestIntervalUtil(unittest.TestCase):
+ def test_assert_valid_bound(self):
+ intvls = [(1, 2), (-1, 2)]
+ for intvl in intvls:
+ try:
+ _assert_valid_bound(intvl)
+ except TypeError:
+ self.assertTrue(False)
+
+ def test_assert_valid_bound_wrong_type(self):
+ intvls = [[1, 2], 1, [1, 2, 3]]
+ for intvl in intvls:
+ with self.assertRaises(TypeError):
+ _assert_valid_bound(intvl)
+
+ def test_assert_valid_bound_wrong_value(self):
+ intvls = [(1, 2, 3), (2, 1), (True, 0), ('s', 'r')]
+ for intvl in intvls:
+ with self.assertRaises(ValueError):
+ _assert_valid_bound(intvl)
+
+ def test_assert_valid_fuzzy(self):
+ fuzzy = [(True, False), (True, True)]
+ for fuzzy in fuzzy:
+ try:
+ _assert_valid_fuzzy(fuzzy)
+ except:
+ self.assertTrue(False)
+
+ def test_assert_valid_fuzzy_wrong_value(self):
+ fuzzy = [(True, False, True), ()]
+ for fuzzy in fuzzy:
+ with self.assertRaises(ValueError):
+ _assert_valid_fuzzy(fuzzy)
+
+ def test_assert_valid_fuzzy_wrong_type(self):
+ fuzzy = [[True, False], 's', 1, (0, 1), ('s', '')]
+ for fuzzy in fuzzy:
+ with self.assertRaises(TypeError):
+ _assert_valid_fuzzy(fuzzy)
+
+
+class TestIntervalMetadata(unittest.TestCase, ReallyEqualMixin):
+ def setUp(self):
+ self.upper_bound = 10
+ self.im_empty = IntervalMetadata(self.upper_bound)
+ self.im_1 = IntervalMetadata(self.upper_bound)
+ self.im_1_1 = Interval(
+ interval_metadata=self.im_1,
+ bounds=[(1, 2), (4, self.upper_bound)],
+ metadata={'gene': 'sagA', 'bound': 0})
+ self.im_2 = IntervalMetadata(self.upper_bound)
+ self.im_2_1 = Interval(
+ interval_metadata=self.im_2,
+ bounds=[(1, 2), (4, self.upper_bound)],
+ metadata={'gene': 'sagA', 'bound': 0})
+ self.im_2_2 = Interval(
+ interval_metadata=self.im_2,
+ bounds=[(3, 5)],
+ metadata={'gene': 'sagB', 'bound': 0, 'spam': [0]})
+
+ def test_copy_empty(self):
+ obs = copy(self.im_empty)
+ self.assertEqual(obs, self.im_empty)
+ self.assertIsNot(obs._intervals, self.im_empty._intervals)
+ self.assertIsNot(obs._interval_tree, self.im_empty._interval_tree)
+
+ def test_copy(self):
+ obs = copy(self.im_2)
+ self.assertEqual(obs, self.im_2)
+ self.assertIsNot(obs._intervals, self.im_2._intervals)
+ self.assertIsNot(obs._interval_tree, self.im_2._interval_tree)
+
+ for i in range(self.im_2.num_interval_features):
+ i1, i2 = obs._intervals[i], self.im_2._intervals[i]
+ self.assertIsNot(i1, i2)
+ self.assertIsNot(i1.bounds, i2.bounds)
+ self.assertIsNot(i1.fuzzy, i2.fuzzy)
+ self.assertIsNot(i1._interval_metadata, i2._interval_metadata)
+ self.assertIsNot(i1.metadata, i2.metadata)
+ for k in i1.metadata:
+ self.assertIs(i1.metadata[k], i2.metadata[k])
+
+ def test_deepcopy(self):
+ obs = deepcopy(self.im_2)
+ self.assertEqual(obs, self.im_2)
+ self.assertIsNot(obs._intervals, self.im_2._intervals)
+ self.assertIsNot(obs._interval_tree, self.im_2._interval_tree)
+
+ for i in range(self.im_2.num_interval_features):
+ i1, i2 = obs._intervals[i], self.im_2._intervals[i]
+ self.assertIsNot(i1, i2)
+ self.assertIsNot(i1.bounds, i2.bounds)
+ self.assertIsNot(i1.fuzzy, i2.fuzzy)
+ self.assertIsNot(i1.metadata, i2.metadata)
+
+ i2.metadata['spam'].append(1)
+ self.assertEqual(i2.metadata,
+ {'gene': 'sagB', 'bound': 0, 'spam': [0, 1]})
+ self.assertEqual(i1.metadata,
+ {'gene': 'sagB', 'bound': 0, 'spam': [0]})
+
+ def test_deepcopy_memo_is_respected(self):
+ memo = {}
+ deepcopy(self.im_1, memo)
+ self.assertGreater(len(memo), 2)
+
+ def test_init(self):
+ self.assertFalse(self.im_empty._is_stale_tree)
+ self.assertEqual(self.im_empty._intervals, [])
+
+ def test_init_upper_bound_lt_lower_bound(self):
+ # test that no exception is raised
+ IntervalMetadata(0)
+
+ with self.assertRaises(ValueError):
+ IntervalMetadata(-1)
+
+ def test_num_interval_features(self):
+ self.assertEqual(self.im_empty.num_interval_features, 0)
+ self.assertEqual(self.im_1.num_interval_features, 1)
+ self.assertEqual(self.im_2.num_interval_features, 2)
+
+ def test_duplicate(self):
+ '''Test query and drop methods on duplicate Intervals.'''
+ intvl_1 = self.im_empty.add([(1, 2)])
+ intvl_2 = self.im_empty.add([(1, 2)])
+ self.assertEqual(len(list(self.im_empty.query([(1, 2)]))), 2)
+ self.im_empty.drop([intvl_1])
+ self.assertEqual(len(self.im_empty._intervals), 1)
+ self.assertTrue(self.im_empty._intervals[0] is intvl_2)
+
+ def test_duplicate_bounds(self):
+ intvl = self.im_empty.add([(1, 2), (1, 2)])
+ intvls = list(self.im_empty.query([(1, 2)]))
+ self.assertEqual(len(intvls), 1)
+ self.assertTrue(intvl is intvls[0])
+
+ def test_concat_empty(self):
+ for i in 0, 1, 2:
+ obs = IntervalMetadata.concat([self.im_empty] * i)
+ exp = IntervalMetadata(self.upper_bound * i)
+ self.assertEqual(obs, exp)
+
+ obs = IntervalMetadata.concat([])
+ self.assertEqual(obs, IntervalMetadata(0))
+
+ def test_concat(self):
+ im1 = IntervalMetadata(3)
+ im2 = IntervalMetadata(4)
+ im3 = IntervalMetadata(5)
+ im1.add([(0, 2)], [(True, True)])
+ im2.add([(0, 3)], [(True, False)], {'gene': 'sagA'})
+ im2.add([(2, 4)], metadata={'gene': 'sagB'})
+ im3.add([(1, 5)], [(False, True)], {'gene': 'sagC'})
+ obs = IntervalMetadata.concat([im1, im2, im3])
+
+ exp = IntervalMetadata(12)
+ exp.add(bounds=[(0, 2)], fuzzy=[(True, True)])
+ exp.add(bounds=[(3, 6)], fuzzy=[(True, False)],
+ metadata={'gene': 'sagA'})
+ exp.add(bounds=[(5, 7)], metadata={'gene': 'sagB'})
+ exp.add(bounds=[(8, 12)], fuzzy=[(False, True)],
+ metadata={'gene': 'sagC'})
+ self.assertEqual(obs, exp)
+
+ def test_merge(self):
+ # empty + empty
+ im = IntervalMetadata(self.upper_bound)
+ self.im_empty.merge(im)
+ self.assertEqual(self.im_empty, im)
+ # empty + non-empty
+ self.im_empty.merge(self.im_1)
+ self.assertEqual(self.im_empty, self.im_1)
+ # non-empty + non-empty
+ self.im_empty.merge(self.im_2)
+ self.im_2.merge(self.im_1)
+ self.assertEqual(self.im_empty, self.im_2)
+
+ def test_merge_unequal_upper_bounds(self):
+ n = 3
+ im1 = IntervalMetadata(n)
+ for im in [self.im_empty, self.im_1]:
+ with self.assertRaisesRegex(
+ ValueError,
+ r'not equal \(%d != %d\)' % (self.upper_bound, n)):
+ im.merge(im1)
+
+ def test_sort(self):
+ interval = Interval(
+ self.im_2,
+ [(1, 2), (3, 8)],
+ metadata={'gene': 'sagA', 'bound': 0})
+ im = deepcopy(self.im_2)
+ self.im_2.sort(False)
+ # check sorting does not have other side effects
+ self.assertEqual(im, self.im_2)
+ self.assertEqual(self.im_2._intervals,
+ [self.im_2_2, self.im_2_1, interval])
+
+ self.im_2.sort()
+ self.assertEqual(im, self.im_2)
+ self.assertEqual(self.im_2._intervals,
+ [interval, self.im_2_1, self.im_2_2])
+
+ self.im_empty.sort()
+ self.assertEqual(self.im_empty, IntervalMetadata(self.upper_bound))
+
+ def test_add_eq_upper_bound(self):
+ self.im_empty.add(bounds=[(1, 2), (4, self.upper_bound)],
+ metadata={'gene': 'sagA', 'bound': 0})
+ self.assertTrue(self.im_empty._is_stale_tree)
+ interval = self.im_empty._intervals[0]
+ self.assertEqual(interval.bounds, [(1, 2), (4, self.upper_bound)])
+ self.assertEqual(interval.metadata, {'gene': 'sagA', 'bound': 0})
+ self.assertTrue(isinstance(self.im_empty._interval_tree, IntervalTree))
+
+ def test_add_gt_upper_bound(self):
+ with self.assertRaises(ValueError):
+ self.im_empty.add(bounds=[(1, 2), (4, self.upper_bound+1)],
+ metadata={'gene': 'sagA', 'bound': 0})
+
+ def test_add_eq_start_end_bound(self):
+ for i in 0, 1, self.upper_bound:
+ # test that no exception is raised
+ self.im_empty.add(bounds=[(i, i)],
+ metadata={'gene': 'sagA', 'bound': 0})
+
+ def test_query_attribute(self):
+ intervals = self.im_2._query_attribute({})
+ for i, j in zip(intervals, self.im_2._intervals):
+ self.assertEqual(i, j)
+
+ intervals = list(self.im_2._query_attribute(None))
+ self.assertEqual(len(intervals), 0)
+
+ for i in self.im_2._intervals:
+ intervals = list(self.im_2._query_attribute(i.metadata))
+ self.assertEqual(len(intervals), 1)
+ self.assertEqual(intervals[0], i)
+
+ def test_query_interval(self):
+ intervals = list(self.im_2._query_interval((1, 2)))
+ self.assertEqual(len(intervals), 1)
+ self.assertEqual(intervals[0], self.im_2_1)
+
+ intervals = list(self.im_2._query_interval((3, 4)))
+ self.assertEqual(len(intervals), 1)
+ self.assertEqual(intervals[0], self.im_2_2)
+
+ intervals = {repr(i) for i in self.im_2._query_interval((1, 7))}
+ self.assertEqual(len(intervals), 2)
+ self.assertSetEqual(intervals,
+ {repr(i) for i in self.im_2._intervals})
+
+ def test_query_interval_upper_bound(self):
+ intervals = list(self.im_2._query_interval((self.upper_bound-1,
+ self.upper_bound)))
+ self.assertEqual(intervals, [self.im_2_1])
+
+ def test_query(self):
+ intervals = list(self.im_2.query(bounds=[(1, 5)],
+ metadata={'gene': 'sagA'}))
+ self.assertEqual(len(intervals), 1)
+ self.assertEqual(intervals[0], self.im_2_1)
+
+ def test_query_empty(self):
+ intervals = list(self.im_1.query())
+ self.assertEqual(len(intervals), 0)
+
+ def test_query_no_hits(self):
+ intervals = list(self.im_2.query(bounds=[(self.upper_bound, 200)]))
+ self.assertEqual(len(intervals), 0)
+
+ intervals = list(self.im_2.query(metadata={'gene': 'sagC'}))
+ self.assertEqual(len(intervals), 0)
+
+ intervals = list(self.im_2.query(bounds=[(1, 2)],
+ metadata={'gene': 'sagC'}))
+ self.assertEqual(len(intervals), 0)
+
+ def test_query_interval_only(self):
+ for loc in [[(1, 7)],
+ [(1, 2), (3, 4)]]:
+ intervals = list(self.im_2.query(bounds=loc))
+ self.assertEqual(len(intervals), 2)
+ self.assertEqual(intervals[0], self.im_2_1)
+ self.assertEqual(intervals[1], self.im_2_2)
+
+ def test_query_metadata_only(self):
+ intervals = list(self.im_2.query(metadata={'gene': 'sagB'}))
+ self.assertEqual(len(intervals), 1)
+ self.assertEqual(intervals[0], self.im_2_2)
+
+ intervals = list(self.im_2.query(metadata={'bound': 0}))
+ self.assertEqual(len(intervals), 2)
+ self.assertEqual(intervals[0], self.im_2_1)
+ self.assertEqual(intervals[1], self.im_2_2)
+
+ def test_drop(self):
+ intvl = self.im_2._intervals[0]
+ self.im_2.drop([intvl])
+ self.assertEqual(len(self.im_2._intervals), 1)
+ self.assertEqual(self.im_2._intervals[0], self.im_2_2)
+ # test the intvl was set to dropped
+ self.assertTrue(intvl.dropped)
+
+ def test_drop_all(self):
+ self.im_2.drop(self.im_2._intervals)
+ self.assertEqual(self.im_2, self.im_empty)
+
+ def test_reverse(self):
+ self.im_2._reverse()
+ Interval(
+ interval_metadata=self.im_empty,
+ bounds=[(0, 6), (8, 9)],
+ metadata={'gene': 'sagA', 'bound': 0})
+ Interval(
+ interval_metadata=self.im_empty,
+ bounds=[(5, 7)],
+ metadata={'gene': 'sagB', 'bound': 0, 'spam': [0]})
+ self.assertEqual(self.im_2, self.im_empty)
+
+ def test_eq_ne(self):
+ im1 = IntervalMetadata(10)
+ im1.add(metadata={'gene': 'sagA', 'bound': '0'},
+ bounds=[(0, 2), (4, 7)])
+ im1.add(metadata={'gene': 'sagB', 'bound': '3'},
+ bounds=[(3, 5)])
+
+ # The ordering shouldn't matter
+ im2 = IntervalMetadata(10)
+ im2.add(metadata={'gene': 'sagB', 'bound': '3'},
+ bounds=[(3, 5)])
+ im2.add(metadata={'gene': 'sagA', 'bound': '0'},
+ bounds=[(0, 2), (4, 7)])
+
+ im3 = IntervalMetadata(10)
+ im3.add(metadata={'gene': 'sagA', 'bound': '3'},
+ bounds=[(0, 2), (4, 7)])
+ im3.add(metadata={'gene': 'sagB', 'bound': '3'},
+ bounds=[(3, 5)])
+
+ self.assertReallyEqual(im1, im2)
+ self.assertReallyNotEqual(im1, im3)
+
+ def test_ne_diff_bounds(self):
+ im1 = IntervalMetadata(10)
+ im2 = IntervalMetadata(9)
+ intvl = {'bounds': [(0, 1)], 'metadata': {'spam': 'foo'}}
+ im1.add(**intvl)
+ im2.add(**intvl)
+ self.assertReallyNotEqual(im1, im2)
+
+ def test_repr(self):
+ exp = '''0 interval features
+-------------------'''
+ self.assertEqual(repr(self.im_empty), exp)
+
+ self.im_empty.add([(1, 2)], metadata={'gene': 'sagA'})
+
+ exp = '''1 interval feature
+------------------
+Interval\(interval_metadata=<[0-9]+>, bounds=\[\(1, 2\)\], \
+fuzzy=\[\(False, False\)\], metadata={'gene': 'sagA'}\)'''
+ self.assertRegex(repr(self.im_empty), exp)
+
+ self.im_empty.add([(3, 4)], metadata={'gene': 'sagB'})
+ self.im_empty.add([(3, 4)], metadata={'gene': 'sagC'})
+ self.im_empty.add([(3, 4)], metadata={'gene': 'sagD'})
+ self.im_empty.add([(3, 4)], metadata={'gene': 'sagE'})
+ self.im_empty.add([(3, 4)], metadata={'gene': 'sagF'})
+ exp = '''6 interval features
+-------------------
+Interval\(interval_metadata=<[0-9]+>, bounds=\[\(1, 2\)\], \
+fuzzy=\[\(False, False\)\], metadata={'gene': 'sagA'}\)
+Interval\(interval_metadata=<[0-9]+>, bounds=\[\(3, 4\)\], \
+fuzzy=\[\(False, False\)\], metadata={'gene': 'sagB'}\)
+...
+Interval\(interval_metadata=<[0-9]+>, bounds=\[\(3, 4\)\], \
+fuzzy=\[\(False, False\)\], metadata={'gene': 'sagE'}\)
+Interval\(interval_metadata=<[0-9]+>, bounds=\[\(3, 4\)\], \
+fuzzy=\[\(False, False\)\], metadata={'gene': 'sagF'}\)'''
+ self.assertRegex(repr(self.im_empty), exp)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/skbio/metadata/tests/test_mixin.py b/skbio/metadata/tests/test_mixin.py
index a76a6ec..f274ffe 100644
--- a/skbio/metadata/tests/test_mixin.py
+++ b/skbio/metadata/tests/test_mixin.py
@@ -8,11 +8,14 @@
import unittest
-from skbio.metadata._mixin import MetadataMixin, PositionalMetadataMixin
+from skbio.metadata._mixin import (MetadataMixin,
+ PositionalMetadataMixin,
+ IntervalMetadataMixin)
from skbio.util._decorator import overrides
from skbio.util._testing import ReallyEqualMixin
from skbio.metadata._testing import (MetadataMixinTests,
- PositionalMetadataMixinTests)
+ PositionalMetadataMixinTests,
+ IntervalMetadataMixinTests)
class TestMetadataMixin(unittest.TestCase, ReallyEqualMixin,
@@ -76,5 +79,40 @@ class TestPositionalMetadataMixin(unittest.TestCase, ReallyEqualMixin,
self._positional_metadata_constructor_ = ExamplePositionalMetadataMixin
+class TestIntervalMetadataMixin(unittest.TestCase, ReallyEqualMixin,
+ IntervalMetadataMixinTests):
+ def setUp(self):
+ super()._set_up()
+
+ class ExampleIntervalMetadataMixin(IntervalMetadataMixin):
+ @overrides(IntervalMetadataMixin)
+ def _interval_metadata_axis_len_(self):
+ return self._axis_len
+
+ def __init__(self, axis_len, interval_metadata=None):
+ self._axis_len = axis_len
+ IntervalMetadataMixin._init_(
+ self, interval_metadata=interval_metadata)
+
+ def __eq__(self, other):
+ return IntervalMetadataMixin._eq_(self, other)
+
+ def __ne__(self, other):
+ return IntervalMetadataMixin._ne_(self, other)
+
+ def __copy__(self):
+ copy = self.__class__(self._axis_len, interval_metadata=None)
+ copy._interval_metadata = IntervalMetadataMixin._copy_(self)
+ return copy
+
+ def __deepcopy__(self, memo):
+ copy = self.__class__(self._axis_len, interval_metadata=None)
+ copy._interval_metadata = IntervalMetadataMixin._deepcopy_(
+ self, memo)
+ return copy
+
+ self._interval_metadata_constructor_ = ExampleIntervalMetadataMixin
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/skbio/sequence/_dna.py b/skbio/sequence/_dna.py
index 6f4c5a6..39c6274 100644
--- a/skbio/sequence/_dna.py
+++ b/skbio/sequence/_dna.py
@@ -29,6 +29,10 @@ class DNA(GrammaredSequence, NucleotideMixin,
Arbitrary per-character metadata. For example, quality data from
sequencing reads. Must be able to be passed directly to the Pandas
DataFrame constructor.
+ interval_metadata : IntervalMetadata
+ Arbitrary interval metadata which applies to intervals within
+ a sequence to store interval features (such as genes on the
+ DNA sequence).
lowercase : bool or str, optional
If ``True``, lowercase sequence characters will be converted to
uppercase characters in order to be valid IUPAC DNA characters. If
@@ -52,6 +56,7 @@ class DNA(GrammaredSequence, NucleotideMixin,
values
metadata
positional_metadata
+ interval_metadata
alphabet
gap_chars
default_gap_char
@@ -169,8 +174,8 @@ class DNA(GrammaredSequence, NucleotideMixin,
Notes
-----
- DNA sequence's metadata and positional metadata are included in the
- transcribed RNA sequence.
+ DNA sequence's metadata, positional, and interval
+ metadata are included in the transcribed RNA sequence.
Examples
--------
@@ -212,9 +217,14 @@ class DNA(GrammaredSequence, NucleotideMixin,
if self.has_positional_metadata():
positional_metadata = self.positional_metadata
+ interval_metadata = None
+ if self.has_interval_metadata():
+ interval_metadata = self.interval_metadata
+
# turn off validation because `seq` is guaranteed to be valid
return skbio.RNA(seq, metadata=metadata,
positional_metadata=positional_metadata,
+ interval_metadata=interval_metadata,
validate=False)
@stable(as_of="0.4.0")
diff --git a/skbio/sequence/_grammared_sequence.py b/skbio/sequence/_grammared_sequence.py
index b9c8396..e2217d0 100644
--- a/skbio/sequence/_grammared_sequence.py
+++ b/skbio/sequence/_grammared_sequence.py
@@ -105,6 +105,7 @@ class GrammaredSequence(Sequence, metaclass=GrammaredSequenceMeta):
----------
values
metadata
+ interval_metadata
positional_metadata
alphabet
gap_chars
@@ -328,9 +329,10 @@ class GrammaredSequence(Sequence, metaclass=GrammaredSequenceMeta):
@overrides(Sequence)
def __init__(self, sequence, metadata=None, positional_metadata=None,
- lowercase=False, validate=True):
+ interval_metadata=None, lowercase=False, validate=True):
super(GrammaredSequence, self).__init__(
- sequence, metadata, positional_metadata, lowercase)
+ sequence, metadata, positional_metadata,
+ interval_metadata, lowercase)
if validate:
self._validate()
@@ -695,7 +697,8 @@ class GrammaredSequence(Sequence, metaclass=GrammaredSequenceMeta):
yield self._constructor(
sequence=''.join(definite_seq),
metadata=metadata,
- positional_metadata=positional_metadata)
+ positional_metadata=positional_metadata,
+ interval_metadata=self.interval_metadata)
@stable(as_of='0.4.1')
def to_regex(self):
diff --git a/skbio/sequence/_nucleotide_mixin.py b/skbio/sequence/_nucleotide_mixin.py
index cf421e8..974c883 100644
--- a/skbio/sequence/_nucleotide_mixin.py
+++ b/skbio/sequence/_nucleotide_mixin.py
@@ -80,15 +80,15 @@ class NucleotideMixin(metaclass=ABCMeta):
Parameters
----------
reverse : bool, optional
- If ``True``, return the reverse complement. If positional metadata
- is present, it will be reversed.
+ If ``True``, return the reverse complement. If positional and/or
+ interval metadata are present, they will be reversed.
Returns
-------
NucleotideMixin
The (reverse) complement of the nucleotide sequence. The type and
metadata of the result will be the same as the nucleotide
- sequence. If `reverse` is ``True``, positional metadata
+ sequence. If `reverse` is ``True``, positional or interval metadata
will be reversed if it is present.
See Also
@@ -160,7 +160,18 @@ class NucleotideMixin(metaclass=ABCMeta):
positional_metadata=positional_metadata)
if reverse:
+ # this has to be before the interval metadata code,
+ # because __gititem__ drops interval_metadata.
complement = complement[::-1]
+
+ if self.has_interval_metadata():
+ complement.interval_metadata = self.interval_metadata
+ if reverse:
+ # TODO: this can be revised to match
+ # positional_metadata when __getitem__
+ # supports interval_metadata
+ complement.interval_metadata._reverse()
+
return complement
@stable(as_of='0.4.0')
diff --git a/skbio/sequence/_protein.py b/skbio/sequence/_protein.py
index afb601b..5681e33 100644
--- a/skbio/sequence/_protein.py
+++ b/skbio/sequence/_protein.py
@@ -29,6 +29,9 @@ class Protein(GrammaredSequence, metaclass=DisableSubclassingMeta):
Arbitrary per-character metadata. For example, quality data from
sequencing reads. Must be able to be passed directly to the Pandas
DataFrame constructor.
+ interval_metadata : IntervalMetadata
+ Arbitrary interval metadata which applies to intervals within
+ a sequence to store interval features (such as protein domains).
lowercase : bool or str, optional
If ``True``, lowercase sequence characters will be converted to
uppercase characters in order to be valid IUPAC Protein characters. If
@@ -51,6 +54,7 @@ class Protein(GrammaredSequence, metaclass=DisableSubclassingMeta):
----------
values
metadata
+ interval_metadata
positional_metadata
alphabet
gap_chars
diff --git a/skbio/sequence/_rna.py b/skbio/sequence/_rna.py
index a5dba07..ba0c27a 100644
--- a/skbio/sequence/_rna.py
+++ b/skbio/sequence/_rna.py
@@ -29,6 +29,9 @@ class RNA(GrammaredSequence, NucleotideMixin,
Arbitrary per-character metadata. For example, quality data from
sequencing reads. Must be able to be passed directly to the Pandas
DataFrame constructor.
+ interval_metadata : IntervalMetadata
+ Arbitrary metadata which applies to intervals within a sequence to
+ store interval features (such as exons or introns on the sequence).
lowercase : bool or str, optional
If ``True``, lowercase sequence characters will be converted to
uppercase characters in order to be valid IUPAC RNA characters. If
@@ -51,6 +54,7 @@ class RNA(GrammaredSequence, NucleotideMixin,
----------
values
metadata
+ interval_metadata
positional_metadata
alphabet
gap_chars
@@ -212,9 +216,14 @@ class RNA(GrammaredSequence, NucleotideMixin,
if self.has_positional_metadata():
positional_metadata = self.positional_metadata
+ interval_metadata = None
+ if self.has_interval_metadata():
+ interval_metadata = self.interval_metadata
+
# turn off validation because `seq` is guaranteed to be valid
return skbio.DNA(seq, metadata=metadata,
positional_metadata=positional_metadata,
+ interval_metadata=interval_metadata,
validate=False)
@stable(as_of="0.4.0")
diff --git a/skbio/sequence/_sequence.py b/skbio/sequence/_sequence.py
index 6a2c21a..09c59e7 100644
--- a/skbio/sequence/_sequence.py
+++ b/skbio/sequence/_sequence.py
@@ -16,14 +16,16 @@ import pandas as pd
import skbio.sequence.distance
from skbio._base import SkbioObject
-from skbio.metadata._mixin import MetadataMixin, PositionalMetadataMixin
+from skbio.metadata._mixin import (MetadataMixin, PositionalMetadataMixin,
+ IntervalMetadataMixin)
+from skbio.metadata import IntervalMetadata
from skbio.sequence._repr import _SequenceReprBuilder
-from skbio.util._decorator import (stable, experimental, deprecated,
- classonlymethod, overrides)
+from skbio.util._decorator import (stable, experimental, classonlymethod,
+ overrides)
-class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
- SkbioObject):
+class Sequence(MetadataMixin, PositionalMetadataMixin, IntervalMetadataMixin,
+ collections.Sequence, SkbioObject):
"""Store generic sequence data and optional associated metadata.
``Sequence`` objects do not enforce an alphabet or grammar and are thus the
@@ -51,6 +53,9 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
constructor. Each column of metadata must be the same length as
`sequence`. A shallow copy of the positional metadata will be made if
necessary (see Examples section below for details).
+ interval_metadata : IntervalMetadata
+ Arbitrary metadata which applies to intervals within a sequence to
+ store interval features (such as genes, ncRNA on the sequence).
lowercase : bool or str, optional
If ``True``, lowercase sequence characters will be converted to
uppercase characters. If ``False``, no characters will be converted.
@@ -64,6 +69,7 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
values
metadata
positional_metadata
+ interval_metadata
observed_chars
See Also
@@ -83,6 +89,7 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
--------
>>> from pprint import pprint
>>> from skbio import Sequence
+ >>> from skbio.metadata import IntervalMetadata
**Creating sequences:**
@@ -102,8 +109,11 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
>>> metadata = {'id':'seq-id', 'desc':'seq desc', 'authors': ['Alice']}
>>> positional_metadata = {'quality': [3, 3, 4, 10],
... 'exons': [True, True, False, True]}
+ >>> interval_metadata = IntervalMetadata(4)
+ >>> interval = interval_metadata.add([(1, 3)], metadata={'gene': 'sagA'})
>>> seq = Sequence('ACGT', metadata=metadata,
- ... positional_metadata=positional_metadata)
+ ... positional_metadata=positional_metadata,
+ ... interval_metadata=interval_metadata)
>>> seq
Sequence
-----------------------------
@@ -114,6 +124,8 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
Positional metadata:
'exons': <dtype: bool>
'quality': <dtype: int64>
+ Interval metadata:
+ 1 interval feature
Stats:
length: 4
-----------------------------
@@ -155,6 +167,14 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
2 False 4
3 True 10
+ Retrieve interval metadata:
+
+ >>> seq.interval_metadata # doctest: +ELLIPSIS
+ 1 interval feature
+ ------------------
+ Interval(interval_metadata=<...>, bounds=[(1, 3)], \
+fuzzy=[(False, False)], metadata={'gene': 'sagA'})
+
**Updating sequence metadata:**
.. warning:: Be aware that a shallow copy of ``metadata`` and
@@ -314,6 +334,32 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
2 [] 4
3 [] 10
+ You can also update the interval metadata. Let's re-create a
+ ``Sequence`` object with interval metadata at first:
+
+ >>> seq = Sequence('ACGT')
+ >>> interval = seq.interval_metadata.add(
+ ... [(1, 3)], metadata={'gene': 'foo'})
+
+ You can update directly on the ``Interval`` object:
+
+ >>> interval # doctest: +ELLIPSIS
+ Interval(interval_metadata=<...>, bounds=[(1, 3)], \
+fuzzy=[(False, False)], metadata={'gene': 'foo'})
+ >>> interval.bounds = [(0, 2)]
+ >>> interval # doctest: +ELLIPSIS
+ Interval(interval_metadata=<...>, bounds=[(0, 2)], \
+fuzzy=[(False, False)], metadata={'gene': 'foo'})
+
+ You can also query and obtain the interval features you are
+ interested and then modify them:
+
+ >>> intervals = list(seq.interval_metadata.query(metadata={'gene': 'foo'}))
+ >>> intervals[0].fuzzy = [(True, False)]
+ >>> print(intervals[0]) # doctest: +ELLIPSIS
+ Interval(interval_metadata=<...>, bounds=[(0, 2)], \
+fuzzy=[(True, False)], metadata={'gene': 'foo'})
+
"""
_number_of_extended_ascii_codes = 256
# ASCII is built such that the difference between uppercase and lowercase
@@ -396,7 +442,7 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
Parameters
----------
- seqs : iterable (Sequence)
+ sequences : iterable (Sequence)
An iterable of ``Sequence`` objects or appropriate subclasses.
how : {'strict', 'inner', 'outer'}, optional
How to intersect the `positional_metadata` of the sequences.
@@ -526,7 +572,9 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
pm = pd.concat(pm_data, join=how, ignore_index=True)
bytes_ = np.concatenate(seq_data)
- return cls(bytes_, positional_metadata=pm)
+ im = IntervalMetadata.concat(i.interval_metadata for i in seqs)
+
+ return cls(bytes_, positional_metadata=pm, interval_metadata=im)
@classmethod
def _assert_can_cast_to(cls, target):
@@ -538,9 +586,13 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
def _positional_metadata_axis_len_(self):
return len(self)
+ @overrides(IntervalMetadataMixin)
+ def _interval_metadata_axis_len_(self):
+ return len(self)
+
@stable(as_of="0.4.0")
def __init__(self, sequence, metadata=None, positional_metadata=None,
- lowercase=False):
+ interval_metadata=None, lowercase=False):
if isinstance(sequence, np.ndarray):
if sequence.dtype == np.uint8:
self._set_bytes_contiguous(sequence)
@@ -566,7 +618,9 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
if (positional_metadata is None and
sequence.has_positional_metadata()):
positional_metadata = sequence.positional_metadata
-
+ if (interval_metadata is None and
+ sequence.has_interval_metadata()):
+ interval_metadata = sequence.interval_metadata
sequence = sequence._bytes
self._owns_bytes = False
self._set_bytes(sequence)
@@ -592,6 +646,8 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
MetadataMixin._init_(self, metadata=metadata)
PositionalMetadataMixin._init_(
self, positional_metadata=positional_metadata)
+ IntervalMetadataMixin._init_(
+ self, interval_metadata=interval_metadata)
if lowercase is False:
pass
@@ -705,13 +761,14 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
False
Define a sequence with the same sequence of characters as ``u`` but
- with different metadata and positional metadata:
+ with different metadata, positional metadata, and interval metadata:
>>> v = Sequence('ACGA', metadata={'id': 'abc'},
... positional_metadata={'quality':[1, 5, 3, 3]})
+ >>> _ = v.interval_metadata.add([(0, 1)])
- The two sequences are not considered equal because their metadata and
- positional metadata do not match:
+ The two sequences are not considered equal because their metadata,
+ positional metadata, and interval metadata do not match:
>>> u == v
False
@@ -730,6 +787,9 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
if not PositionalMetadataMixin._eq_(self, other):
return False
+ if not IntervalMetadataMixin._eq_(self, other):
+ return False
+
return True
@stable(as_of="0.4.0")
@@ -770,6 +830,11 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
def __getitem__(self, indexable):
"""Slice this sequence.
+ Notes
+ -----
+ This drops the ``self.interval_metadata`` from the returned
+ new ``Sequence`` object.
+
Parameters
----------
indexable : int, slice, iterable (int and slice), 1D array_like (bool)
@@ -1033,6 +1098,7 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
in the order they appear in the positional metadata ``pd.DataFrame``.
Column names (i.e., keys) follow the same display rules as metadata
keys
+ * interval metadata: the number of interval features will be displayed.
* sequence stats (e.g., length)
* up to five lines of chunked sequence data. Each line of chunked
sequence data displays the current position in the sequence
@@ -1052,6 +1118,7 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
Short sequence without metadata:
>>> from skbio import Sequence
+ >>> from skbio.metadata._interval import IntervalMetadata
>>> Sequence('ACGTAATGGATACGTAATGCA')
Sequence
-------------------------
@@ -1074,7 +1141,7 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
300 ACGTACGTAC GTACGTACGT ACGTACGTAC GTACGTACGT ACGTACGTAC GTACGTACGT
360 ACGTACGTAC GTACGTACGT ACGTACGTAC GTACGTACGT
- Sequence with metadata and positional metadata:
+ Sequence with metadata, positional metadata, and interval metadata:
>>> metadata = {
... 'id': 'seq-id',
@@ -1088,8 +1155,10 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
... 'quality': [3, 10, 11, 10],
... 'exons': [True, True, False, True]
... }
- >>> Sequence('ACGT', metadata=metadata,
+ >>> seq = Sequence('ACGT', metadata=metadata,
... positional_metadata=positional_metadata)
+ >>> _ = seq.interval_metadata.add([(0, 2)], metadata={'gene': 'sagA'})
+ >>> seq
Sequence
----------------------------------------------------------------------
Metadata:
@@ -1102,6 +1171,8 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
Positional metadata:
'exons': <dtype: bool>
'quality': <dtype: int64>
+ Interval metadata:
+ 1 interval feature
Stats:
length: 4
----------------------------------------------------------------------
@@ -1164,129 +1235,6 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
"""
return self._copy(True, memo)
- @deprecated(as_of="0.4.1", until="0.5.1",
- reason="Use `copy.copy(seq)` instead of "
- "`seq.copy(deep=False)`, and `copy.deepcopy(seq)` "
- "instead of `seq.copy(deep=True)`.")
- def copy(self, deep=False):
- """Return a copy of this sequence.
-
- Parameters
- ----------
- deep : bool, optional
- Perform a deep copy. If ``False``, perform a shallow copy.
-
- Returns
- -------
- Sequence
- Copy of this sequence.
-
- Notes
- -----
- Since sequence objects can share the same underlying immutable sequence
- data (or pieces of it), this method can be used to create a sequence
- object with its own copy of the sequence data so that the original
- sequence data can be garbage-collected.
-
- Examples
- --------
- Create a sequence:
-
- >>> from pprint import pprint
- >>> from skbio import Sequence
- >>> seq = Sequence('ACGT',
- ... metadata={'id': 'seq-id', 'authors': ['Alice']},
- ... positional_metadata={'quality': [7, 10, 8, 5],
- ... 'list': [[], [], [], []]})
-
- Make a shallow copy of the sequence:
-
- >>> seq_copy = seq.copy()
- >>> seq_copy == seq
- True
-
- Setting new references in the copied sequence's metadata doesn't affect
- the original sequence's metadata:
-
- >>> seq_copy.metadata['id'] = 'new-id'
- >>> pprint(seq_copy.metadata)
- {'authors': ['Alice'], 'id': 'new-id'}
- >>> pprint(seq.metadata)
- {'authors': ['Alice'], 'id': 'seq-id'}
-
- The same applies to the sequence's positional metadata:
-
- >>> seq_copy.positional_metadata.loc[0, 'quality'] = 999
- >>> seq_copy.positional_metadata
- list quality
- 0 [] 999
- 1 [] 10
- 2 [] 8
- 3 [] 5
- >>> seq.positional_metadata
- list quality
- 0 [] 7
- 1 [] 10
- 2 [] 8
- 3 [] 5
-
- Since only a *shallow* copy was made, updates to mutable objects stored
- as metadata affect the original sequence's metadata:
-
- >>> seq_copy.metadata['authors'].append('Bob')
- >>> pprint(seq_copy.metadata)
- {'authors': ['Alice', 'Bob'], 'id': 'new-id'}
- >>> pprint(seq.metadata)
- {'authors': ['Alice', 'Bob'], 'id': 'seq-id'}
-
- The same applies to the sequence's positional metadata:
-
- >>> seq_copy.positional_metadata.loc[0, 'list'].append(1)
- >>> seq_copy.positional_metadata
- list quality
- 0 [1] 999
- 1 [] 10
- 2 [] 8
- 3 [] 5
- >>> seq.positional_metadata
- list quality
- 0 [1] 7
- 1 [] 10
- 2 [] 8
- 3 [] 5
-
- Perform a deep copy to avoid this behavior:
-
- >>> seq_deep_copy = seq.copy(deep=True)
-
- Updates to mutable objects no longer affect the original sequence's
- metadata:
-
- >>> seq_deep_copy.metadata['authors'].append('Carol')
- >>> pprint(seq_deep_copy.metadata)
- {'authors': ['Alice', 'Bob', 'Carol'], 'id': 'seq-id'}
- >>> pprint(seq.metadata)
- {'authors': ['Alice', 'Bob'], 'id': 'seq-id'}
-
- Nor its positional metadata:
-
- >>> seq_deep_copy.positional_metadata.loc[0, 'list'].append(2)
- >>> seq_deep_copy.positional_metadata
- list quality
- 0 [1, 2] 7
- 1 [] 10
- 2 [] 8
- 3 [] 5
- >>> seq.positional_metadata
- list quality
- 0 [1] 7
- 1 [] 10
- 2 [] 8
- 3 [] 5
-
- """
- return self._copy(deep, {})
-
def _copy(self, deep, memo):
# strategy: copy the sequence without metadata first, then set metadata
# attributes with copies. we take this approach instead of simply
@@ -1302,16 +1250,21 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
bytes_ = np.copy(self._bytes)
seq_copy = self._constructor(sequence=bytes_, metadata=None,
- positional_metadata=None)
+ positional_metadata=None,
+ interval_metadata=None)
if deep:
seq_copy._metadata = MetadataMixin._deepcopy_(self, memo)
seq_copy._positional_metadata = \
PositionalMetadataMixin._deepcopy_(self, memo)
+ seq_copy._interval_metadata = IntervalMetadataMixin._deepcopy_(
+ self, memo)
else:
seq_copy._metadata = MetadataMixin._copy_(self)
seq_copy._positional_metadata = \
PositionalMetadataMixin._copy_(self)
+ seq_copy._interval_metadata = IntervalMetadataMixin._copy_(
+ self)
return seq_copy
@@ -1484,11 +1437,15 @@ class Sequence(MetadataMixin, PositionalMetadataMixin, collections.Sequence,
if self.has_positional_metadata():
positional_metadata = self.positional_metadata
+ interval_metadata = None
+ if self.has_interval_metadata():
+ interval_metadata = self.interval_metadata
# Use __class__ instead of _constructor so that validations are
# performed for subclasses (the user could have introduced invalid
# characters).
return self.__class__(seq_bytes, metadata=metadata,
- positional_metadata=positional_metadata)
+ positional_metadata=positional_metadata,
+ interval_metadata=interval_metadata)
@stable(as_of="0.4.0")
def index(self, subsequence, start=None, end=None):
diff --git a/skbio/sequence/tests/test_dna.py b/skbio/sequence/tests/test_dna.py
index 1141359..bd366a9 100644
--- a/skbio/sequence/tests/test_dna.py
+++ b/skbio/sequence/tests/test_dna.py
@@ -9,6 +9,7 @@
import unittest
from skbio import DNA, RNA
+from skbio.metadata import IntervalMetadata
# tests specific to DNA go here. tests for functionality shared by DNA and RNA
@@ -27,10 +28,15 @@ class TestDNA(unittest.TestCase):
self.assertEqual(DNA('TTTG').transcribe(), RNA('UUUG'))
def test_transcribe_preserves_all_metadata(self):
+ im = IntervalMetadata(4)
+ im.add([(0, 2)], metadata={'gene': 'p53'})
+
exp = RNA('AGUU', metadata={'foo': 'bar'},
- positional_metadata={'foo': range(4)})
+ positional_metadata={'foo': range(4)},
+ interval_metadata=im)
seq = DNA('AGTT', metadata={'foo': 'bar'},
- positional_metadata={'foo': range(4)})
+ positional_metadata={'foo': range(4)},
+ interval_metadata=im)
self.assertEqual(seq.transcribe(), exp)
def test_transcribe_does_not_modify_input(self):
diff --git a/skbio/sequence/tests/test_grammared_sequence.py b/skbio/sequence/tests/test_grammared_sequence.py
index 17a32d5..473e34c 100644
--- a/skbio/sequence/tests/test_grammared_sequence.py
+++ b/skbio/sequence/tests/test_grammared_sequence.py
@@ -15,6 +15,7 @@ import pandas as pd
from skbio.sequence import GrammaredSequence
from skbio.util import classproperty
from skbio.util import assert_data_frame_almost_equal
+from skbio.metadata import IntervalMetadata
class ExampleGrammaredSequence(GrammaredSequence):
@@ -146,17 +147,23 @@ class TestGrammaredSequence(TestCase):
self.assertEqual(seq.metadata, {})
assert_data_frame_almost_equal(seq.positional_metadata,
pd.DataFrame(index=range(8)))
+ self.assertEqual(seq.interval_metadata,
+ IntervalMetadata(8))
def test_init_nondefault_parameters(self):
+ im = IntervalMetadata(8)
+ im.add([(1, 8)], metadata={'gene': 'p53'})
seq = ExampleGrammaredSequence(
'.-ABCXYZ',
metadata={'id': 'foo'},
- positional_metadata={'quality': range(8)})
+ positional_metadata={'quality': range(8)},
+ interval_metadata=im)
npt.assert_equal(seq.values, np.array('.-ABCXYZ', dtype='c'))
self.assertEqual(seq.metadata, {'id': 'foo'})
assert_data_frame_almost_equal(seq.positional_metadata,
pd.DataFrame({'quality': range(8)}))
+ self.assertEqual(seq.interval_metadata, im)
def test_init_valid_empty_sequence(self):
# just make sure we can instantiate an empty sequence regardless of
diff --git a/skbio/sequence/tests/test_nucleotide_sequences.py b/skbio/sequence/tests/test_nucleotide_sequences.py
index f83be6f..e7d7c44 100644
--- a/skbio/sequence/tests/test_nucleotide_sequences.py
+++ b/skbio/sequence/tests/test_nucleotide_sequences.py
@@ -14,6 +14,7 @@ from skbio import DNA, RNA, Protein, GeneticCode
from skbio.sequence._nucleotide_mixin import NucleotideMixin
from skbio.sequence import GrammaredSequence
from skbio.util import classproperty
+from skbio.metadata import IntervalMetadata
# This file contains tests for functionality of sequence types which implement
@@ -21,7 +22,7 @@ from skbio.util import classproperty
# similar that the testing logic can be shared and parameterized across
# different test data.
-class TestNucelotideSequence(unittest.TestCase):
+class TestNucleotideSequence(unittest.TestCase):
def setUp(self):
self.sequence_kinds = frozenset([
str,
@@ -272,7 +273,8 @@ class TestNucelotideSequence(unittest.TestCase):
comp = constructor(
'',
metadata={'id': 'foo', 'description': 'bar'},
- positional_metadata={'quality': []}).complement()
+ positional_metadata={'quality': []},
+ interval_metadata=IntervalMetadata(0)).complement()
self.assertEqual(
comp,
constructor(
@@ -286,16 +288,20 @@ class TestNucelotideSequence(unittest.TestCase):
comp = constructor(seq_str).complement()
self.assertEqual(comp, constructor(comp_str))
+ im = IntervalMetadata(len(seq_str))
+ im.add([(0, 1)], metadata={'gene': 'p53'})
comp = constructor(
seq_str,
metadata={'id': 'foo', 'description': 'bar'},
- positional_metadata={'quality': qual}).complement()
+ positional_metadata={'quality': qual},
+ interval_metadata=im).complement()
self.assertEqual(
comp,
constructor(
comp_str,
metadata={'id': 'foo', 'description': 'bar'},
- positional_metadata={'quality': qual}))
+ positional_metadata={'quality': qual},
+ interval_metadata=im))
def test_complement_with_reverse_empty(self):
for constructor in (DNA, RNA):
@@ -305,7 +311,8 @@ class TestNucelotideSequence(unittest.TestCase):
rc = constructor(
'',
metadata={'id': 'foo', 'description': 'bar'},
- positional_metadata={'quality': []}).complement(reverse=True)
+ positional_metadata={'quality': []},
+ interval_metadata=IntervalMetadata(0)).complement(reverse=True)
self.assertEqual(
rc,
constructor(
@@ -319,18 +326,30 @@ class TestNucelotideSequence(unittest.TestCase):
rc = constructor(seq_str).complement(reverse=True)
self.assertEqual(rc, constructor(rev_comp_str))
- rc = constructor(
+ l = len(seq_str)
+ im = IntervalMetadata(l)
+ im.add([(0, 1)], metadata={'gene': 'p53'})
+ im_rc = IntervalMetadata(l)
+ im_rc.add([(l-1, l)], metadata={'gene': 'p53'})
+ original = constructor(
seq_str,
metadata={'id': 'foo', 'description': 'bar'},
positional_metadata={
- 'quality': qual}).complement(reverse=True)
+ 'quality': qual},
+ interval_metadata=im)
+ rc = original.complement(reverse=True)
+
self.assertEqual(
rc,
constructor(
rev_comp_str,
metadata={'id': 'foo', 'description': 'bar'},
positional_metadata={'quality':
- list(qual)[::-1]}))
+ list(qual)[::-1]},
+ interval_metadata=im_rc))
+ # assert the original object is not changed
+ self.assertIsNot(original.interval_metadata, im)
+ self.assertEqual(original.interval_metadata, im)
def test_reverse_complement(self):
# light tests because this just calls
diff --git a/skbio/sequence/tests/test_rna.py b/skbio/sequence/tests/test_rna.py
index 99dd331..3cc1dc1 100644
--- a/skbio/sequence/tests/test_rna.py
+++ b/skbio/sequence/tests/test_rna.py
@@ -9,6 +9,7 @@
import unittest
from skbio import DNA, RNA
+from skbio.metadata import IntervalMetadata
# tests specific to RNA go here. tests for functionality shared by DNA and RNA
@@ -27,10 +28,15 @@ class TestRNA(unittest.TestCase):
self.assertEqual(DNA('TTTG'), RNA('UUUG').reverse_transcribe())
def test_reverse_transcribe_preserves_all_metadata(self):
+ im = IntervalMetadata(4)
+ im.add([(0, 2)], metadata={'gene': 'p53'})
+
seq = RNA('AGUU', metadata={'foo': 'bar'},
- positional_metadata={'foo': range(4)})
+ positional_metadata={'foo': range(4)},
+ interval_metadata=im)
exp = DNA('AGTT', metadata={'foo': 'bar'},
- positional_metadata={'foo': range(4)})
+ positional_metadata={'foo': range(4)},
+ interval_metadata=im)
self.assertEqual(seq.reverse_transcribe(), exp)
def test_reverse_transcribe_does_not_modify_input(self):
diff --git a/skbio/sequence/tests/test_sequence.py b/skbio/sequence/tests/test_sequence.py
index 89356fb..2fb691d 100644
--- a/skbio/sequence/tests/test_sequence.py
+++ b/skbio/sequence/tests/test_sequence.py
@@ -26,7 +26,9 @@ from skbio.sequence._sequence import (_single_index_to_slice, _is_single_index,
_as_slice_if_single_index)
from skbio.util._testing import ReallyEqualMixin
from skbio.metadata._testing import (MetadataMixinTests,
+ IntervalMetadataMixinTests,
PositionalMetadataMixinTests)
+from skbio.metadata import IntervalMetadata
class SequenceSubclass(Sequence):
@@ -53,6 +55,17 @@ class TestSequencePositionalMetadata(TestCase, ReallyEqualMixin,
self._positional_metadata_constructor_ = factory
+class TestSequenceIntervalMetadata(TestCase, ReallyEqualMixin,
+ IntervalMetadataMixinTests):
+ def setUp(self):
+ super()._set_up()
+
+ def factory(axis_len, interval_metadata=None):
+ return Sequence('Z' * axis_len,
+ interval_metadata=interval_metadata)
+ self._interval_metadata_constructor_ = factory
+
+
class TestSequenceBase(TestCase):
def setUp(self):
self.sequence_kinds = frozenset([
@@ -101,6 +114,37 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
with self.assertRaises(TypeError):
SequenceSubclass.concat([seq1, seq2])
+ def test_concat_interval_metadata(self):
+ seq1 = Sequence("1234")
+ seq1.interval_metadata.add(
+ [(0, 2)], [(True, False)], {'gene': 'sagA'})
+ seq2 = Sequence("5678")
+ seq2.interval_metadata.add(
+ [(1, 3)], [(False, True)], {'gene': 'sagB'})
+ obs = Sequence.concat([seq1, seq2])
+ exp = Sequence('12345678')
+ exp.interval_metadata.add(
+ [(0, 2)], [(True, False)], {'gene': 'sagA'})
+ exp.interval_metadata.add(
+ [(5, 7)], [(False, True)], {'gene': 'sagB'})
+ self.assertEqual(exp, obs)
+
+ def test_concat_one_seq_has_none_interval_metadata(self):
+ seq1 = Sequence("1234")
+ seq1.interval_metadata.add(
+ [(0, 2)], [(True, False)], {'gene': 'sagA'})
+ seq2 = Sequence("5678")
+ seq3 = Sequence("910")
+ seq3.interval_metadata.add(
+ [(1, 3)], [(False, True)], {'gene': 'sagB'})
+ obs = Sequence.concat([seq1, seq2, seq3])
+ exp = Sequence('12345678910')
+ exp.interval_metadata.add(
+ [(0, 2)], [(True, False)], {'gene': 'sagA'})
+ exp.interval_metadata.add(
+ [(9, 11)], [(False, True)], {'gene': 'sagB'})
+ self.assertEqual(exp, obs)
+
def test_concat_default_how(self):
seq1 = Sequence("1234", positional_metadata={'a': [1]*4})
seq2 = Sequence("5678", positional_metadata={'a': [2]*4})
@@ -199,14 +243,22 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
self.assertEqual(seq.metadata, {})
assert_data_frame_almost_equal(seq.positional_metadata,
pd.DataFrame(index=range(11)))
+ self.assertEqual(seq.interval_metadata,
+ IntervalMetadata(len(seq)))
def test_init_nondefault_parameters(self):
- seq = Sequence('.ABC123xyz-',
+ s = '.ABC123xyz-'
+ im = IntervalMetadata(len(s))
+ im.add([(0, 1)], metadata={'gene': 'sagA'})
+ seq = Sequence(s,
metadata={'id': 'foo', 'description': 'bar baz'},
- positional_metadata={'quality': range(11)})
+ positional_metadata={'quality': range(11)},
+ interval_metadata=im)
+
+ self.assertEqual(seq.interval_metadata, im)
npt.assert_equal(seq.values, np.array('.ABC123xyz-', dtype='c'))
- self.assertEqual('.ABC123xyz-', str(seq))
+ self.assertEqual(s, str(seq))
self.assertTrue(seq.metadata)
self.assertEqual(seq.metadata, {'id': 'foo', 'description': 'bar baz'})
@@ -234,6 +286,9 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
self.assertFalse(seq.metadata)
self.assertEqual(seq.metadata, {})
+ self.assertEqual(seq.interval_metadata,
+ IntervalMetadata(0))
+
assert_data_frame_almost_equal(seq.positional_metadata,
pd.DataFrame(index=range(0)))
@@ -255,6 +310,8 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
self.assertFalse(seq.metadata)
self.assertEqual(seq.metadata, {})
+ self.assertEqual(seq.interval_metadata,
+ IntervalMetadata(1))
assert_data_frame_almost_equal(seq.positional_metadata,
pd.DataFrame(index=range(1)))
@@ -277,6 +334,8 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
self.assertFalse(seq.metadata)
self.assertEqual(seq.metadata, {})
+ self.assertEqual(seq.interval_metadata,
+ IntervalMetadata(14))
assert_data_frame_almost_equal(seq.positional_metadata,
pd.DataFrame(index=range(14)))
@@ -293,24 +352,34 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
seq = Sequence('ACGT',
metadata={'id': 'foo', 'description': 'bar baz'},
positional_metadata={'quality': range(4)})
+ seq.interval_metadata.add([(0, 1)], metadata={'gene': 'sagA'})
self.assertEqual(Sequence(seq), seq)
# should be able to override metadata
+ im = IntervalMetadata(4)
+ im.add([(0, 2)], metadata={'gene': 'sagB'})
self.assertEqual(
Sequence(seq, metadata={'id': 'abc', 'description': '123'},
- positional_metadata={'quality': [42] * 4}),
+ positional_metadata={'quality': [42] * 4},
+ interval_metadata=im),
Sequence('ACGT', metadata={'id': 'abc', 'description': '123'},
- positional_metadata={'quality': [42] * 4}))
+ positional_metadata={'quality': [42] * 4},
+ interval_metadata=im))
# subclasses work too
+ im = IntervalMetadata(4)
+ im.add([(0, 2)], metadata={'gene': 'sagB'})
seq = SequenceSubclass('ACGT',
metadata={'id': 'foo',
'description': 'bar baz'},
- positional_metadata={'quality': range(4)})
+ positional_metadata={'quality': range(4)},
+ interval_metadata=im)
+
self.assertEqual(
Sequence(seq),
Sequence('ACGT', metadata={'id': 'foo', 'description': 'bar baz'},
- positional_metadata={'quality': range(4)}))
+ positional_metadata={'quality': range(4)},
+ interval_metadata=im))
def test_init_from_non_descendant_sequence_object(self):
seq = SequenceSubclass('ACGT')
@@ -460,9 +529,13 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
self.assertEqual(Sequence('xYzxxZz').observed_chars,
{'x', 'Y', 'z', 'Z'})
self.assertEqual(Sequence('\t ').observed_chars, {' ', '\t'})
+
+ im = IntervalMetadata(6)
+ im.add([(0, 2)], metadata={'gene': 'sagB'})
self.assertEqual(
Sequence('aabbcc', metadata={'foo': 'bar'},
- positional_metadata={'foo': range(6)}).observed_chars,
+ positional_metadata={'foo': range(6)},
+ interval_metadata=im).observed_chars,
{'a', 'b', 'c'})
with self.assertRaises(AttributeError):
@@ -472,6 +545,11 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
seq_a = Sequence("A")
seq_b = Sequence("B")
+ im = IntervalMetadata(1)
+ im.add([(0, 1)], metadata={'gene': 'sagA'})
+ im2 = IntervalMetadata(1)
+ im.add([(0, 1)], metadata={'gene': 'sagB'})
+
self.assertTrue(seq_a == seq_a)
self.assertTrue(Sequence("a") == Sequence("a"))
self.assertTrue(Sequence("a", metadata={'id': 'b'}) ==
@@ -481,9 +559,11 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
Sequence("a",
metadata={'id': 'b', 'description': 'c'}))
self.assertTrue(Sequence("a", metadata={'id': 'b', 'description': 'c'},
- positional_metadata={'quality': [1]}) ==
+ positional_metadata={'quality': [1]},
+ interval_metadata=im) ==
Sequence("a", metadata={'id': 'b', 'description': 'c'},
- positional_metadata={'quality': [1]}))
+ positional_metadata={'quality': [1]},
+ interval_metadata=im))
self.assertTrue(seq_a != seq_b)
self.assertTrue(SequenceSubclass("a") != Sequence("a"))
@@ -494,10 +574,18 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
metadata={'id': 'c', 'description': 't'}))
self.assertTrue(Sequence("a", positional_metadata={'quality': [1]}) !=
Sequence("a"))
+ self.assertTrue(Sequence("a", interval_metadata=im) !=
+ Sequence("a"))
+
self.assertTrue(Sequence("a", positional_metadata={'quality': [1]}) !=
Sequence("a", positional_metadata={'quality': [2]}))
+ self.assertTrue(Sequence("a", interval_metadata=im) !=
+ Sequence("a", interval_metadata=im2))
+
self.assertTrue(Sequence("c", positional_metadata={'quality': [3]}) !=
Sequence("b", positional_metadata={'quality': [3]}))
+ self.assertTrue(Sequence("c", interval_metadata=im) !=
+ Sequence("b", interval_metadata=im))
self.assertTrue(Sequence("a", metadata={'id': 'b'}) !=
Sequence("c", metadata={'id': 'b'}))
@@ -520,12 +608,16 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
def test_eq_sequences_from_different_sources_compare_equal(self):
# sequences that have the same data but are constructed from different
# types of data should compare equal
+ im = IntervalMetadata(4)
+ im.add([(0, 2)], metadata={'gene': 'sagB'})
seq1 = Sequence('ACGT', metadata={'id': 'foo', 'desc': 'abc'},
- positional_metadata={'quality': (1, 2, 3, 4)})
+ positional_metadata={'quality': (1, 2, 3, 4)},
+ interval_metadata=im)
seq2 = Sequence(np.array([65, 67, 71, 84], dtype=np.uint8),
metadata={'id': 'foo', 'desc': 'abc'},
positional_metadata={'quality': np.array([1, 2, 3,
- 4])})
+ 4])},
+ interval_metadata=im)
self.assertTrue(seq1 == seq2)
def test_eq_type_mismatch(self):
@@ -544,6 +636,21 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
seq2 = Sequence('ACGT')
self.assertFalse(seq1 == seq2)
+ def test_eq_interval_metadata_mismatch(self):
+ im1 = IntervalMetadata(4)
+ im1.add([(0, 3)], metadata={'gene': 'sagA'})
+ im2 = IntervalMetadata(4)
+ im2.add([(0, 2)], metadata={'gene': 'sagA'})
+ # both provided
+ seq1 = Sequence('ACGT', interval_metadata=im1)
+ seq2 = Sequence('ACGT', interval_metadata=im2)
+ self.assertFalse(seq1 == seq2)
+
+ # one provided
+ seq1 = Sequence('ACGT', interval_metadata=im1)
+ seq2 = Sequence('ACGT')
+ self.assertFalse(seq1 == seq2)
+
def test_eq_sequence_mismatch(self):
seq1 = Sequence('ACGT')
seq2 = Sequence('TGCA')
@@ -553,6 +660,14 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
seq = Sequence("Sequence string !1 at 2#3?.,")
self.assertFalse(seq is seq[:])
+ def test_getitem_drops_interval_metadata(self):
+ s = "Sequence string !1 at 2#3?.,"
+ seq = Sequence(s, metadata={'id': 'id', 'description': 'dsc'})
+ seq.interval_metadata.add([(0, 3)], metadata={'gene': 'sagA'})
+
+ eseq = Sequence('Se', metadata={'id': 'id', 'description': 'dsc'})
+ self.assertEqual(seq[:2], eseq)
+
def test_getitem_with_int_has_positional_metadata(self):
s = "Sequence string !1 at 2#3?.,"
length = len(s)
@@ -1091,12 +1206,16 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
metadata={'NM': 'Kestrel Gorlick'},
positional_metadata={'diff':
list('01100001110010001100')})
+ seq.interval_metadata.add([(0, 1)], metadata={'gene': 'sagA'})
+
index = self._make_index('01100001110010001100')
obs = seq.replace(index, '-')
exp = Sequence('G--CGGC---AA-CGC--CA',
metadata={'NM': 'Kestrel Gorlick'},
positional_metadata={'diff':
list('01100001110010001100')})
+ exp.interval_metadata.add([(0, 1)], metadata={'gene': 'sagA'})
+
self.assertEqual(obs, exp)
def test_replace_with_subclass(self):
@@ -1903,12 +2022,8 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
self.assertEqual(list(obs), exp)
def test_copy_without_metadata(self):
- # shallow vs deep copy with sequence only should be equivalent. thus,
- # copy.copy, copy.deepcopy, and Sequence.copy(deep=True|False) should
- # all be equivalent
- for copy_method in (lambda seq: seq.copy(deep=False),
- lambda seq: seq.copy(deep=True),
- copy.copy, copy.deepcopy):
+ # shallow vs deep copy with sequence only should be equivalent
+ for copy_method in copy.copy, copy.deepcopy:
seq = Sequence('ACGT')
seq_copy = copy_method(seq)
@@ -1917,80 +2032,93 @@ class TestSequence(TestSequenceBase, ReallyEqualMixin):
self.assertIsNot(seq_copy._bytes, seq._bytes)
def test_copy_with_metadata_shallow(self):
- # copy.copy and Sequence.copy should behave identically
- for copy_method in lambda seq: seq.copy(), copy.copy:
- seq = Sequence('ACGT', metadata={'foo': [1]},
- positional_metadata={'bar': [[], [], [], []],
- 'baz': [42, 42, 42, 42]})
- seq_copy = copy_method(seq)
+ seq = Sequence('ACGT', metadata={'foo': [1]},
+ positional_metadata={'bar': [[], [], [], []],
+ 'baz': [42, 42, 42, 42]})
+ seq.interval_metadata.add([(0, 3)], metadata={'gene': ['sagA']})
- self.assertEqual(seq_copy, seq)
- self.assertIsNot(seq_copy, seq)
- self.assertIsNot(seq_copy._bytes, seq._bytes)
- self.assertIsNot(seq_copy._metadata, seq._metadata)
- self.assertIsNot(seq_copy._positional_metadata,
- seq._positional_metadata)
- self.assertIsNot(seq_copy._positional_metadata.values,
- seq._positional_metadata.values)
- self.assertIs(seq_copy._metadata['foo'], seq._metadata['foo'])
- self.assertIs(seq_copy._positional_metadata.loc[0, 'bar'],
- seq._positional_metadata.loc[0, 'bar'])
-
- seq_copy.metadata['foo'].append(2)
- seq_copy.metadata['foo2'] = 42
-
- self.assertEqual(seq_copy.metadata, {'foo': [1, 2], 'foo2': 42})
- self.assertEqual(seq.metadata, {'foo': [1, 2]})
-
- seq_copy.positional_metadata.loc[0, 'bar'].append(1)
- seq_copy.positional_metadata.loc[0, 'baz'] = 43
-
- assert_data_frame_almost_equal(
- seq_copy.positional_metadata,
- pd.DataFrame({'bar': [[1], [], [], []],
- 'baz': [43, 42, 42, 42]}))
- assert_data_frame_almost_equal(
- seq.positional_metadata,
- pd.DataFrame({'bar': [[1], [], [], []],
- 'baz': [42, 42, 42, 42]}))
+ seq_copy = copy.copy(seq)
+
+ self.assertEqual(seq_copy, seq)
+ self.assertIsNot(seq_copy, seq)
+ self.assertIsNot(seq_copy._bytes, seq._bytes)
+ self.assertIsNot(seq_copy._metadata, seq._metadata)
+ self.assertIsNot(seq_copy._positional_metadata,
+ seq._positional_metadata)
+ self.assertIsNot(seq_copy._positional_metadata.values,
+ seq._positional_metadata.values)
+ self.assertIs(seq_copy._metadata['foo'], seq._metadata['foo'])
+ self.assertIs(seq_copy._positional_metadata.loc[0, 'bar'],
+ seq._positional_metadata.loc[0, 'bar'])
+ self.assertIsNot(seq_copy.interval_metadata, seq.interval_metadata)
+ self.assertIsNot(seq_copy.interval_metadata._intervals[0],
+ seq.interval_metadata._intervals[0])
+ self.assertIsNot(seq_copy.interval_metadata._intervals[0].metadata,
+ seq.interval_metadata._intervals[0].metadata)
+ self.assertIs(
+ seq_copy.interval_metadata._intervals[0].metadata['gene'],
+ seq.interval_metadata._intervals[0].metadata['gene'])
+ seq_copy.metadata['foo'].append(2)
+ seq_copy.metadata['foo2'] = 42
+
+ self.assertEqual(seq_copy.metadata, {'foo': [1, 2], 'foo2': 42})
+ self.assertEqual(seq.metadata, {'foo': [1, 2]})
+
+ seq_copy.positional_metadata.loc[0, 'bar'].append(1)
+ seq_copy.positional_metadata.loc[0, 'baz'] = 43
+
+ assert_data_frame_almost_equal(
+ seq_copy.positional_metadata,
+ pd.DataFrame({'bar': [[1], [], [], []],
+ 'baz': [43, 42, 42, 42]}))
+ assert_data_frame_almost_equal(
+ seq.positional_metadata,
+ pd.DataFrame({'bar': [[1], [], [], []],
+ 'baz': [42, 42, 42, 42]}))
def test_copy_with_metadata_deep(self):
- # copy.deepcopy and Sequence.copy(deep=True) should behave identically
- for copy_method in lambda seq: seq.copy(deep=True), copy.deepcopy:
- seq = Sequence('ACGT', metadata={'foo': [1]},
- positional_metadata={'bar': [[], [], [], []],
- 'baz': [42, 42, 42, 42]})
- seq_copy = copy_method(seq)
+ seq = Sequence('ACGT', metadata={'foo': [1]},
+ positional_metadata={'bar': [[], [], [], []],
+ 'baz': [42, 42, 42, 42]})
+ seq.interval_metadata.add([(0, 3)], metadata={'gene': ['sagA']})
+ seq_copy = copy.deepcopy(seq)
+
+ self.assertEqual(seq_copy, seq)
+ self.assertIsNot(seq_copy, seq)
+ self.assertIsNot(seq_copy._bytes, seq._bytes)
+ self.assertIsNot(seq_copy._metadata, seq._metadata)
+ self.assertIsNot(seq_copy._positional_metadata,
+ seq._positional_metadata)
+ self.assertIsNot(seq_copy._positional_metadata.values,
+ seq._positional_metadata.values)
+ self.assertIsNot(seq_copy._metadata['foo'], seq._metadata['foo'])
+ self.assertIsNot(seq_copy._positional_metadata.loc[0, 'bar'],
+ seq._positional_metadata.loc[0, 'bar'])
+ self.assertIsNot(seq_copy.interval_metadata, seq.interval_metadata)
+ self.assertIsNot(seq_copy.interval_metadata._intervals[0],
+ seq.interval_metadata._intervals[0])
+ self.assertIsNot(seq_copy.interval_metadata._intervals[0].metadata,
+ seq.interval_metadata._intervals[0].metadata)
+ self.assertIsNot(
+ seq_copy.interval_metadata._intervals[0].metadata['gene'],
+ seq.interval_metadata._intervals[0].metadata['gene'])
+ seq_copy.metadata['foo'].append(2)
+ seq_copy.metadata['foo2'] = 42
+
+ self.assertEqual(seq_copy.metadata, {'foo': [1, 2], 'foo2': 42})
+ self.assertEqual(seq.metadata, {'foo': [1]})
+
+ seq_copy.positional_metadata.loc[0, 'bar'].append(1)
+ seq_copy.positional_metadata.loc[0, 'baz'] = 43
- self.assertEqual(seq_copy, seq)
- self.assertIsNot(seq_copy, seq)
- self.assertIsNot(seq_copy._bytes, seq._bytes)
- self.assertIsNot(seq_copy._metadata, seq._metadata)
- self.assertIsNot(seq_copy._positional_metadata,
- seq._positional_metadata)
- self.assertIsNot(seq_copy._positional_metadata.values,
- seq._positional_metadata.values)
- self.assertIsNot(seq_copy._metadata['foo'], seq._metadata['foo'])
- self.assertIsNot(seq_copy._positional_metadata.loc[0, 'bar'],
- seq._positional_metadata.loc[0, 'bar'])
-
- seq_copy.metadata['foo'].append(2)
- seq_copy.metadata['foo2'] = 42
-
- self.assertEqual(seq_copy.metadata, {'foo': [1, 2], 'foo2': 42})
- self.assertEqual(seq.metadata, {'foo': [1]})
-
- seq_copy.positional_metadata.loc[0, 'bar'].append(1)
- seq_copy.positional_metadata.loc[0, 'baz'] = 43
-
- assert_data_frame_almost_equal(
- seq_copy.positional_metadata,
- pd.DataFrame({'bar': [[1], [], [], []],
- 'baz': [43, 42, 42, 42]}))
- assert_data_frame_almost_equal(
- seq.positional_metadata,
- pd.DataFrame({'bar': [[], [], [], []],
- 'baz': [42, 42, 42, 42]}))
+ assert_data_frame_almost_equal(
+ seq_copy.positional_metadata,
+ pd.DataFrame({'bar': [[1], [], [], []],
+ 'baz': [43, 42, 42, 42]}))
+ assert_data_frame_almost_equal(
+ seq.positional_metadata,
+ pd.DataFrame({'bar': [[], [], [], []],
+ 'baz': [42, 42, 42, 42]}))
def test_copy_preserves_read_only_flag_on_bytes(self):
seq = Sequence('ACGT')
@@ -2335,8 +2463,7 @@ class TestDistance(TestSequenceBase):
# (TestSequence.test_repr) but cannot be relied upon for coverage (the unit
# tests take care of this)
class SequenceReprDoctests:
- r"""
- >>> import pandas as pd
+ r""">>> import pandas as pd
>>> from skbio import Sequence
Empty (minimal) sequence:
@@ -2504,9 +2631,10 @@ class SequenceReprDoctests:
57180 opqrstuvwx yzABCDEFGH IJKLMNOPQR STUVWXYZ!" #$%&'()*+, -./:;<=>?@
57240 [\]^_`{|}~ 0123456789 a space
- Supply horrendous metadata and positional metadata to exercise a variety of
- metadata formatting cases and rules. Sorting should be by type, then by
- value within each type (Python 3 doesn't allow sorting of mixed types):
+ Supply horrendous metadata, positional, and interval metadata to
+ exercise a variety of metadata formatting cases and rules. Sorting
+ should be by type, then by value within each type (Python 3
+ doesn't allow sorting of mixed types):
>>> metadata = {
... # str key, str value
@@ -2550,8 +2678,14 @@ class SequenceReprDoctests:
... ('abc' * 90, [True, False, False, True]),
... # None key
... (None, range(4))])
+ >>> interval_metadata = IntervalMetadata(4)
+ >>> _ = interval_metadata.add([(0, 2), (1, 3)],
+ ... [(False, True), (False, False)],
+ ... {'gene': 'p53'})
+ >>> _ = interval_metadata.add([(1, 4)])
>>> Sequence('ACGT', metadata=metadata,
- ... positional_metadata=positional_metadata)
+ ... positional_metadata=positional_metadata,
+ ... interval_metadata=interval_metadata)
Sequence
-----------------------------------------------------------------------
Metadata:
@@ -2581,10 +2715,13 @@ class SequenceReprDoctests:
42: <dtype: object>
<class 'str'>: <dtype: bool>
None: <dtype: int64>
+ Interval metadata:
+ 2 interval features
Stats:
length: 4
-----------------------------------------------------------------------
0 ACGT
+
"""
pass
diff --git a/skbio/stats/composition.py b/skbio/stats/composition.py
index 326216b..eb6b364 100644
--- a/skbio/stats/composition.py
+++ b/skbio/stats/composition.py
@@ -538,7 +538,10 @@ def ilr(mat, basis=None, check=True):
basis: numpy.ndarray, float, optional
orthonormal basis for Aitchison simplex
- defaults to J.J.Egozcue orthonormal basis
+ defaults to J.J.Egozcue orthonormal basis.
+
+ check: bool
+ Specifies if the basis is orthonormal.
Examples
--------
@@ -548,12 +551,24 @@ def ilr(mat, basis=None, check=True):
>>> ilr(x)
array([-0.7768362 , -0.68339802, 0.11704769])
+ Notes
+ -----
+ If the `basis` parameter is specified, it is expected to be a basis in the
+ Aitchison simplex. If there are `D-1` elements specified in `mat`, then
+ the dimensions of the basis needs be `D-1 x D`, where rows represent
+ basis vectors, and the columns represent proportions.
"""
mat = closure(mat)
if basis is None:
basis = clr_inv(_gram_schmidt_basis(mat.shape[-1]))
- elif check:
- _check_orthogonality(basis)
+ else:
+ if len(basis.shape) != 2:
+ raise ValueError("Basis needs to be a 2D matrix, "
+ "not a %dD matrix." %
+ (len(basis.shape)))
+ if check:
+ _check_orthogonality(basis)
+
return inner(mat, basis)
@@ -575,7 +590,7 @@ def ilr_inv(mat, basis=None, check=True):
where :math:`[e_1,\ldots, e_{D-1}]` is an orthonormal basis in the simplex.
- If an orthornormal basis isn't specified, the J. J. Egozcue orthonormal
+ If an orthonormal basis isn't specified, the J. J. Egozcue orthonormal
basis derived from Gram-Schmidt orthogonalization will be used by
default.
@@ -591,6 +606,10 @@ def ilr_inv(mat, basis=None, check=True):
orthonormal basis for Aitchison simplex
defaults to J.J.Egozcue orthonormal basis
+ check: bool
+ Specifies if the basis is orthonormal.
+
+
Examples
--------
>>> import numpy as np
@@ -599,12 +618,27 @@ def ilr_inv(mat, basis=None, check=True):
>>> ilr_inv(x)
array([ 0.34180297, 0.29672718, 0.22054469, 0.14092516])
+ Notes
+ -----
+ If the `basis` parameter is specified, it is expected to be a basis in the
+ Aitchison simplex. If there are `D-1` elements specified in `mat`, then
+ the dimensions of the basis needs be `D-1 x D`, where rows represent
+ basis vectors, and the columns represent proportions.
"""
if basis is None:
basis = _gram_schmidt_basis(mat.shape[-1] + 1)
- elif check:
- _check_orthogonality(basis)
+ else:
+ if len(basis.shape) != 2:
+ raise ValueError("Basis needs to be a 2D matrix, "
+ "not a %dD matrix." %
+ (len(basis.shape)))
+ if check:
+ _check_orthogonality(basis)
+ # this is necessary, since the clr function
+ # performs np.squeeze()
+ basis = np.atleast_2d(clr(basis))
+
return clr_inv(np.dot(mat, basis))
@@ -1081,6 +1115,7 @@ def _check_orthogonality(basis):
basis: numpy.ndarray
basis in the Aitchison simplex
"""
+ basis = np.atleast_2d(basis)
if not np.allclose(inner(basis, basis), np.identity(len(basis)),
rtol=1e-4, atol=1e-6):
raise ValueError("Aitchison basis is not orthonormal")
diff --git a/skbio/stats/distance/_base.py b/skbio/stats/distance/_base.py
index 7281b26..34a04f9 100644
--- a/skbio/stats/distance/_base.py
+++ b/skbio/stats/distance/_base.py
@@ -6,6 +6,7 @@
# The full license is in the file COPYING.txt, distributed with this software.
# ----------------------------------------------------------------------------
+import itertools
from copy import deepcopy
import matplotlib.pyplot as plt
@@ -109,6 +110,57 @@ class DissimilarityMatrix(SkbioObject):
self._ids = ids
self._id_index = self._index_list(self._ids)
+ @classonlymethod
+ @experimental(as_of="0.5.1")
+ def from_iterable(cls, iterable, metric, key=None, keys=None):
+ """Create DissimilarityMatrix from an iterable given a metric.
+
+ Parameters
+ ----------
+ iterable : iterable
+ Iterable containing objects to compute pairwise dissimilarities on.
+ metric : callable
+ A function that takes two arguments and returns a float
+ representing the dissimilarity between the two arguments.
+ key : callable or metadata key, optional
+ A function that takes one argument and returns a string
+ representing the id of the element in the dissimilarity matrix.
+ Alternatively, a key to a `metadata` property if it exists for
+ each element in the `iterable`. If None, then default ids will be
+ used.
+ keys : iterable, optional
+ An iterable of the same length as `iterable`. Each element will be
+ used as the respective key.
+
+ Returns
+ -------
+ DissimilarityMatrix
+ The `metric` applied to all pairwise elements in the `iterable`.
+
+ Raises
+ ------
+ ValueError
+ If `key` and `keys` are both provided.
+
+ """
+ iterable = list(iterable)
+ if key is not None and keys is not None:
+ raise ValueError("Cannot use both `key` and `keys` at the same"
+ " time.")
+
+ keys_ = None
+ if key is not None:
+ keys_ = [resolve_key(e, key) for e in iterable]
+ elif keys is not None:
+ keys_ = keys
+
+ dm = np.empty((len(iterable),) * 2)
+ for i, a in enumerate(iterable):
+ for j, b in enumerate(iterable):
+ dm[i, j] = metric(a, b)
+
+ return cls(dm, keys_)
+
@property
@experimental(as_of="0.4.0")
def data(self):
@@ -623,9 +675,6 @@ class DissimilarityMatrix(SkbioObject):
if data.dtype != np.double:
raise DissimilarityMatrixError("Data must contain only floating "
"point values.")
- if np.trace(data) != 0:
- raise DissimilarityMatrixError("Data must be hollow (i.e., the "
- "diagonal can only contain zeros).")
duplicates = find_duplicates(ids)
if duplicates:
formatted_duplicates = ', '.join(repr(e) for e in duplicates)
@@ -683,7 +732,8 @@ class DistanceMatrix(DissimilarityMatrix):
@classonlymethod
@experimental(as_of="0.4.1")
- def from_iterable(cls, iterable, metric, key=None, keys=None):
+ def from_iterable(cls, iterable, metric, key=None, keys=None,
+ validate=True):
"""Create DistanceMatrix from all pairs in an iterable given a metric.
Parameters
@@ -702,24 +752,30 @@ class DistanceMatrix(DissimilarityMatrix):
keys : iterable, optional
An iterable of the same length as `iterable`. Each element will be
used as the respective key.
+ validate : boolean, optional
+ If ``True``, all pairwise distances are computed, including upper
+ and lower triangles and the diagonal, and the resulting matrix is
+ validated for symmetry and hollowness. If ``False``, `metric` is
+ assumed to be hollow and symmetric and only the lower triangle
+ (excluding the diagonal) is computed. Pass ``validate=False`` if
+ you are sure `metric` is hollow and symmetric for improved
+ performance.
Returns
-------
DistanceMatrix
- The `metric` applied to all pairwise elements in the `iterable`.
+ The `metric` applied to pairwise elements in the `iterable`.
Raises
------
ValueError
If `key` and `keys` are both provided.
- Notes
- -----
- Symmetry and hollowness are assumed when calculating the distances via
- `metric`. Therefore, distances are only computed for the strictly
- upper/lower triangle.
-
"""
+ if validate:
+ return super(DistanceMatrix, cls).from_iterable(iterable, metric,
+ key, keys)
+
iterable = list(iterable)
if key is not None and keys is not None:
raise ValueError("Cannot use both `key` and `keys` at the same"
@@ -815,6 +871,60 @@ class DistanceMatrix(DissimilarityMatrix):
raise DistanceMatrixError(
"Data must be symmetric and cannot contain NaNs.")
+ if np.trace(data) != 0:
+ raise DistanceMatrixError("Data must be hollow (i.e., the diagonal"
+ " can only contain zeros).")
+
+ @experimental(as_of="0.5.1")
+ def to_series(self):
+ """Create a ``pandas.Series`` from this ``DistanceMatrix``.
+
+ The series will contain distances in condensed form: only distances
+ from one matrix triangle are included, and the diagonal is excluded.
+ The series' index will be a ``pd.MultiIndex`` relating pairs of IDs to
+ distances. The pairs of IDs will be in row-major order with respect to
+ the upper matrix triangle.
+
+ To obtain all distances (i.e. both upper and lower matrix triangles and
+ the diagonal), use ``DistanceMatrix.to_data_frame``. To obtain *only*
+ the distances in condensed form (e.g. for use with SciPy), use
+ ``DistanceMatrix.condensed_form``.
+
+ Returns
+ -------
+ pd.Series
+ ``pd.Series`` with pairs of IDs on the index.
+
+ See Also
+ --------
+ to_data_frame
+ condensed_form
+ scipy.spatial.distance.squareform
+
+ Examples
+ --------
+ >>> from skbio import DistanceMatrix
+ >>> dm = DistanceMatrix([[0, 1, 2, 3],
+ ... [1, 0, 4, 5],
+ ... [2, 4, 0, 6],
+ ... [3, 5, 6, 0]], ids=['a', 'b', 'c', 'd'])
+ >>> dm.to_series()
+ a b 1.0
+ c 2.0
+ d 3.0
+ b c 4.0
+ d 5.0
+ c d 6.0
+ dtype: float64
+
+ """
+ distances = self.condensed_form()
+ # `id_pairs` will not be interpreted as a `pd.MultiIndex` if it is an
+ # iterable returned by `itertools.combinations`.
+ id_pairs = list(itertools.combinations(self.ids, 2))
+ index = pd.Index(id_pairs, tupleize_cols=True)
+ return pd.Series(data=distances, index=index, dtype=float)
+
@experimental(as_of="0.4.0")
def randdm(num_objects, ids=None, constructor=None, random_fn=None):
diff --git a/skbio/stats/distance/tests/test_base.py b/skbio/stats/distance/tests/test_base.py
index f0a156b..f94b4e8 100644
--- a/skbio/stats/distance/tests/test_base.py
+++ b/skbio/stats/distance/tests/test_base.py
@@ -24,6 +24,7 @@ from skbio.stats.distance import (
from skbio.stats.distance._base import (_preprocess_input,
_run_monte_carlo_stats)
from skbio.util import assert_data_frame_almost_equal
+from skbio.util._testing import assert_series_almost_equal
class DissimilarityMatrixTestData(TestCase):
@@ -78,6 +79,11 @@ class DissimilarityMatrixTests(DissimilarityMatrixTestData):
with self.assertRaises(DistanceMatrixError):
DistanceMatrix(self.dm_2x2_asym, ['foo', 'bar'])
+ def test_init_non_hollow_dm(self):
+ data = [[1, 1], [1, 1]]
+ obs = DissimilarityMatrix(data, ['a', 'b'])
+ self.assertTrue(np.array_equal(obs.data, data))
+
def test_init_no_ids(self):
exp = DissimilarityMatrix(self.dm_3x3_data, ('0', '1', '2'))
obs = DissimilarityMatrix(self.dm_3x3_data)
@@ -113,10 +119,122 @@ class DissimilarityMatrixTests(DissimilarityMatrixTestData):
with self.assertRaises(DissimilarityMatrixError):
DissimilarityMatrix(data, [])
- # Non-hollow.
- data = [[0.0, 1.0], [1.0, 0.01]]
+ def test_from_iterable_non_hollow_data(self):
+ iterable = (x for x in range(4))
+
+ exp = DissimilarityMatrix([[1, 1, 1, 1],
+ [1, 1, 1, 1],
+ [1, 1, 1, 1],
+ [1, 1, 1, 1]])
+ res = DissimilarityMatrix.from_iterable(iterable, lambda a, b: 1)
+ self.assertEqual(res, exp)
+
+ def test_from_iterable_asymmetric_data(self):
+ iterable = (x for x in range(4))
+
+ exp = DissimilarityMatrix([[0, 1, 2, 3],
+ [-1, 0, 1, 2],
+ [-2, -1, 0, 1],
+ [-3, -2, -1, 0]])
+ res = DissimilarityMatrix.from_iterable(iterable, lambda a, b: b - a)
+ self.assertEqual(res, exp)
+
+ def test_from_iterable_no_key(self):
+ iterable = (x for x in range(4))
+
+ exp = DissimilarityMatrix([[0, 1, 2, 3],
+ [1, 0, 1, 2],
+ [2, 1, 0, 1],
+ [3, 2, 1, 0]])
+ res = DissimilarityMatrix.from_iterable(iterable,
+ lambda a, b: abs(b - a))
+ self.assertEqual(res, exp)
+
+ def test_from_iterable_with_key(self):
+ iterable = (x for x in range(4))
+
+ exp = DissimilarityMatrix([[0, 1, 2, 3],
+ [1, 0, 1, 2],
+ [2, 1, 0, 1],
+ [3, 2, 1, 0]], ['0', '1', '4', '9'])
+ res = DissimilarityMatrix.from_iterable(iterable,
+ lambda a, b: abs(b - a),
+ key=lambda x: str(x ** 2))
+ self.assertEqual(res, exp)
+
+ def test_from_iterable_empty(self):
with self.assertRaises(DissimilarityMatrixError):
- DissimilarityMatrix(data, ['a', 'b'])
+ DissimilarityMatrix.from_iterable([], lambda x: x)
+
+ def test_from_iterable_single(self):
+ exp = DissimilarityMatrix([[100]])
+ res = DissimilarityMatrix.from_iterable(["boo"], lambda a, b: 100)
+ self.assertEqual(res, exp)
+
+ def test_from_iterable_with_keys(self):
+ iterable = (x for x in range(4))
+
+ exp = DissimilarityMatrix([[0, 1, 2, 3],
+ [1, 0, 1, 2],
+ [2, 1, 0, 1],
+ [3, 2, 1, 0]], ['0', '1', '4', '9'])
+ res = DissimilarityMatrix.from_iterable(iterable,
+ lambda a, b: abs(b - a),
+ keys=iter(['0', '1', '4', '9'])
+ )
+ self.assertEqual(res, exp)
+
+ def test_from_iterable_with_key_and_keys(self):
+ iterable = (x for x in range(4))
+ with self.assertRaises(ValueError):
+ DissimilarityMatrix.from_iterable(iterable,
+ lambda a, b: abs(b - a),
+ key=str,
+ keys=['1', '2', '3', '4'])
+
+ def test_from_iterable_scipy_hamming_metric_with_metadata(self):
+ # test for #1254
+ seqs = [
+ Sequence('ACGT'),
+ Sequence('ACGA', metadata={'id': 'seq1'}),
+ Sequence('AAAA', metadata={'id': 'seq2'}),
+ Sequence('AAAA', positional_metadata={'qual': range(4)})
+ ]
+
+ exp = DissimilarityMatrix([
+ [0, 0.25, 0.75, 0.75],
+ [0.25, 0.0, 0.5, 0.5],
+ [0.75, 0.5, 0.0, 0.0],
+ [0.75, 0.5, 0.0, 0.0]], ['a', 'b', 'c', 'd'])
+
+ dm = DissimilarityMatrix.from_iterable(
+ seqs,
+ metric=scipy.spatial.distance.hamming,
+ keys=['a', 'b', 'c', 'd'])
+
+ self.assertEqual(dm, exp)
+
+ def test_from_iterable_skbio_hamming_metric_with_metadata(self):
+ # test for #1254
+ seqs = [
+ Sequence('ACGT'),
+ Sequence('ACGA', metadata={'id': 'seq1'}),
+ Sequence('AAAA', metadata={'id': 'seq2'}),
+ Sequence('AAAA', positional_metadata={'qual': range(4)})
+ ]
+
+ exp = DissimilarityMatrix([
+ [0, 0.25, 0.75, 0.75],
+ [0.25, 0.0, 0.5, 0.5],
+ [0.75, 0.5, 0.0, 0.0],
+ [0.75, 0.5, 0.0, 0.0]], ['a', 'b', 'c', 'd'])
+
+ dm = DissimilarityMatrix.from_iterable(
+ seqs,
+ metric=skbio.sequence.distance.hamming,
+ keys=['a', 'b', 'c', 'd'])
+
+ self.assertEqual(dm, exp)
def test_data(self):
for dm, exp in zip(self.dms, self.dm_redundant_forms):
@@ -481,6 +599,11 @@ class DistanceMatrixTests(DissimilarityMatrixTestData):
with self.assertRaises(DistanceMatrixError):
DistanceMatrix(data, ['a', 'b'])
+ # Non-hollow
+ data = [[1.0, 2.0], [2.0, 1.0]]
+ with self.assertRaises(DistanceMatrixError):
+ DistanceMatrix(data, ['a', 'b'])
+
# Ensure that the superclass validation is still being performed.
with self.assertRaises(DissimilarityMatrixError):
DistanceMatrix([[1, 2, 3]], ['a'])
@@ -499,6 +622,46 @@ class DistanceMatrixTests(DissimilarityMatrixTestData):
res = DistanceMatrix.from_iterable(iterable, lambda a, b: abs(b - a))
self.assertEqual(res, exp)
+ def test_from_iterable_validate_equal_valid_data(self):
+ validate_true = DistanceMatrix.from_iterable((x for x in range(4)),
+ lambda a, b: abs(b - a),
+ validate=True)
+ validate_false = DistanceMatrix.from_iterable((x for x in range(4)),
+ lambda a, b: abs(b - a),
+ validate=False)
+ self.assertEqual(validate_true, validate_false)
+
+ def test_from_iterable_validate_false(self):
+ iterable = (x for x in range(4))
+
+ exp = DistanceMatrix([[0, 1, 2, 3],
+ [1, 0, 1, 2],
+ [2, 1, 0, 1],
+ [3, 2, 1, 0]])
+ res = DistanceMatrix.from_iterable(iterable, lambda a, b: abs(b - a),
+ validate=False)
+ self.assertEqual(res, exp)
+
+ def test_from_iterable_validate_non_hollow(self):
+ iterable = (x for x in range(4))
+ with self.assertRaises(DistanceMatrixError):
+ DistanceMatrix.from_iterable(iterable, lambda a, b: 1)
+
+ def test_from_iterable_validate_false_non_symmetric(self):
+ exp = DistanceMatrix([[0, 1, 2, 3],
+ [1, 0, 1, 2],
+ [2, 1, 0, 1],
+ [3, 2, 1, 0]])
+ res = DistanceMatrix.from_iterable((x for x in range(4)),
+ lambda a, b: a - b,
+ validate=False)
+ self.assertEqual(res, exp)
+
+ def test_from_iterable_validate_asym(self):
+ iterable = (x for x in range(4))
+ with self.assertRaises(DistanceMatrixError):
+ DistanceMatrix.from_iterable(iterable, lambda a, b: b - a)
+
def test_from_iterable_with_key(self):
iterable = (x for x in range(4))
@@ -516,7 +679,7 @@ class DistanceMatrixTests(DissimilarityMatrixTestData):
def test_from_iterable_single(self):
exp = DistanceMatrix([[0]])
- res = DistanceMatrix.from_iterable(["boo"], lambda _: 100)
+ res = DistanceMatrix.from_iterable(["boo"], lambda a, b: 0)
self.assertEqual(res, exp)
def test_from_iterable_with_keys(self):
@@ -639,6 +802,38 @@ class DistanceMatrixTests(DissimilarityMatrixTestData):
self.assertTrue(self.dm_3x3 == eq_dm)
self.assertTrue(eq_dm == self.dm_3x3)
+ def test_to_series_1x1(self):
+ series = self.dm_1x1.to_series()
+
+ exp = pd.Series([], index=[])
+ assert_series_almost_equal(series, exp)
+
+ def test_to_series_2x2(self):
+ series = self.dm_2x2.to_series()
+
+ exp = pd.Series([0.123], index=pd.Index([('a', 'b')]))
+ assert_series_almost_equal(series, exp)
+
+ def test_to_series_4x4(self):
+ dm = DistanceMatrix([
+ [0.0, 0.2, 0.3, 0.4],
+ [0.2, 0.0, 0.5, 0.6],
+ [0.3, 0.5, 0.0, 0.7],
+ [0.4, 0.6, 0.7, 0.0]], ['a', 'b', 'c', 'd'])
+
+ series = dm.to_series()
+
+ exp = pd.Series([0.2, 0.3, 0.4, 0.5, 0.6, 0.7],
+ index=pd.Index([('a', 'b'), ('a', 'c'), ('a', 'd'),
+ ('b', 'c'), ('b', 'd'), ('c', 'd')]))
+ assert_series_almost_equal(series, exp)
+
+ def test_to_series_default_ids(self):
+ series = DistanceMatrix(self.dm_2x2_data).to_series()
+
+ exp = pd.Series([0.123], index=pd.Index([('0', '1')]))
+ assert_series_almost_equal(series, exp)
+
class RandomDistanceMatrixTests(TestCase):
def test_default_usage(self):
diff --git a/skbio/stats/power.py b/skbio/stats/power.py
index fd81ab8..3f858ce 100644
--- a/skbio/stats/power.py
+++ b/skbio/stats/power.py
@@ -1017,10 +1017,10 @@ def _identify_sample_groups(meta, cat, control_cats, order, strict_match):
m_ids = meta.loc[ids].groupby(cat).groups
# Checks if samples from the cat groups are represented in those
# Samples
- ids_vec = id_vecs = [m_ids[o] for o in order if o in
- m_ids]
+ id_vecs = [m_ids[o] for o in order if o in
+ m_ids]
# If all groups are represented, the index and results are retained
- if len(ids_vec) == len(order):
+ if len(id_vecs) == len(order):
min_vec = np.array([len(v) for v in id_vecs])
loc_vec = np.arange(0, min_vec.min())
meta_pairs[i1] = id_vecs
diff --git a/skbio/stats/tests/test_composition.py b/skbio/stats/tests/test_composition.py
index b0d9b75..863e8ad 100644
--- a/skbio/stats/tests/test_composition.py
+++ b/skbio/stats/tests/test_composition.py
@@ -304,6 +304,32 @@ class CompositionTests(TestCase):
np.array([[2, 2, 6],
[4, 4, 2]]))
+ def test_ilr_basis(self):
+ table = np.array([[1., 10.],
+ [1.14141414, 9.90909091],
+ [1.28282828, 9.81818182],
+ [1.42424242, 9.72727273],
+ [1.56565657, 9.63636364]])
+ basis = np.array([[0.80442968, 0.19557032]])
+ res = ilr(table, basis=basis)
+ exp = np.array([np.log(1/10)*np.sqrt(1/2),
+ np.log(1.14141414 / 9.90909091)*np.sqrt(1/2),
+ np.log(1.28282828 / 9.81818182)*np.sqrt(1/2),
+ np.log(1.42424242 / 9.72727273)*np.sqrt(1/2),
+ np.log(1.56565657 / 9.63636364)*np.sqrt(1/2)])
+
+ npt.assert_allclose(res, exp)
+
+ def test_ilr_basis_one_dimension_error(self):
+ table = np.array([[1., 10.],
+ [1.14141414, 9.90909091],
+ [1.28282828, 9.81818182],
+ [1.42424242, 9.72727273],
+ [1.56565657, 9.63636364]])
+ basis = np.array([0.80442968, 0.19557032])
+ with self.assertRaises(ValueError):
+ ilr(table, basis=basis)
+
def test_ilr_inv(self):
mat = closure(self.cdata7)
npt.assert_array_almost_equal(ilr_inv(ilr(mat)), mat)
@@ -320,6 +346,52 @@ class CompositionTests(TestCase):
np.array([[2, 2, 6],
[4, 4, 2]]))
+ def test_ilr_basis_isomorphism(self):
+ # tests to make sure that the isomorphism holds
+ # with the introduction of the basis.
+ basis = np.array([[0.80442968, 0.19557032]])
+ table = np.array([[np.log(1/10)*np.sqrt(1/2),
+ np.log(1.14141414 / 9.90909091)*np.sqrt(1/2),
+ np.log(1.28282828 / 9.81818182)*np.sqrt(1/2),
+ np.log(1.42424242 / 9.72727273)*np.sqrt(1/2),
+ np.log(1.56565657 / 9.63636364)*np.sqrt(1/2)]]).T
+ res = ilr(ilr_inv(table, basis=basis), basis=basis)
+ npt.assert_allclose(res, table.squeeze())
+
+ table = np.array([[1., 10.],
+ [1.14141414, 9.90909091],
+ [1.28282828, 9.81818182],
+ [1.42424242, 9.72727273],
+ [1.56565657, 9.63636364]])
+
+ res = ilr_inv(np.atleast_2d(ilr(table, basis=basis)).T, basis=basis)
+ npt.assert_allclose(res, closure(table.squeeze()))
+
+ def test_ilr_inv_basis(self):
+ exp = closure(np.array([[1., 10.],
+ [1.14141414, 9.90909091],
+ [1.28282828, 9.81818182],
+ [1.42424242, 9.72727273],
+ [1.56565657, 9.63636364]]))
+ basis = np.array([[0.80442968, 0.19557032]])
+ table = np.array([[np.log(1/10)*np.sqrt(1/2),
+ np.log(1.14141414 / 9.90909091)*np.sqrt(1/2),
+ np.log(1.28282828 / 9.81818182)*np.sqrt(1/2),
+ np.log(1.42424242 / 9.72727273)*np.sqrt(1/2),
+ np.log(1.56565657 / 9.63636364)*np.sqrt(1/2)]]).T
+ res = ilr_inv(table, basis=basis)
+ npt.assert_allclose(res, exp)
+
+ def test_ilr_inv_basis_one_dimension_error(self):
+ basis = clr(np.array([[0.80442968, 0.19557032]]))
+ table = np.array([[np.log(1/10)*np.sqrt(1/2),
+ np.log(1.14141414 / 9.90909091)*np.sqrt(1/2),
+ np.log(1.28282828 / 9.81818182)*np.sqrt(1/2),
+ np.log(1.42424242 / 9.72727273)*np.sqrt(1/2),
+ np.log(1.56565657 / 9.63636364)*np.sqrt(1/2)]]).T
+ with self.assertRaises(ValueError):
+ ilr_inv(table, basis=basis)
+
class AncomTests(TestCase):
def setUp(self):
diff --git a/skbio/stats/tests/test_power.py b/skbio/stats/tests/test_power.py
index fd1428b..837cd44 100644
--- a/skbio/stats/tests/test_power.py
+++ b/skbio/stats/tests/test_power.py
@@ -477,9 +477,12 @@ class PowerAnalysisTest(TestCase):
def test__identify_sample_groups_not_strict(self):
# Defines the know values
- known_pairs = {0: [['PP'], ['CD', 'NR']],
- 1: [['MM', 'WM'], ['MH']],
- 2: [['GW'], ['CB']]}
+ known_pairs = {1: [np.array(['PP'], dtype=object),
+ np.array(['CD', 'NR'], dtype=object)],
+ 0: [np.array(['MM', 'WM'], dtype=object),
+ np.array(['MH'], dtype=object)],
+ 2: [np.array(['GW'], dtype=object),
+ np.array(['CB'], dtype=object)]}
known_index = np.array([0, 1, 2])
test_pairs, test_index = _identify_sample_groups(self.meta,
'INT',
@@ -487,8 +490,10 @@ class PowerAnalysisTest(TestCase):
order=['N', 'Y'],
strict_match=False)
self.assertEqual(known_pairs.keys(), test_pairs.keys())
- self.assertEqual(sorted(known_pairs.values()),
- sorted(test_pairs.values()))
+
+ for k in known_pairs:
+ for i in range(2):
+ npt.assert_array_equal(known_pairs[k][i], test_pairs[k][i])
npt.assert_array_equal(known_index, test_index)
def test__draw_paired_samples(self):
diff --git a/skbio/tree/_tree.py b/skbio/tree/_tree.py
index a758a08..234ab92 100644
--- a/skbio/tree/_tree.py
+++ b/skbio/tree/_tree.py
@@ -383,18 +383,6 @@ class TreeNode(SkbioObject):
if len(node.children) == 1:
nodes_to_remove.append(node)
- # if a single descendent from the root, the root adopts the childs
- # properties. we can't "delete" the root as that would be deleting
- # self.
- if len(self.children) == 1:
- node_to_copy = self.children[0]
- efc = self._exclude_from_copy
- for key in node_to_copy.__dict__:
- if key not in efc:
- self.__dict__[key] = deepcopy(node_to_copy.__dict__[key])
- self.remove(node_to_copy)
- self.children.extend(node_to_copy.children)
-
# clean up the single children nodes
for node in nodes_to_remove:
child = node.children[0]
@@ -404,9 +392,24 @@ class TreeNode(SkbioObject):
else:
child.length += node.length
+ if node.parent is None:
+ continue
+
node.parent.append(child)
node.parent.remove(node)
+ # if a single descendent from the root, the root adopts the childs
+ # properties. we can't "delete" the root as that would be deleting
+ # self.
+ if len(self.children) == 1:
+ node_to_copy = self.children[0]
+ efc = self._exclude_from_copy
+ for key in node_to_copy.__dict__:
+ if key not in efc:
+ self.__dict__[key] = deepcopy(node_to_copy.__dict__[key])
+ self.remove(node_to_copy)
+ self.extend(node_to_copy.children)
+
@experimental(as_of="0.4.0")
def shear(self, names):
"""Lop off tips until the tree just has the desired tip names.
@@ -450,10 +453,19 @@ class TreeNode(SkbioObject):
if not ids.issubset(all_tips):
raise ValueError("ids are not a subset of the tree.")
- while len(list(tcopy.tips())) != len(ids):
- for n in list(tcopy.tips()):
- if n.name not in ids:
- n.parent.remove(n)
+ marked = set()
+ for tip in tcopy.tips():
+ if tip.name in ids:
+ marked.add(tip)
+ for anc in tip.ancestors():
+ if anc in marked:
+ break
+ else:
+ marked.add(anc)
+
+ for node in list(tcopy.traverse()):
+ if node not in marked:
+ node.parent.remove(node)
tcopy.prune()
diff --git a/skbio/tree/tests/test_tree.py b/skbio/tree/tests/test_tree.py
index f2f65dc..968d35d 100644
--- a/skbio/tree/tests/test_tree.py
+++ b/skbio/tree/tests/test_tree.py
@@ -52,6 +52,17 @@ class TreeTests(TestCase):
self.complex_tree = TreeNode.read(io.StringIO(
"(((a,b)int1,(x,y,(w,z)int2,(c,d)int3)int4),(e,f)int5);"))
+ def test_bug_issue_1416(self):
+ tree = TreeNode.read(['(((a,b,f,g),c),d);'])
+ new_tree = tree.shear(['a', 'b', 'c', 'f'])
+
+ exp = {'a', 'b', 'c', 'f'}
+ obs = {n.name for n in new_tree.tips()}
+
+ self.assertEqual(obs, exp)
+ self.assertEqual(id(new_tree), id(new_tree.children[0].parent))
+ self.assertEqual(id(new_tree), id(new_tree.children[1].parent))
+
def test_observed_node_counts(self):
"""returns observed nodes counts given vector of otu observation counts
"""
@@ -303,6 +314,21 @@ class TreeTests(TestCase):
self.assertEqual(len(n.children), 2)
self.assertNotIn(n, self.simple_t.children)
+ def test_shear_prune_parent_dropped(self):
+ bugtree = "((a,b),((c,d),(e,f)));"
+ to_keep = ['c', 'd']
+ exp = "(c,d);\n"
+ obs = str(TreeNode.read(io.StringIO(bugtree)).shear(to_keep))
+ self.assertEqual(obs, exp)
+
+ def test_prune_nested_single_descendent(self):
+ bugtree = "(((a,b)));"
+ exp = "(a,b);\n"
+ t = TreeNode.read(io.StringIO(bugtree))
+ t.prune()
+ obs = str(t)
+ self.assertEqual(obs, exp)
+
def test_prune_root_single_desc(self):
t = TreeNode.read(["((a,b)c)extra;"])
exp = "(a,b)c;\n"
diff --git a/skbio/util/__init__.py b/skbio/util/__init__.py
index 67c5c70..5a72a63 100644
--- a/skbio/util/__init__.py
+++ b/skbio/util/__init__.py
@@ -30,10 +30,7 @@ Generally useful functionality that doesn't fit in more specific locations.
:toctree: generated/
cardinal_to_ordinal
- create_dir
find_duplicates
- is_casava_v180_or_later
- remove_files
safe_md5
classproperty
@@ -57,16 +54,14 @@ Warnings
# ----------------------------------------------------------------------------
from ._warning import EfficiencyWarning, RepresentationWarning, SkbioWarning
-from ._misc import (cardinal_to_ordinal, create_dir, find_duplicates,
- is_casava_v180_or_later, remove_files, safe_md5)
+from ._misc import cardinal_to_ordinal, find_duplicates, safe_md5
from ._testing import (get_data_path, TestRunner,
assert_ordination_results_equal,
assert_data_frame_almost_equal)
from ._decorator import classproperty
__all__ = ['SkbioWarning', 'EfficiencyWarning', 'RepresentationWarning',
- 'cardinal_to_ordinal', 'create_dir', 'find_duplicates',
- 'is_casava_v180_or_later', 'remove_files', 'safe_md5',
+ 'cardinal_to_ordinal', 'find_duplicates', 'safe_md5',
'get_data_path', 'TestRunner', 'assert_ordination_results_equal',
'assert_data_frame_almost_equal', 'classproperty']
diff --git a/skbio/util/_decorator.py b/skbio/util/_decorator.py
index 8d08274..d5728fb 100644
--- a/skbio/util/_decorator.py
+++ b/skbio/util/_decorator.py
@@ -341,30 +341,9 @@ class classproperty(property):
class classonlymethod(classmethod):
"""Just like `classmethod`, but it can't be called on an instance."""
- def __init__(self, function):
- super(classonlymethod, self).__init__(function)
-
def __get__(self, obj, cls=None):
if obj is not None:
raise TypeError("Class-only method called on an instance. Use"
" '%s.%s' instead."
% (cls.__name__, self.__func__.__name__))
-
- evaldict = self.__func__.__globals__.copy()
- evaldict['_call_'] = self.__func__
- evaldict['_cls_'] = cls
- fun = FunctionMakerDropFirstArg.create(
- self.__func__, "return _call_(_cls_, %(shortsignature)s)",
- evaldict, __wrapped__=self.__func__)
- fun.__func__ = self.__func__ # Doctests need the orginal function
- return fun
-
-
-class FunctionMakerDropFirstArg(decorator.FunctionMaker):
- def __init__(self, *args, **kwargs):
- super(FunctionMakerDropFirstArg, self).__init__(*args, **kwargs)
- self.signature = self._remove_first_arg(self.signature)
- self.shortsignature = self._remove_first_arg(self.shortsignature)
-
- def _remove_first_arg(self, string):
- return ",".join(string.split(',')[1:])[1:]
+ return super().__get__(obj, cls)
diff --git a/skbio/util/_misc.py b/skbio/util/_misc.py
index 3fae35a..fbb5646 100644
--- a/skbio/util/_misc.py
+++ b/skbio/util/_misc.py
@@ -7,12 +7,10 @@
# ----------------------------------------------------------------------------
import hashlib
-from os import remove, makedirs
-from os.path import exists, isdir
-from functools import partial
-from types import FunctionType
import inspect
-from ._decorator import experimental, deprecated
+from types import FunctionType
+
+from ._decorator import experimental
def resolve_key(obj, key):
@@ -133,40 +131,6 @@ def cardinal_to_ordinal(n):
return "%d%s" % (n, "tsnrhtdd"[(n//10 % 10 != 1)*(n % 10 < 4)*n % 10::4])
- at deprecated(as_of='0.5.0', until='0.5.1',
- reason='This functionality will be moved to the '
- 'fastq sniffer, where it will be more useful as it will '
- 'determine the variant of a fastq file.')
-def is_casava_v180_or_later(header_line):
- """Check if the header looks like it is Illumina software post-casava v1.8
-
- Parameters
- ----------
- header_line : bytes
- A header line
-
- Returns
- -------
- bool
- ``True`` for if casava v1.8+, otherwise ``False``
-
- Examples
- --------
- >>> from skbio.util import is_casava_v180_or_later
- >>> is_casava_v180_or_later(b'@foo')
- False
- >>> id_ = b'@M00176:17:000000000-A0CNA:1:1:15487:1773 1:N:0:0'
- >>> is_casava_v180_or_later(id_)
- True
-
- """
- if not header_line.startswith(b'@'):
- raise ValueError("Non-header line passed in.")
- fields = header_line.split(b':')
-
- return len(fields) == 10 and fields[7] in b'YN'
-
-
@experimental(as_of="0.4.0")
def safe_md5(open_file, block_size=2 ** 20):
"""Computes an md5 sum without loading the file into memory
@@ -209,122 +173,6 @@ def safe_md5(open_file, block_size=2 ** 20):
return md5
- at deprecated(as_of="0.5.0", until="0.5.1",
- reason="Deprecated in favor of solutions present in Python "
- "standard library.")
-def remove_files(list_of_filepaths, error_on_missing=True):
- """Remove list of filepaths, optionally raising an error if any are missing
-
- Parameters
- ----------
- list_of_filepaths : list of strings
- list with filepaths to remove
- error_on_missing : bool, optional
- whether or not the function should raise an ``OSError`` if a file is
- not found
-
- Raises
- ------
- OSError
- If a filepath in the list does not exist
-
- Examples
- --------
- >>> from tempfile import NamedTemporaryFile
- >>> from os.path import exists
- >>> from skbio.util import remove_files
- >>> h = NamedTemporaryFile(delete=False)
- >>> exists(h.name) # it exists
- True
- >>> remove_files([h.name])
- >>> exists(h.name) # and now it's gone
- False
-
- """
- missing = []
- for fp in list_of_filepaths:
- try:
- remove(fp)
- except OSError:
- missing.append(fp)
-
- if error_on_missing and missing:
- raise OSError("Some filepaths were not accessible: %s" %
- '\t'.join(missing))
-
-
- at deprecated(as_of="0.5.0", until="0.5.1",
- reason="Deprecated in favor of solutions present in Python "
- "standard library.")
-def create_dir(dir_name, fail_on_exist=False, handle_errors_externally=False):
- """Create a directory safely and fail meaningfully
-
- Parameters
- ----------
- dir_name: string
- name of directory to create
-
- fail_on_exist: bool, optional
- if true raise an error if ``dir_name`` already exists
-
- handle_errors_externally: bool, optional
- if True do not raise Errors, but return failure codes. This allows to
- handle errors locally and e.g. hint the user at a --force_overwrite
- options.
-
- Returns
- -------
- return_value : int
- These values are only returned if no error is raised:
-
- - ``0``: directory was safely created
- - ``1``: directory already existed
- - ``2``: a file with the same name exists
- - ``3``: any other unspecified ``OSError``
-
- Notes
- -----
- Depending of how thorough we want to be we could add tests, e.g. for
- testing actual write permission in an existing dir.
-
- Examples
- --------
- >>> from skbio.util import create_dir
- >>> from os.path import exists, join
- >>> from tempfile import gettempdir
- >>> from os import rmdir
- >>> new_dir = join(gettempdir(), 'scikitbio')
- >>> create_dir(new_dir)
- 0
- >>> exists(new_dir)
- True
- >>> rmdir(new_dir)
-
- """
- error_code_lookup = _get_create_dir_error_codes()
- # pre-instanciate function with
- ror = partial(_handle_error_codes, dir_name, handle_errors_externally)
-
- if exists(dir_name):
- if isdir(dir_name):
- # dir is there
- if fail_on_exist:
- return ror(error_code_lookup['DIR_EXISTS'])
- else:
- return error_code_lookup['DIR_EXISTS']
- else:
- # must be file with same name
- return ror(error_code_lookup['FILE_EXISTS'])
- else:
- # no dir there, try making it
- try:
- makedirs(dir_name)
- except OSError:
- return ror(error_code_lookup['OTHER_OS_ERROR'])
-
- return error_code_lookup['NO_ERROR']
-
-
@experimental(as_of="0.4.0")
def find_duplicates(iterable):
"""Find duplicate elements in an iterable.
@@ -351,41 +199,3 @@ def find_duplicates(iterable):
else:
seen.add(e)
return repeated
-
-
-def _get_create_dir_error_codes():
- return {'NO_ERROR': 0,
- 'DIR_EXISTS': 1,
- 'FILE_EXISTS': 2,
- 'OTHER_OS_ERROR': 3}
-
-
-def _handle_error_codes(dir_name, suppress_errors=False,
- error_code=None):
- """Wrapper function for error_handling.
-
- dir_name: name of directory that raised the error
- suppress_errors: if True raise Errors, otherwise return error_code
- error_code: the code for the error
-
- """
- error_code_lookup = _get_create_dir_error_codes()
-
- if error_code is None:
- error_code = error_code_lookup['NO_ERROR']
-
- error_strings = \
- {error_code_lookup['DIR_EXISTS']:
- "Directory already exists: %s" % dir_name,
- error_code_lookup['FILE_EXISTS']:
- "File with same name exists: %s" % dir_name,
- error_code_lookup['OTHER_OS_ERROR']:
- "Could not create output directory: %s. " % dir_name +
- "Check the permissions."}
-
- if error_code == error_code_lookup['NO_ERROR']:
- return error_code_lookup['NO_ERROR']
- if suppress_errors:
- return error_code
- else:
- raise OSError(error_strings[error_code])
diff --git a/skbio/util/tests/test_misc.py b/skbio/util/tests/test_misc.py
index ae4b236..7ff8da2 100644
--- a/skbio/util/tests/test_misc.py
+++ b/skbio/util/tests/test_misc.py
@@ -8,15 +8,9 @@
import io
import unittest
-from tempfile import NamedTemporaryFile, mkdtemp
-from os.path import exists, join
-from shutil import rmtree
-from uuid import uuid4
-from skbio.util import (cardinal_to_ordinal, safe_md5, remove_files,
- create_dir, find_duplicates, is_casava_v180_or_later)
-from skbio.util._misc import (
- _handle_error_codes, MiniRegistry, chunk_str, resolve_key)
+from skbio.util import cardinal_to_ordinal, safe_md5, find_duplicates
+from skbio.util._misc import MiniRegistry, chunk_str, resolve_key
class TestMiniRegistry(unittest.TestCase):
@@ -181,22 +175,7 @@ class ChunkStrTests(unittest.TestCase):
chunk_str('abcdef', -42, ' ')
-class MiscTests(unittest.TestCase):
- def setUp(self):
- self.dirs_to_remove = []
-
- def tearDown(self):
- for element in self.dirs_to_remove:
- rmtree(element)
-
- def test_is_casava_v180_or_later(self):
- self.assertFalse(is_casava_v180_or_later(b'@foo'))
- id_ = b'@M00176:17:000000000-A0CNA:1:1:15487:1773 1:N:0:0'
- self.assertTrue(is_casava_v180_or_later(id_))
-
- with self.assertRaises(ValueError):
- is_casava_v180_or_later(b'foo')
-
+class SafeMD5Tests(unittest.TestCase):
def test_safe_md5(self):
exp = 'ab07acbb1e496801937adfa772424bf7'
@@ -206,61 +185,6 @@ class MiscTests(unittest.TestCase):
fd.close()
- def test_remove_files(self):
- # create list of temp file paths
- test_fds = [NamedTemporaryFile(delete=False) for i in range(5)]
- test_filepaths = [element.name for element in test_fds]
-
- # should work just fine
- remove_files(test_filepaths)
-
- # check that an error is raised on trying to remove the files...
- self.assertRaises(OSError, remove_files, test_filepaths)
-
- # touch one of the filepaths so it exists
- extra_file = NamedTemporaryFile(delete=False).name
- test_filepaths.append(extra_file)
-
- # no error is raised on trying to remove the files
- # (although 5 don't exist)...
- remove_files(test_filepaths, error_on_missing=False)
- # ... and the existing file was removed
- self.assertFalse(exists(extra_file))
-
- # try to remove them with remove_files and verify that an IOError is
- # raises
- self.assertRaises(OSError, remove_files, test_filepaths)
-
- # now get no error when error_on_missing=False
- remove_files(test_filepaths, error_on_missing=False)
-
- def test_create_dir(self):
- # create a directory
- tmp_dir_path = mkdtemp()
-
- # create a random temporary directory name
- tmp_dir_path2 = join(mkdtemp(), str(uuid4()))
- tmp_dir_path3 = join(mkdtemp(), str(uuid4()))
-
- self.dirs_to_remove += [tmp_dir_path, tmp_dir_path2, tmp_dir_path3]
-
- # create on existing dir raises OSError if fail_on_exist=True
- self.assertRaises(OSError, create_dir, tmp_dir_path,
- fail_on_exist=True)
- self.assertEqual(create_dir(tmp_dir_path, fail_on_exist=True,
- handle_errors_externally=True), 1)
-
- # return should be 1 if dir exist and fail_on_exist=False
- self.assertEqual(create_dir(tmp_dir_path, fail_on_exist=False), 1)
-
- # if dir not there make it and return always 0
- self.assertEqual(create_dir(tmp_dir_path2), 0)
- self.assertEqual(create_dir(tmp_dir_path3, fail_on_exist=True), 0)
-
- def test_handle_error_codes_no_error(self):
- obs = _handle_error_codes('/foo/bar/baz')
- self.assertEqual(obs, 0)
-
class CardinalToOrdinalTests(unittest.TestCase):
def test_valid_range(self):
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/python-skbio.git
More information about the debian-med-commit
mailing list