text: __all__ = ["BaseRegionsMixin",
             "ExtraBaseRegionsMixin",
             "ADAPTER_REGION",
             "INSERT_REGION",
             "HQ_REGION"]
  
  # Region types
  ADAPTER_REGION = 0
  INSERT_REGION = 1
  HQ_REGION = 2
  
  
  # Interval arithmetic
  def intersectRanges(r1, r2):
      b1, e1 = r1
      b2, e2 = r2
      b, e = max(b1, b2), min(e1, e2)
      return (b, e) if (b < e) else None
  
  
  def removeNones(lst):
      return [x for x in lst if x != None]
  
  
  class BaseRegionsMixin:
      """
      Mixin class for "ZMW" client classes providing access to base
      regions and reads sliced to those regions.
  
      Requires the client subclass to provide 'regionsTable' and 'read'
      (subrange slicing) methods with the correct semantics.
  
      The "...Region[s]" calls here return one or more intervals ((int, int));
  
      *All accessors in this mixin class implicitly clip regions to the
       HQ region.*
      """
      @property
      def hqRegion(self):
          """
          Return the HQ region interval.
  
          The HQ region is an interval of basecalls where the basecaller has
          inferred that a single sequencing reaction is taking place.
          Secondary analysis should only use subreads within the HQ
          region.  Methods in this class, with the exception of the
          "NoQC" methods, return data appropriately clipped/filtered to
          the HQ region.
          """
          rt = self.regionTable
          hqRows = rt[rt.regionType == HQ_REGION]
          assert len(hqRows) == 1
          hqRow = hqRows[0]
          return hqRow.regionStart, hqRow.regionEnd
  
      def _unclippedInsertRegions(self):
          return [(region.regionStart, region.regionEnd)
                  for region in self.regionTable
                  if region.regionType == INSERT_REGION]
  
      @property
      def insertRegions(self):
          """
          Get insert regions as intervals, clipped to the HQ region
          """
          hqRegion = self.hqRegion
          return removeNones([intersectRanges(hqRegion, region)
                              for region in self._unclippedInsertRegions()])
  
      @property
      def subreads(self):
          """
          Get the subreads as a list of ZmwRead objects.  Restricts focus,
          and clips to, the HQ region.  This method can be used by
          production code.
          """
          return [self.read(readStart, readEnd)
                  for (readStart, readEnd) in self.insertRegions]
  
      def _unclippedAdapterRegions(self):
          return [(region.regionStart, region.regionEnd)
                  for region in self.regionTable
                  if region.regionType == ADAPTER_REGION]
  
      @property
      def adapterRegions(self):
          """
          Get adapter regions as intervals, performing clipping to the HQ region
          """
          hqRegion = self.hqRegion
          return removeNones([intersectRanges(hqRegion, region)
                              for region in self._unclippedAdapterRegions()])
  
      @property
      def adapters(self):
          """
          Get the adapter hits as a list of ZmwRead objects.  Restricts
          focus, and clips to, the HQ region.  This method can be used
          by production code.
          """
          return [self.read(readStart, readEnd)
                  for (readStart, readEnd) in self.adapterRegions]
  
  
  class ExtraBaseRegionsMixin(BaseRegionsMixin):
      """
      Mixin class for a "ZMW" class providing the basic region
      accessors, and additional region accessors that do not clip to the
      HQ region.
  
      These accessors only make sense for bas.h5 ZMW objects, where we
      have access to region information extending beyond the HQ region.
      In the BAM world, "regions" are all inherently clipped to HQ by
      design of PPA.
  
      Requires 'readNoQC' accessor
      """
      @property
      def adapterRegionsNoQC(self):
          """
          Get adapter regions as intervals, without clipping to the HQ
          region.  Don't use this unless you know what you're doing.
          """
          return self._unclippedAdapterRegions()
  
      @property
      def adaptersNoQC(self):
          """
          Get the adapters, including data beyond the bounds of the HQ
          region.
  
          .. warning::
  
              It is not recommended that production code use this method
              as we make no guarantees about what happens outside of the
              HQ region.
          """
          return [self.read(readStart, readEnd)
                  for (readStart, readEnd) in self.adapterRegionsNoQC]
  
      @property
      def insertRegionsNoQC(self):
          """
          Get insert regions as intervals, without clipping to the HQ
          region.  Don't use this unless you know what you're doing.
          """
          return self._unclippedInsertRegions()
  
      @property
      def subreadsNoQC(self):
          """
          Get the subreads, including data beyond the bounds of the HQ region.
  
          .. warning::
  
              It is not recommended that production code use this method
              as we make no guarantees about what happens outside of the
              HQ region.
          """
          return [self.read(readStart, readEnd)
                  for (readStart, readEnd) in self.insertRegionsNoQC]
  