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