1 // ==========================================================================
2 // SeqAn - The Library for Sequence Analysis
3 // ==========================================================================
4 // Copyright (c) 2006-2015, Knut Reinert, FU Berlin
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions are met:
9 //
10 // * Redistributions of source code must retain the above copyright
11 // notice, this list of conditions and the following disclaimer.
12 // * Redistributions in binary form must reproduce the above copyright
13 // notice, this list of conditions and the following disclaimer in the
14 // documentation and/or other materials provided with the distribution.
15 // * Neither the name of Knut Reinert or the FU Berlin nor the names of
16 // its contributors may be used to endorse or promote products derived
17 // from this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 // ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
23 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
29 // DAMAGE.
30 //
31 // ==========================================================================
32 // Author: Stephan Aiche <stephan.aiche@fu-berlin.de>
33 // ==========================================================================
34
35 #include <stack>
36
37 // TODO(holtgrew): Get rid of this?
38 //#define SEQAN_HIRSCHBERG_DEBUG_CUT
39
40 #ifndef SEQAN_INCLUDE_SEQAN_ALIGN_GLOBAL_ALIGNMENT_HIRSCHBERG_IMPL_H_
41 #define SEQAN_INCLUDE_SEQAN_ALIGN_GLOBAL_ALIGNMENT_HIRSCHBERG_IMPL_H_
42
43 namespace seqan {
44
45 // ============================================================================
46 // Forwards
47 // ============================================================================
48
49 // ============================================================================
50 // Tags, Classes, Enums
51 // ============================================================================
52
53 struct Hirschberg_;
54 typedef Tag<Hirschberg_> Hirschberg;
55
56 // ----------------------------------------------------------------------------
57 // Helper Class HirschbergSet_
58 // ----------------------------------------------------------------------------
59
60 // State for the implementation of Hirschberg's algorithm.
61
62 class HirschbergSet_
63 {
64 public:
65 int x1,x2,y1,y2;
66 int score;
67
HirschbergSet_()68 HirschbergSet_()
69 : x1(0), x2(0), y1(0), y2(0), score(0)
70 {}
71
HirschbergSet_(int a1,int a2,int b1,int b2,int sc)72 HirschbergSet_(int a1,int a2,int b1,int b2,int sc)
73 : x1(a1), x2(a2), y1(b1), y2(b2), score(sc)
74 {
75 SEQAN_ASSERT_LEQ(a1, a2);
76 SEQAN_ASSERT_LEQ(b1, b2);
77 }
78
79 HirschbergSet_ &
80 operator=(HirschbergSet_ const & other_)
81 {
82 x1 = other_.x1;
83 x2 = other_.x2;
84 y1 = other_.y1;
85 y2 = other_.y2;
86 score = other_.score;
87 return *this;
88 }
89
90 };
91
92 // ============================================================================
93 // Metafunctions
94 // ============================================================================
95
96 // ============================================================================
97 // Functions
98 // ============================================================================
99
100 // ----------------------------------------------------------------------------
101 // Function _begin1()
102 // ----------------------------------------------------------------------------
103
104 inline int&
_begin1(HirschbergSet_ & me)105 _begin1(HirschbergSet_ & me) {
106 return me.x1;
107 }
108
109
110 inline int const&
_begin1(HirschbergSet_ const & me)111 _begin1(HirschbergSet_ const & me) {
112 return me.x1;
113 }
114
115 // ----------------------------------------------------------------------------
116 // Function _setBegin1()
117 // ----------------------------------------------------------------------------
118
119 inline void
_setBegin1(HirschbergSet_ & me,int const & new_begin)120 _setBegin1(HirschbergSet_ & me, int const & new_begin) {
121 me.x1 = new_begin;
122 }
123
124 // ----------------------------------------------------------------------------
125 // Function _end1()
126 // ----------------------------------------------------------------------------
127
128 inline int&
_end1(HirschbergSet_ & me)129 _end1(HirschbergSet_ & me) {
130 return me.x2;
131 }
132
133 inline int const&
_end1(HirschbergSet_ const & me)134 _end1(HirschbergSet_ const & me) {
135 return me.x2;
136 }
137
138 // ----------------------------------------------------------------------------
139 // Function _setEnd1()
140 // ----------------------------------------------------------------------------
141
142 inline void
_setEnd1(HirschbergSet_ & me,int const & new_end)143 _setEnd1(HirschbergSet_ & me, int const & new_end) {
144 me.x2 = new_end;
145 }
146
147 // ----------------------------------------------------------------------------
148 // Function _begin2()
149 // ----------------------------------------------------------------------------
150
151 inline int&
_begin2(HirschbergSet_ & me)152 _begin2(HirschbergSet_ & me) {
153 return me.y1;
154 }
155
156 inline int const&
_begin2(HirschbergSet_ const & me)157 _begin2(HirschbergSet_ const & me) {
158 return me.y1;
159 }
160
161 // ----------------------------------------------------------------------------
162 // Function _setBegin2()
163 // ----------------------------------------------------------------------------
164
165 inline void
_setBegin2(HirschbergSet_ & me,int const & new_begin)166 _setBegin2(HirschbergSet_ & me, int const & new_begin) {
167 me.y1 = new_begin;
168 }
169
170 // ----------------------------------------------------------------------------
171 // Function _end2()
172 // ----------------------------------------------------------------------------
173
174 inline int&
_end2(HirschbergSet_ & me)175 _end2(HirschbergSet_ & me) {
176 return me.y2;
177 }
178
179 inline int const&
_end2(HirschbergSet_ const & me)180 _end2(HirschbergSet_ const & me) {
181 return me.y2;
182 }
183
184 // ----------------------------------------------------------------------------
185 // Function _setEnd2()
186 // ----------------------------------------------------------------------------
187
188 inline void
_setEnd2(HirschbergSet_ & me,int const & new_end)189 _setEnd2(HirschbergSet_ & me, int const & new_end) {
190 me.y2 = new_end;
191 }
192
193 // ----------------------------------------------------------------------------
194 // Function _score()
195 // ----------------------------------------------------------------------------
196
197 inline int&
_score(HirschbergSet_ & me)198 _score(HirschbergSet_ & me) {
199 return me.score;
200 }
201
202 // ----------------------------------------------------------------------------
203 // Function _score()
204 // ----------------------------------------------------------------------------
205
206 inline int const&
_score(HirschbergSet_ const & me)207 _score(HirschbergSet_ const & me)
208 {
209 return me.score;
210 }
211
212 // ----------------------------------------------------------------------------
213 // Function _setScore()
214 // ----------------------------------------------------------------------------
215
216 inline void
_setScore(HirschbergSet_ & me,int new_score)217 _setScore(HirschbergSet_ & me,int new_score) {
218 me.score = new_score;
219 }
220
221 // ----------------------------------------------------------------------------
222 // Function _setBegin2()
223 // ----------------------------------------------------------------------------
224
225 // //////////////////////////////////////////////////////////////////////////////////////////////
226 // Debug Methods
227 // functions are only used for debugging or verbose output, therefore they
228 // are only active in SEQAN_DEBUG
229 // //////////////////////////////////////////////////////////////////////////////////////////////
230 #ifdef SEQAN_DEBUG
231
232 inline
233 void
print(HirschbergSet_ const & me)234 print(HirschbergSet_ const & me)
235 {
236 std::cout << me.x1 << " " << me.x2 << "\t" << me.y1 << " " << me.y2 << std::endl;
237 }
238 #endif
239
240 // ----------------------------------------------------------------------------
241 // Function operator==()
242 // ----------------------------------------------------------------------------
243
244 inline bool
245 operator==(HirschbergSet_ const & lhs,
246 HirschbergSet_ const & rhs)
247 {
248 return ((_begin1(lhs) == _begin1(rhs)) && (_end1(lhs) == _end1(rhs)) &&
249 (_begin2(lhs) == _begin2(rhs)) && (_end2(lhs) == _end2(rhs)));
250 }
251
252 // ----------------------------------------------------------------------------
253 // Function _writeDebugMatrix()
254 // ----------------------------------------------------------------------------
255
256 #ifdef SEQAN_HIRSCHBERG_DEBUG_CUT
257 template<typename TSource>
_writeDebugMatrix(TSource s1,TSource s2)258 void _writeDebugMatrix(TSource s1,TSource s2)
259 {
260 int l1 = length(s1);
261 int l2 = length(s2);
262
263 int i,j,sg,sd;
264
265 String<String<int> > fMatrix,rMatrix,tMatrix;
266
267 resize(fMatrix,l1 + 1);
268 resize(rMatrix,l1 + 1);
269 resize(tMatrix,l1 + 1);
270
271 for(i = 0;i <= l1;++i)
272 {
273 resize(fMatrix[i],l2 + 1);
274 resize(rMatrix[i],l2 + 1);
275 resize(tMatrix[i],l2 + 1);
276 }
277
278 for(i = 0;i <= l1;++i)
279 fMatrix[i][0] = i * (-1);
280
281 for(i = l1;i >= 0;--i)
282 rMatrix[i][l2] = (l1 - i) * (-1);
283
284 // calculate forward matrix
285 for(j = 1;j <= l2;++j)
286 {
287 fMatrix[0][j] = j*(-1);
288 for(i = 1;i <= l1;++i)
289 {
290 sg = -1 + ((fMatrix[i-1][j] > fMatrix[i][j-1]) ? fMatrix[i-1][j] : fMatrix[i][j-1]);
291 sd = fMatrix[i-1][j-1] + ((s1[i - 1] == s2[j-1]) ? 0 : -1 );
292
293 fMatrix[i][j] = ((sg > sd) ? sg : sd);
294 }
295 }
296
297 // calculate reverse matrix
298 for(j = l2 - 1;j >= 0;--j)
299 {
300 rMatrix[l1][j] = (l2 - j)*(-1);
301 for(i = l1 - 1;i >= 0;--i)
302 {
303 sg = -1 + ((rMatrix[i+1][j] > rMatrix[i][j+1]) ? rMatrix[i+1][j] : rMatrix[i][j+1]);
304 sd = rMatrix[i+1][j+1] + ((s1[i] == s2[j]) ? 0 : -1 );
305
306 rMatrix[i][j] = ((sg > sd) ? sg : sd);
307 }
308 }
309
310 // print fMatrix
311 std::cout << ";-;";
312 for(i = 0;i < l1;++i)
313 std::cout << s1[i] << ";";
314
315 std::cout << std::endl << "-;";
316 for(j = 0;j <= l2;++j)
317 {
318 if(j != 0) std::cout << s2[j-1] << ";";
319 for(i = 0;i <= l1;++i)
320 {
321 std::cout << fMatrix[i][j] << ";";
322 }
323 std::cout << std::endl;
324 }
325 // print rMatrix
326 std::cout << ";";
327 for(i = 0;i < l1;++i)
328 std::cout << s1[i] << ";";
329 std::cout << "-;" << std::endl;
330
331 for(j = 0;j <= l2;++j)
332 {
333 if(j != l2) std::cout << s2[j] << ";";
334 else std::cout << "-;";
335 for(i = 0;i <= l1;++i)
336 {
337 std::cout << rMatrix[i][j] << ";";
338 }
339 std::cout << std::endl;
340 }
341
342 // fill and print target matrix
343 std::cout << ";-;";
344 for(i = 0;i < l1;++i)
345 std::cout << s1[i] << ";";
346
347 std::cout << std::endl << "-;";
348 for(j = 0;j <= l2;++j)
349 {
350 if(j != 0) std::cout << s2[j-1] << ";";
351 for(i = 0;i <= l1;++i)
352 {
353 tMatrix[i][j] = fMatrix[i][j] + rMatrix[i][j];
354 std::cout << tMatrix[i][j] << ";";
355 }
356 std::cout << std::endl;
357 }
358 }
359
360 #endif
361
362 // debug flag .. define to see where Hirschberg cuts the sequences
363 //#define SEQAN_HIRSCHBERG_DEBUG_CUT
364
365 // ----------------------------------------------------------------------------
366 // Function globalAlignment()
367 // ----------------------------------------------------------------------------
368
369 template <typename TSequenceH, typename TGapsSpecH, typename TSequenceV, typename TGapsSpecV,
370 typename TScoreValue, typename TScoreSpec>
371 TScoreValue
_globalAlignment(Gaps<TSequenceH,TGapsSpecH> & gapsH,Gaps<TSequenceV,TGapsSpecV> & gapsV,Score<TScoreValue,TScoreSpec> const & score_,Hirschberg const &)372 _globalAlignment(Gaps<TSequenceH, TGapsSpecH> & gapsH,
373 Gaps<TSequenceV, TGapsSpecV> & gapsV,
374 Score<TScoreValue, TScoreSpec> const & score_,
375 Hirschberg const & /*algorithmTag*/)
376 {
377 TSequenceH const & s1 = source(gapsH);
378 TSequenceV const & s2 = source(gapsV);
379
380 TScoreValue total_score = 0;
381
382 typedef typename Value<TSequenceV>::Type TValueV;
383
384 typedef typename Size<TSequenceH>::Type TStringSize;
385
386 typedef typename Iterator<TSequenceH const, Standard>::Type TSequenceHIter;
387 typedef typename Iterator<TSequenceV const, Standard>::Type TSequenceVIter;
388
389 typedef typename Iterator<Gaps<TSequenceH, TGapsSpecH> >::Type TGapsHIter;
390 typedef typename Iterator<Gaps<TSequenceV, TGapsSpecV> >::Type TGapsVIter;
391
392 TGapsHIter target_0 = begin(gapsH);
393 TGapsVIter target_1 = begin(gapsV);
394
395 typedef typename Iterator<Matrix<TScoreValue> >::Type TMatrixIterator;
396
397 TValueV v;
398
399 TStringSize len1 = length(s1);
400 TStringSize len2 = length(s2);
401
402 // string to store the score values for the currently active cell
403 String<TScoreValue> c_score;
404 resize(c_score,len2 + 1);
405 // string to strore the backpointers
406 String<int> pointer;
407 resize(pointer,len2 + 1);
408
409 // scoring-scheme specific score values
410 TScoreValue score_match = scoreMatch(score_);
411 TScoreValue score_mismatch = scoreMismatch(score_);
412 TScoreValue score_gap = scoreGapExtend(score_);
413
414 TScoreValue border,s,sg,sd,sg1,sg2;
415 int dp;
416
417 std::stack<HirschbergSet_> to_process;
418 HirschbergSet_ target;
419
420 int i,j;
421
422 HirschbergSet_ hs_complete(0,len1,0,len2,0);
423 to_process.push(hs_complete);
424
425 while(!to_process.empty())
426 {
427 target = to_process.top();
428 to_process.pop();
429
430 if(_begin2(target) == _end2(target))
431 {
432 for(i = 0;i < (_end1(target) - _begin1(target));++i)
433 {
434 insertGap(target_1);
435 ++target_0;
436 ++target_1;
437 }
438 }
439 if(_begin1(target) == _end1(target))
440 {
441 for(i = 0;i < (_end2(target) - _begin2(target));++i)
442 {
443 insertGap(target_0);
444 ++target_0;
445 ++target_1;
446 }
447 }
448 else if(_begin1(target) + 1 == _end1(target) || _begin2(target) + 1 == _end2(target))
449 {
450 /* ALIGN */
451 #ifdef SEQAN_HIRSCHBERG_DEBUG_CUT
452 std::cout << "align s1 " << _begin1(target) << " to " << _end1(target) << " and s2 " << _begin2(target) << " to " << _end2(target) << std::endl;
453 std::cout << "align " << infix(s1,_begin1(target),_end1(target)) << " and " << infix(s2,_begin2(target),_end2(target)) << std::endl << std::endl;
454 #endif
455
456 TStringSize len_1 = _end1(target) - _begin1(target);
457 TStringSize len_2 = _end2(target) - _begin2(target);
458
459 Matrix<TScoreValue> matrix_;
460
461 setDimension(matrix_, 2);
462 setLength(matrix_, 0, len_1 + 1);
463 setLength(matrix_, 1, len_2 + 1);
464 resize(matrix_);
465
466 /* init matrix */
467 TSequenceHIter x_begin = iter(s1, _begin1(target), Standard()) - 1;
468 TSequenceHIter x_end = iter(s1, _end1(target), Standard()) - 1;
469 TSequenceVIter y_begin = iter(s2, _begin2(target), Standard()) - 1;
470 TSequenceVIter y_end = iter(s2, _end2(target), Standard()) - 1;
471
472 TSequenceHIter x = x_end;
473 TSequenceVIter y;
474
475 TMatrixIterator col_ = end(matrix_) - 1;
476 TMatrixIterator finger1;
477 TMatrixIterator finger2;
478
479
480 TScoreValue h = 0;
481 TScoreValue border_ = score_gap;
482 TScoreValue v = border_;
483
484
485 //-------------------------------------------------------------------------
486 // init
487
488 finger1 = col_;
489 *finger1 = 0;
490 for (x = x_end; x != x_begin; --x)
491 {
492 goPrevious(finger1, 0);
493 *finger1 = border_;
494 border_ += score_gap;
495 }
496
497 //-------------------------------------------------------------------------
498 //fill matrix
499 border_ = 0;
500 for (y = y_end; y != y_begin; --y)
501 {
502 TValueV cy = *y;
503 h = border_;
504 border_ += score_gap;
505 v = border_;
506
507 finger2 = col_;
508 goPrevious(col_, 1);
509 finger1 = col_;
510
511 *finger1 = v;
512
513 for (x = x_end; x != x_begin; --x)
514 {
515 goPrevious(finger1, 0);
516 goPrevious(finger2, 0);
517 if (*x == cy)
518 {
519 v = h + score_match;
520 h = *finger2;
521 }
522 else
523 {
524 TScoreValue s1 = h + score_mismatch;
525 h = *finger2;
526 TScoreValue s2 = score_gap + ((h > v) ? h : v);
527 v = (s1 > s2) ? s1 : s2;
528 }
529 *finger1 = v;
530 }
531 }
532 total_score += value(matrix_, 0,0);
533 #ifdef SEQAN_HIRSCHBERG_DEBUG_CUT
534 std::cout << "alignment score is " << total_score << std::endl << std::endl;
535 #endif
536
537 /* TRACE BACK */
538 finger1 = begin(matrix_);
539 x = iter(s1,_begin1(target));
540 y = iter(s2,_begin2(target));
541 x_end = iter(s1,_end1(target));
542 y_end = iter(s2,_end2(target));
543
544 while ((x != x_end) && (y != y_end))
545 {
546 bool gv;
547 bool gh;
548
549 if (*x == *y)
550 {
551 gv = gh = true;
552 }
553 else
554 {
555 TMatrixIterator it_ = finger1;
556
557 goNext(it_, 0);
558 TScoreValue v = *it_;
559
560 goNext(it_, 1);
561 TScoreValue d = *it_;
562
563 it_ = finger1;
564 goNext(it_, 1);
565 TScoreValue h = *it_;
566
567 gv = (v >= h) | (d >= h);
568 gh = (h >= v) | (d >= v);
569 }
570
571 if (gv)
572 {
573 ++x;
574 goNext(finger1, 0);
575 }
576 else
577 {
578 insertGap(target_0);
579 }
580
581 if (gh)
582 {
583 ++y;
584 goNext(finger1, 1);
585 }
586 else
587 {
588 insertGap(target_1);
589 }
590
591 ++target_0;
592 ++target_1;
593 }
594
595 // if x or y did not reached there end position, fill the rest with gaps
596 while(x != x_end)
597 {
598 insertGap(target_1);
599 ++target_0;
600 ++target_1;
601 ++x;
602 }
603
604 while(y != y_end)
605 {
606 insertGap(target_0);
607 ++target_0;
608 ++target_1;
609 ++y;
610 }
611 /* END ALIGN */
612 }
613 else
614 {
615 /*
616 Calculate cut using the algorithm as proposed in the lecture of Clemens Gröpl
617 using a backpointer to remember the position where the optimal alignment passes
618 the mid column
619 */
620 int mid = static_cast<int>(floor( static_cast<double>((_begin1(target) + _end1(target))/2) ));
621
622 #ifdef SEQAN_HIRSCHBERG_DEBUG_CUT
623 std::cout << "calculate cut for s1 " << _begin1(target) << " to " << _end1(target) << " and s2 " << _begin2(target) << " to " << _end2(target) << std::endl;
624 std::cout << "calculate cut for " << infix(s1,_begin1(target),_end1(target)) << " and " << infix(s2,_begin2(target),_end2(target)) << std::endl;
625 std::cout << "cut is in row " << mid << " symbol is " << getValue(s1,mid-1) << std::endl << std::endl;
626
627
628 _writeDebugMatrix(infix(s1,_begin1(target),_end1(target)),infix(s2,_begin2(target),_end2(target)));
629 #endif
630
631 border = 0;
632 for(i = _begin2(target);i <= _end2(target);++i)
633 {
634 c_score[i] = border;
635 border += score_gap;
636 pointer[i] = i;
637 }
638
639 // iterate over s1 until the mid column is reached
640 border = score_gap;
641 for(i = _begin1(target) + 1;i <= mid;++i)
642 {
643 s = c_score[_begin2(target)];
644 c_score[_begin2(target)] = border;
645 border += score_gap;
646 v = getValue(s1,i-1);
647 for(j = _begin2(target) + 1;j <= _end2(target);++j)
648 {
649 sg = score_gap + ((c_score[j] > c_score[j - 1]) ? c_score[j] : c_score[j - 1]);
650 sd = s + ((v == getValue(s2,j-1)) ? score_match : score_mismatch);
651
652 s = c_score[j];
653 c_score[j] = (sg > sd) ? sg : sd;
654 }
655 }
656
657 // from here, rememeber the cell of mid-column, where optimal alignment passed
658 for(i = mid + 1;i <= _end1(target);++i)
659 {
660 s = c_score[_begin2(target)];
661 c_score[_begin2(target)] = border;
662 border += score_gap;
663 v = getValue(s1,i-1);
664
665 dp = _begin2(target);
666
667 for(j = _begin2(target) + 1;j <= _end2(target);++j)
668 {
669 sg1 = score_gap + c_score[j];
670 sg2 = score_gap + c_score[j - 1];
671
672 sd = s + ((v == getValue(s2,j-1)) ? score_match : score_mismatch);
673
674 s = c_score[j];
675 sg = pointer[j];
676 if(sd >= _max(sg1,sg2))
677 {
678 c_score[j] = sd;
679 pointer[j] = dp;
680 }
681 else
682 {
683 if(sg2 > sg1)
684 {
685 c_score[j] = sg2;
686 pointer[j] = pointer[j-1];
687 }
688 else
689 {
690 // gap introduced from left
691 // no update for the pointer
692 c_score[j] = sg1;
693 }
694 }
695 dp = sg;
696 }
697 }
698
699 #ifdef SEQAN_HIRSCHBERG_DEBUG_CUT
700 std::cout << "hirschberg calculates cut in column " << mid << " and row " << pointer[_end2(target)] << std::endl;
701 std::cout << "requested position in c_score and pointer is " << _end2(target) << std::endl;
702 std::cout << "alignment score is " << c_score[_end2(target)] << std::endl << std::endl;
703 #endif
704 to_process.push(HirschbergSet_(mid,_end1(target),pointer[_end2(target)],_end2(target),0));
705 to_process.push(HirschbergSet_(_begin1(target),mid,_begin2(target),pointer[_end2(target)],0));
706 }
707 /* END CUT */
708 }
709 return total_score;
710 }
711
712 } // namespace seqan
713
714 #endif // #ifndef SEQAN_INCLUDE_SEQAN_ALIGN_GLOBAL_ALIGNMENT_HIRSCHBERG_IMPL_H_
715