float nonPositionalMaxScore;
+ private SequenceFeature[] temp = new SequenceFeature[3];
+
+ private boolean isTainted;
+
/**
* Constructor
*/
features = new IntervalStore<>();
}
features.add(feature);
+ isTainted = true;
}
/**
positionalMaxScore = Float.NaN;
nonPositionalMinScore = Float.NaN;
nonPositionalMaxScore = Float.NaN;
-
+ isTainted = true;
/*
* scan non-positional features for groups and scores
*/
}
/**
- * Adds the shift value to the start and end of all positional features.
- * Returns true if at least one feature was updated, else false.
+ * Adds the shift amount to the start and end of all positional features whose
+ * start position is at or after fromPosition. Returns true if at least one
+ * feature was shifted, else false.
*
- * @param shift
+ * @param fromPosition
+ * @param shiftBy
* @return
*/
- public synchronized boolean shiftFeatures(int shift)
+ public synchronized boolean shiftFeatures(int fromPosition, int shiftBy)
{
/*
* Because begin and end are final fields (to ensure the data store's
boolean modified = false;
for (SequenceFeature sf : getPositionalFeatures())
{
- modified = true;
- int newBegin = sf.getBegin() + shift;
- int newEnd = sf.getEnd() + shift;
-
- /*
- * sanity check: don't shift left of the first residue
- */
- if (newEnd > 0)
+ if (sf.getBegin() >= fromPosition)
{
- newBegin = Math.max(1, newBegin);
- SequenceFeature sf2 = new SequenceFeature(sf, newBegin, newEnd,
- sf.getFeatureGroup(), sf.getScore());
- addFeature(sf2);
+ modified = true;
+ int newBegin = sf.getBegin() + shiftBy;
+ int newEnd = sf.getEnd() + shiftBy;
+
+ /*
+ * sanity check: don't shift left of the first residue
+ */
+ if (newEnd > 0)
+ {
+ newBegin = Math.max(1, newBegin);
+ SequenceFeature sf2 = new SequenceFeature(sf, newBegin, newEnd,
+ sf.getFeatureGroup(), sf.getScore());
+ addFeature(sf2);
+ }
+ delete(sf);
}
- delete(sf);
}
return modified;
}
+
+ /**
+ * Find all features containing this position.
+ * Uses isTainted field to know when to reconstruct its temporary array.
+ *
+ * @param pos
+ * @return list of SequenceFeatures
+ * @author Bob Hanson 2019.07.30
+ */
+ public void findOverlappingFeatures(int pos, List<SequenceFeature> result)
+ {
+
+ if (contactFeatureStarts != null)
+ {
+ findContacts(contactFeatureStarts, pos, result, true);
+ findContacts(contactFeatureEnds, pos, result, false);
+ }
+ if (features != null)
+ {
+ int n = features.size();
+ if (isTainted)
+ {
+ isTainted = false;
+ if (temp.length < n)
+ {
+ temp = new SequenceFeature[n << 1];
+ }
+ features.toArray(temp);
+ }
+ findOverlaps(temp, n, pos, result);
+ }
+ }
+
+ /**
+ * Binary search for contact start or end at a given (Overview) position.
+ *
+ * @param l
+ * @param pos
+ * @param result
+ * @param isStart
+ *
+ * @author Bob Hanson 2019.07.30
+ */
+ private static void findContacts(List<SequenceFeature> l, int pos,
+ List<SequenceFeature> result, boolean isStart)
+ {
+ int low = 0;
+ int high = l.size() - 1;
+ while (low <= high)
+ {
+ int mid = (low + high) >>> 1;
+ SequenceFeature f = l.get(mid);
+ switch (Long.signum((isStart ? f.begin : f.end) - pos))
+ {
+ case -1:
+ low = mid + 1;
+ continue;
+ case 1:
+ high = mid - 1;
+ continue;
+ case 0:
+ int m = mid;
+ result.add(f);
+ // could be "5" in 12345556788 ?
+ while (++mid <= high && (f = l.get(mid)) != null
+ && (isStart ? f.begin : f.end) == pos)
+ {
+ result.add(f);
+ }
+ while (--m >= low && (f = l.get(m)) != null
+ && (isStart ? f.begin : f.end) == pos)
+ {
+ result.add(f);
+ }
+ return;
+ }
+ }
+ }
+
+ /**
+ * Brute force point-interval overlap test
+ *
+ * @param features
+ * @param n
+ * @param pos
+ * @param result
+ */
+ private static void findOverlaps(SequenceFeature[] features, int n,
+ int pos,
+ List<SequenceFeature> result)
+ {
+ // BH I know, brute force. We need a single-position overlap
+ // method for IntervalStore, I think.
+ for (int i = n; --i >= 0;)
+ {
+ SequenceFeature f = features[i];
+ if (f.begin <= pos && f.end >= pos)
+ {
+ result.add(f);
+ }
+ }
+ }
+
}