1 /* $Id: handle_range.cpp 282685 2011-05-13 18:36:36Z vasilche $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Aleksey Grichenko, Eugene Vasilchenko
27 *
28 * File Description:
29 * CHandleRange:
30 * Internal class to be used instead of CSeq_loc
31 * for better performance.
32 *
33 */
34
35 #include <ncbi_pch.hpp>
36 #include <objmgr/impl/handle_range.hpp>
37
38 #include <algorithm>
39
40 BEGIN_NCBI_SCOPE
BEGIN_SCOPE(objects)41 BEGIN_SCOPE(objects)
42
43 ////////////////////////////////////////////////////////////////////
44 //
45 //
46 //
47
48
49 CHandleRange::CHandleRange(void)
50 : m_TotalRanges_plus(TRange::GetEmpty()),
51 m_TotalRanges_minus(TRange::GetEmpty()),
52 m_IsCircular(false),
53 m_IsSingleStrand(true),
54 m_MoreBefore(false),
55 m_MoreAfter(false)
56 {
57 }
58
59
CHandleRange(const CHandleRange & src,const TOpenRange & filter)60 CHandleRange::CHandleRange(const CHandleRange& src, const TOpenRange& filter)
61 : m_TotalRanges_plus(TRange::GetEmpty()),
62 m_TotalRanges_minus(TRange::GetEmpty()),
63 m_IsCircular(false),
64 m_IsSingleStrand(true),
65 m_MoreBefore(false),
66 m_MoreAfter(false)
67 {
68 ITERATE ( CHandleRange, it, src ) {
69 if ( it->first.IntersectingWith(filter) ) {
70 AddRange(it->first & filter, it->second);
71 }
72 }
73 }
74
75
~CHandleRange(void)76 CHandleRange::~CHandleRange(void)
77 {
78 }
79
80
GetStrandsFlag(void) const81 CHandleRange::TTotalRangeFlags CHandleRange::GetStrandsFlag(void) const
82 {
83 TTotalRangeFlags ret = 0;
84 if ( m_Ranges.empty() ) {
85 return ret;
86 }
87 if ( !m_IsCircular ) {
88 if ( !m_TotalRanges_plus.Empty() ||
89 x_IncludesPlus(m_Ranges.front().second) ) {
90 ret |= eStrandPlus;
91 }
92 if ( !m_TotalRanges_minus.Empty() ||
93 x_IncludesMinus(m_Ranges.front().second) ) {
94 ret |= eStrandMinus;
95 }
96 }
97 else {
98 if ( x_IncludesPlus(m_Ranges.front().second) ) {
99 ret |= eStrandPlus;
100 }
101 if ( x_IncludesMinus(m_Ranges.front().second) ) {
102 ret |= eStrandMinus;
103 }
104 }
105 return ret;
106 }
107
108
AddRange(TRange range,ENa_strand strand)109 void CHandleRange::AddRange(TRange range, ENa_strand strand)
110 {
111 AddRange(range, strand, false, false);
112 }
113
114
AddRange(TRange range,ENa_strand strand,bool more_before,bool more_after)115 void CHandleRange::AddRange(TRange range, ENa_strand strand,
116 bool more_before, bool more_after)
117 {
118 if ( !m_Ranges.empty() && m_IsSingleStrand ) {
119 if ( strand != m_Ranges.front().second) {
120 // Different strands, the location can not be circular
121 if ( m_IsCircular ) {
122 // Different strands, the location can not be circular
123 // Reorganize total ranges by strand
124 TRange total_range = m_TotalRanges_plus;
125 total_range += m_TotalRanges_minus;
126 if ( x_IncludesPlus(m_Ranges.front().second) ) {
127 m_TotalRanges_plus = total_range;
128 }
129 else {
130 m_TotalRanges_plus = TRange::GetEmpty();
131 }
132 if ( x_IncludesMinus(m_Ranges.front().second) ) {
133 m_TotalRanges_minus = total_range;
134 }
135 else {
136 m_TotalRanges_minus = TRange::GetEmpty();
137 }
138 m_IsCircular = false;
139 }
140 m_IsSingleStrand = false;
141 }
142 else {
143 // Same strand, but location may become circular
144 if ( !m_IsCircular ) {
145 // Check if location becomes circular
146 NON_CONST_REVERSE_ITERATE ( TRanges, it, m_Ranges ) {
147 // compare with last non-empty range
148 if ( !it->first.Empty() ) {
149 if ( x_IncludesPlus(strand) ) {
150 m_IsCircular =
151 range.GetFrom() < it->first.GetFrom();
152 }
153 else {
154 m_IsCircular =
155 range.GetFrom() > it->first.GetFrom();
156 }
157 break;
158 }
159 }
160 if ( m_IsCircular ) {
161 // Reorganize total ranges.
162 // First part (everything already collected)
163 // goes to m_TotalRanges_plus,
164 // second part (all new ranges)
165 // will go to m_TotalRanges_minus.
166
167 // Verify that until now all ranges are on the same strand.
168 _ASSERT(m_TotalRanges_plus.Empty() ||
169 m_TotalRanges_minus.Empty() ||
170 m_TotalRanges_plus == m_TotalRanges_minus);
171 m_TotalRanges_plus += m_TotalRanges_minus;
172 m_TotalRanges_minus = TRange::GetEmpty();
173 }
174 else {
175 _ASSERT(m_IsSingleStrand && !m_IsCircular);
176 _ASSERT(!m_Ranges.empty());
177 //_ASSERT(more_before);
178 //_ASSERT(m_MoreAfter);
179 if ( more_after ) {
180 m_MoreAfter = true;
181 }
182 }
183 }
184 }
185 }
186 else {
187 if ( more_before ) {
188 m_MoreBefore = true;
189 }
190 if ( more_after ) {
191 m_MoreAfter = true;
192 }
193 }
194 m_Ranges.push_back(TRanges::value_type(range, strand));
195 if ( !m_IsCircular ) {
196 // Regular location
197 if ( x_IncludesPlus(strand) ) {
198 m_TotalRanges_plus += range;
199 }
200 if ( x_IncludesMinus(strand) ) {
201 m_TotalRanges_minus += range;
202 }
203 }
204 else {
205 // Circular location, second part
206 m_TotalRanges_minus += range;
207 }
208 }
209
210
AddRanges(const CHandleRange & hr)211 void CHandleRange::AddRanges(const CHandleRange& hr)
212 {
213 ITERATE ( CHandleRange, it, hr ) {
214 AddRange(it->first, it->second);
215 }
216 }
217
218
x_IntersectingStrands(ENa_strand str1,ENa_strand str2)219 bool CHandleRange::x_IntersectingStrands(ENa_strand str1, ENa_strand str2)
220 {
221 return
222 str1 == eNa_strand_unknown // str1 includes anything
223 ||
224 str2 == eNa_strand_unknown // str2 includes anything
225 ||
226 str1 == str2; // accept only equal strands
227 //### Not sure about "eNa_strand_both includes eNa_strand_plus" etc.
228 }
229
230
IntersectingWithTotalRange(const CHandleRange & hr) const231 bool CHandleRange::IntersectingWithTotalRange(const CHandleRange& hr) const
232 {
233 return IsCircular() || hr.IsCircular() ||
234 m_TotalRanges_plus.IntersectingWith(hr.m_TotalRanges_plus) ||
235 m_TotalRanges_minus.IntersectingWith(hr.m_TotalRanges_minus);
236 }
237
238
IntersectingWithSubranges(const CHandleRange & hr) const239 bool CHandleRange::IntersectingWithSubranges(const CHandleRange& hr) const
240 {
241 ITERATE(TRanges, it1, m_Ranges) {
242 ITERATE(TRanges, it2, hr.m_Ranges) {
243 if ( it1->first.IntersectingWith(it2->first) ) {
244 if ( x_IntersectingStrands(it1->second, it2->second) ) {
245 return true;
246 }
247 }
248 }
249 }
250 return false;
251 }
252
253
IntersectingWith(const CHandleRange & hr) const254 bool CHandleRange::IntersectingWith(const CHandleRange& hr) const
255 {
256 return IntersectingWithTotalRange(hr) && IntersectingWithSubranges(hr);
257 }
258
259
IntersectingWith_NoStrand(const CHandleRange & hr) const260 bool CHandleRange::IntersectingWith_NoStrand(const CHandleRange& hr) const
261 {
262 if ( !GetOverlappingRange().IntersectingWith(hr.GetOverlappingRange()) ) {
263 return false;
264 }
265 ITERATE(TRanges, it1, m_Ranges) {
266 ITERATE(TRanges, it2, hr.m_Ranges) {
267 if ( it1->first.IntersectingWith(it2->first) ) {
268 return true;
269 }
270 }
271 }
272 return false;
273 }
274
275
MergeRange(TRange range,ENa_strand)276 void CHandleRange::MergeRange(TRange range, ENa_strand /*strand*/)
277 {
278 for ( TRanges::iterator it = m_Ranges.begin(); it != m_Ranges.end(); ) {
279 // Find intersecting intervals, discard strand information
280 // Also merge adjacent ranges, prevent merging whole-to + whole-from
281 if ( !it->first.Empty() &&
282 (it->first.IntersectingWith(range) ||
283 it->first.GetFrom() == range.GetToOpen() ||
284 it->first.GetToOpen() == range.GetFrom()) ) {
285 // Remove the intersecting interval, update the merged range.
286 // We assume that WholeFrom is less than any non-whole value
287 // and WholeTo is greater than any non-whole value.
288 range += it->first;
289 it = m_Ranges.erase(it);
290 }
291 else {
292 ++it;
293 }
294 }
295 AddRange(range, eNa_strand_unknown);
296 }
297
298
GetLeft(void) const299 TSeqPos CHandleRange::GetLeft(void) const
300 {
301 if ( !m_IsCircular ) {
302 // Since empty ranges have extremely large 'from' coordinate it's
303 // ok to simply return min of 'from' coordinates.
304 return min(m_TotalRanges_plus.GetFrom(), m_TotalRanges_minus.GetFrom());
305 }
306 return IsReverse(m_Ranges.front().second) ?
307 m_TotalRanges_minus.GetFrom() : m_TotalRanges_plus.GetFrom();
308 }
309
310
GetRight(void) const311 TSeqPos CHandleRange::GetRight(void) const
312 {
313 if ( !m_IsCircular ) {
314 // A bit more logic is required to check empty ranges.
315 if ( m_TotalRanges_minus.Empty() ) {
316 return m_TotalRanges_plus.GetTo();
317 }
318 else if ( m_TotalRanges_plus.Empty() ) {
319 return m_TotalRanges_minus.GetTo();
320 }
321 else {
322 return max(m_TotalRanges_plus.GetTo(), m_TotalRanges_minus.GetTo());
323 }
324 }
325 return IsReverse(m_Ranges.front().second) ?
326 m_TotalRanges_plus.GetTo() : m_TotalRanges_minus.GetTo();
327 }
328
329
330 CHandleRange::TRange
GetOverlappingRange(TTotalRangeFlags flags) const331 CHandleRange::GetOverlappingRange(TTotalRangeFlags flags) const
332 {
333 TRange ret = TRange::GetEmpty();
334 if (m_IsCircular) {
335 ETotalRangeFlags circular_strand =
336 IsReverse(m_Ranges.front().second) ?
337 eStrandMinus : eStrandPlus;
338 if (flags & circular_strand) {
339 ret = TRange::GetWhole();
340 }
341 return ret;
342 }
343 if (flags & eStrandPlus) { // == eCircularStart
344 ret += m_TotalRanges_plus;
345 }
346 if (flags & eStrandMinus) { // == eCircularEnd
347 ret += m_TotalRanges_minus;
348 }
349 if ( m_IsSingleStrand && (m_MoreBefore || m_MoreAfter) ) {
350 _ASSERT(!m_Ranges.empty());
351 if ( x_IncludesPlus(m_Ranges.front().second) ) {
352 if ( (flags & eStrandPlus) ||
353 x_IncludesMinus(m_Ranges.front().second) ) {
354 if ( m_MoreBefore ) {
355 ret.SetFrom(TRange::GetWholeFrom());
356 }
357 if ( m_MoreAfter ) {
358 ret.SetTo(TRange::GetWholeTo());
359 }
360 }
361 }
362 else {
363 if ( (flags & eStrandMinus) ) {
364 if ( m_MoreAfter ) {
365 ret.SetFrom(TRange::GetWholeFrom());
366 }
367 if ( m_MoreBefore ) {
368 ret.SetTo(TRange::GetWholeTo());
369 }
370 }
371 }
372 }
373 return ret;
374 }
375
376
377 CHandleRange::TRange
GetCircularRangeStart(bool include_origin) const378 CHandleRange::GetCircularRangeStart(bool include_origin) const
379 {
380 _ASSERT(m_IsCircular);
381 TRange ret = m_TotalRanges_plus;
382 if ( include_origin ) {
383 // Adjust start/stop to include cut point
384 if ( !IsReverse(m_Ranges.front().second) ) {
385 // Include end
386 ret.SetTo(TRange::GetWholeTo());
387 }
388 else {
389 // Include start
390 ret.SetFrom(TRange::GetWholeFrom());
391 }
392 }
393 return ret;
394 }
395
396
397 CHandleRange::TRange
GetCircularRangeEnd(bool include_origin) const398 CHandleRange::GetCircularRangeEnd(bool include_origin) const
399 {
400 _ASSERT(m_IsCircular);
401 TRange ret = m_TotalRanges_minus;
402 if ( include_origin ) {
403 // Adjust start/stop to include cut point
404 if ( !IsReverse(m_Ranges.front().second) ) {
405 // Include end
406 ret.SetFrom(TRange::GetWholeFrom());
407 }
408 else {
409 // Include start
410 ret.SetTo(TRange::GetWholeTo());
411 }
412 }
413 return ret;
414 }
415
416
417 CHandleRange::TRange
GetOverlappingRange(const TRange & range) const418 CHandleRange::GetOverlappingRange(const TRange& range) const
419 {
420 TRange ret = TRange::GetEmpty();
421 if ( !range.Empty() ) {
422 ITERATE ( TRanges, it, m_Ranges ) {
423 ret += it->first.IntersectionWith(range);
424 }
425 }
426 return ret;
427 }
428
429
IntersectingWith(const TRange & range,ENa_strand strand) const430 bool CHandleRange::IntersectingWith(const TRange& range,
431 ENa_strand strand) const
432 {
433 if ( !range.Empty() ) {
434 ITERATE ( TRanges, it, m_Ranges ) {
435 if ( range.IntersectingWith(it->first) &&
436 x_IntersectingStrands(strand, it->second) ) {
437 return true;
438 }
439 }
440 }
441 return false;
442 }
443
444
HasGaps(void) const445 bool CHandleRange::HasGaps(void) const
446 {
447 return m_Ranges.size() > 1 || m_MoreBefore || m_MoreAfter;
448 }
449
450
451 END_SCOPE(objects)
452 END_NCBI_SCOPE
453