1 /**
2     Defines bindings and utilities to/for the dazzler commands.
3 
4     Copyright: © 2018 Arne Ludwig <arne.ludwig@posteo.de>
5     License: Subject to the terms of the MIT license, as written in the
6              included LICENSE file.
7     Authors: Arne Ludwig <arne.ludwig@posteo.de>
8 */
9 module dentist.dazzler;
10 
11 import core.memory : GC;
12 import dentist.common : ReferenceInterval, ReferenceRegion;
13 import dentist.common.alignments : AlignmentChain, coord_t, diff_t, id_t, trace_point_t;
14 import dentist.common.binio : CompressedSequence;
15 import dentist.util.algorithm : sliceUntil;
16 import dentist.util.fasta : parseFastaRecord, reverseComplement;
17 import dentist.util.log;
18 import dentist.util.math : absdiff, floor, ceil, RoundingMode;
19 import dentist.util.range : arrayChunks, takeExactly;
20 import dentist.util.region : findTilings, min, sup;
21 import dentist.util..string :
22     EditOp,
23     findAlignment,
24     longestInputsLength,
25     memoryRequired,
26     score_t,
27     SequenceAlignment;
28 import dentist.util.tempfile : mkstemp;
29 import std.algorithm :
30     all,
31     among,
32     cache,
33     canFind,
34     copy,
35     countUntil,
36     cumulativeFold,
37     endsWith,
38     filter,
39     find,
40     isSorted,
41     joiner,
42     map,
43     max,
44     maxElement,
45     min,
46     minElement,
47     remove,
48     sort,
49     splitter,
50     startsWith,
51     sum,
52     SwapStrategy,
53     uniq,
54     until;
55 import std.array : appender, Appender, array, uninitializedArray;
56 import std.conv : to;
57 import std.exception : enforce;
58 import std.file : exists, remove;
59 import std.format : format, formattedRead;
60 import std.meta : AliasSeq;
61 import std.path :
62     absolutePath,
63     baseName,
64     buildPath,
65     dirName,
66     relativePath,
67     stripExtension,
68     withExtension;
69 import std.process : Config, escapeShellCommand, kill, pipeProcess,
70     ProcessPipes, Redirect, wait;
71 import std.range :
72     chain,
73     chunks,
74     drop,
75     enumerate,
76     generate,
77     only,
78     repeat,
79     slide,
80     takeExactly,
81     zip;
82 import std.range.primitives :
83     ElementType,
84     empty,
85     front,
86     isForwardRange,
87     isInputRange,
88     popFront,
89     save,
90     walkLength;
91 import std.stdio : File;
92 import std..string : lineSplitter, outdent;
93 import std.traits : isArray, isIntegral, isSomeString, ReturnType, Unqual;
94 import std.typecons : Flag, No, tuple, Tuple, Yes;
95 import std.variant : Algebraic;
96 import vibe.data.json : Json, toJson = serializeToJson;
97 
98 debug import std.stdio : writeln;
99 
100 /// File suffixes of hidden .db files.
101 private enum hiddenDbFileSuffixes = [".bps", ".idx"];
102 
103 /// File suffixes of hidden .dam files.
104 private enum hiddenDamFileSuffixes = [".bps", ".hdr", ".idx"];
105 
106 /// Constant holding the .db file extension.
107 enum dbFileExtension = ".db";
108 
109 /// Constant holding the .dam file extension.
110 enum damFileExtension = ".dam";
111 
112 /// The Dazzler tools require sequence of a least minSequenceLength base pairs.
113 enum minSequenceLength = 14;
114 
115 enum isOptionsList(T) = isArray!T && isSomeString!(ElementType!T);
116 
117 /**
118     Return a list of hidden files associated to every `.dam`/`.db` file. These
119     files contain the actual data used in all the computation. Thus, we
120     carefully check for their existence.
121 */
122 auto getHiddenDbFiles(string dbFile)
123 {
124     import std.algorithm : map;
125 
126     assert(dbFile.endsWith(dbFileExtension, damFileExtension), "must use with Dazzler DB");
127     auto suffixes = dbFile.endsWith(dbFileExtension)
128         ? hiddenDbFileSuffixes
129         : hiddenDamFileSuffixes;
130 
131     return suffixes.map!(suffix => buildPath(dbFile.dirName,
132             "." ~ dbFile.baseName.withExtension(suffix).to!string));
133 }
134 
135 class DazzlerCommandException : Exception
136 {
137     pure nothrow @nogc @safe this(string msg, string file = __FILE__,
138             size_t line = __LINE__, Throwable nextInChain = null)
139     {
140         super(msg, file, line, nextInChain);
141     }
142 }
143 
144 /// Determines how data should be provided in the working directory.
145 enum ProvideMethod
146 {
147     copy,
148     symlink,
149 }
150 enum provideMethods = __traits(allMembers, ProvideMethod);
151 
152 /**
153     Provide dbFile in `workdir`.
154 
155     Returns: Path of the dbFile in `workdir`.
156 */
157 string provideDamFileInWorkdir(in string dbFile, ProvideMethod provideMethod, in string workdir)
158 {
159     foreach (hiddenDbFile; getHiddenDbFiles(dbFile))
160     {
161         provideFileInWorkdir(hiddenDbFile, provideMethod, workdir);
162     }
163 
164     return provideFileInWorkdir(dbFile, provideMethod, workdir);
165 }
166 
167 /**
168     Provide lasFile in `workdir`.
169 
170     Returns: Path of the lasFile in `workdir`.
171 */
172 string provideLasFileInWorkdir(in string lasFile, ProvideMethod provideMethod, in string workdir)
173 {
174     return provideFileInWorkdir(lasFile, provideMethod, workdir);
175 }
176 
177 /// Provide file in `workdir`.
178 string provideFileInWorkdir(in string file, ProvideMethod provideMethod, in string workdir)
179 {
180     import std.file : copy, symlink;
181     import std.path : absolutePath;
182 
183     auto fileInWorkdir = buildPath(workdir, file.baseName);
184 
185     final switch (provideMethod)
186     {
187     case ProvideMethod.copy:
188         copy(file.absolutePath, fileInWorkdir);
189         break;
190     case ProvideMethod.symlink:
191         symlink(file.absolutePath, fileInWorkdir);
192         break;
193     }
194 
195     return fileInWorkdir;
196 }
197 
198 /// Returns true iff lasFile contains zero parts.
199 bool lasEmpty(in string lasFile, in string dbA, in string workdir)
200 {
201     return lasEmpty(lasFile, dbA, null, workdir);
202 }
203 
204 /// ditto
205 bool lasEmpty(in string lasFile, in string dbA, in string dbB, in string workdir)
206 {
207     auto dumpHeader = ladump(lasFile, dbA, dbB, [], workdir);
208 
209     if (dumpHeader.empty)
210     {
211         return true;
212     }
213 
214     size_t numParts;
215 
216     dumpHeader.front.formattedRead!"+ P %d"(numParts);
217 
218     return numParts == 0;
219 }
220 
221 /// Build a new .dam file by using the given subset of reads in inDbFile.
222 string dbSubset(Options, R)(in string inDbFile, R readIds, in Options options)
223         if (isSomeString!(typeof(options.workdir)) &&
224             isOptionsList!(typeof(options.dbsplitOptions)))
225 {
226     enum outDbNameTemplate = "subset-XXXXXX";
227 
228     auto outDbTemplate = buildPath(options.workdir, outDbNameTemplate);
229     auto outDb = mkstemp(outDbTemplate, damFileExtension);
230 
231     outDb.file.close();
232     remove(outDb.name);
233     buildSubsetDb(inDbFile, outDb.name, readIds, options.workdir);
234     dbsplit(outDb.name, options.dbsplitOptions, options.workdir);
235 
236     return outDb.name;
237 }
238 
239 AlignmentChain[] getLocalAlignments(Options)(in string dbA, in Options options)
240         if (isOptionsList!(typeof(options.dalignerOptions)) &&
241             isOptionsList!(typeof(options.ladumpOptions)) &&
242             isSomeString!(typeof(options.workdir)))
243 {
244     if (!lasFileGenerated(dbA, options.workdir))
245     {
246         dalign(dbA, options.dalignerOptions, options.workdir);
247     }
248 
249     return getGeneratedAlignments(dbA, null, options);
250 }
251 
252 AlignmentChain[] getLocalAlignments(Options)(in string dbA, in string dbB, in Options options)
253         if (isOptionsList!(typeof(options.dalignerOptions)) &&
254             isOptionsList!(typeof(options.ladumpOptions)) &&
255             isSomeString!(typeof(options.workdir)))
256 {
257     if (!lasFileGenerated(dbA, dbB, options.workdir))
258     {
259         dalign(dbA, dbB, options.dalignerOptions, options.workdir);
260     }
261 
262     return getGeneratedAlignments(dbA, dbB, options);
263 }
264 
265 void computeLocalAlignments(Options)(in string[] dbList, in Options options)
266         if (isOptionsList!(typeof(options.dalignerOptions)) &&
267             isSomeString!(typeof(options.workdir)))
268 {
269     dalign(dbList, options.dalignerOptions, options.workdir);
270 }
271 
272 AlignmentChain[] getMappings(Options)(in string dbA, in string dbB, in Options options)
273         if (isOptionsList!(typeof(options.damapperOptions)) &&
274             isOptionsList!(typeof(options.ladumpOptions)) &&
275             isSomeString!(typeof(options.workdir)))
276 {
277     if (!lasFileGenerated(dbA, dbB, options.workdir))
278     {
279         damapper(dbA, dbB, options.damapperOptions, options.workdir);
280     }
281 
282     return getGeneratedAlignments(dbA, dbB, options);
283 }
284 
285 void computeMappings(Options)(in string[] dbList, in Options options)
286         if (isOptionsList!(typeof(options.damapperOptions)) &&
287             isOptionsList!(typeof(options.ladumpOptions)) &&
288             isSomeString!(typeof(options.workdir)))
289 {
290     damapper(dbList, options.damapperOptions, options.workdir);
291 }
292 
293 private AlignmentChain[] getGeneratedAlignments(Options)(
294     in string dbA,
295     in string dbB,
296     in Options options
297 )
298         if (isOptionsList!(typeof(options.ladumpOptions)) &&
299             isSomeString!(typeof(options.workdir)))
300 {
301     auto lasFile = getLasFile(dbA, dbB, options.workdir);
302 
303     return getAlignments(dbA, dbB, lasFile, options);
304 }
305 
306 AlignmentChain[] getAlignments(
307     in string dbA,
308     in string lasFile,
309     in string workdir,
310     in trace_point_t tracePointDistance = 0,
311 )
312 {
313     return getAlignments(dbA, null, lasFile, workdir, tracePointDistance);
314 }
315 
316 AlignmentChain[] getAlignments(
317     in string dbA,
318     in string dbB,
319     in string lasFile,
320     in string workdir,
321     in trace_point_t tracePointDistance = 0,
322 )
323 {
324     string[] ladumpOptions = [
325         LAdumpOptions.coordinates,
326         LAdumpOptions.numDiffs,
327         LAdumpOptions.lengths,
328     ];
329 
330     if (tracePointDistance > 0)
331         ladumpOptions ~= LAdumpOptions.tracePoints;
332 
333     auto alignmentChains = readLasDump(ladump(
334         lasFile,
335         dbA,
336         dbB,
337         ladumpOptions,
338         workdir,
339     )).array;
340     alignmentChains.sort!("a < b", SwapStrategy.stable);
341 
342     if (tracePointDistance > 0)
343         foreach (ref alignmentChain; alignmentChains)
344             alignmentChain.tracePointDistance = tracePointDistance;
345 
346     return alignmentChains;
347 }
348 
349 void attachTracePoints(
350     AlignmentChain*[] alignmentChains,
351     in string dbA,
352     in string dbB,
353     in string lasFile,
354     in trace_point_t tracePointDistance,
355     in string workdir
356 )
357 {
358     static enum ladumpOptions = [
359         LAdumpOptions.coordinates,
360         LAdumpOptions.tracePoints,
361     ];
362 
363     // NOTE: dump only for matching A reads; better would be for matching
364     //       B reads but that is not possible with `LAdump`.
365     auto aReadIds = alignmentChains.map!"a.contigA.id".uniq.array;
366     auto acsWithTracePoints = readLasDump(ladump(
367         lasFile,
368         dbA,
369         dbB,
370         aReadIds,
371         ladumpOptions,
372         workdir,
373     )).array;
374     acsWithTracePoints.sort!"a < b";
375     assert(isSorted!"*a < *b"(alignmentChains), "alignmentChains must be sorted");
376 
377     auto numAttached = alignmentChains.attachTracePoints(acsWithTracePoints, tracePointDistance);
378     assert(numAttached == alignmentChains.length,
379             "missing trace point lists for some alignment chains");
380 
381     debug logJsonDebug("acsWithTracePoints", acsWithTracePoints.toJson);
382     version (assert)
383     {
384         // ensure all alignment chains are valid...
385         foreach (alignmentChain; alignmentChains)
386         {
387             // trigger invariant of AlignmentChain
388             cast(void) alignmentChain.first;
389         }
390     }
391 }
392 
393 auto fingerprint(in AlignmentChain* alignmentChain) pure nothrow
394 {
395     return tuple(
396         alignmentChain.contigA.id,
397         alignmentChain.contigB.id,
398         alignmentChain.first.contigA.begin + 0,
399         alignmentChain.first.contigB.begin + 0,
400         alignmentChain.last.contigA.end + 0,
401         alignmentChain.last.contigB.end + 0,
402     );
403 }
404 
405 private id_t attachTracePoints(AlignmentChain*[] alignmentChains,
406         ref AlignmentChain[] acsWithTracePoints, in trace_point_t tracePointDistance) pure
407 {
408     assert(tracePointDistance > 0);
409 
410     id_t numAlignmentChainsAffected = 0;
411     id_t numLoops = 0;
412     id_t i = 0;
413     id_t j = 0;
414 
415     while (i < alignmentChains.length && j < acsWithTracePoints.length
416             && numLoops < alignmentChains.length + acsWithTracePoints.length)
417     {
418         auto alignmentChain = alignmentChains[i];
419         auto acWithTracePoints = &acsWithTracePoints[j];
420         auto acFingerprint = alignmentChain.fingerprint;
421         auto tpFingerprint = acWithTracePoints.fingerprint;
422 
423         debug logJsonDebug(
424             "acFingerprint", acFingerprint.toJson,
425             "tpFingerprint", tpFingerprint.toJson,
426         );
427 
428         if (acFingerprint == tpFingerprint)
429         {
430             foreach (k, ref localAlignment; alignmentChain.localAlignments)
431             {
432                 localAlignment.tracePoints = acWithTracePoints.localAlignments[k].tracePoints;
433             }
434             alignmentChain.tracePointDistance = tracePointDistance;
435             numAlignmentChainsAffected = i + 1;
436             ++i;
437         }
438         else if (tpFingerprint < acFingerprint)
439         {
440             ++j;
441         }
442         else
443         {
444             assert(tpFingerprint > acFingerprint);
445             throw new Exception(
446                     format!"missing trace point data for alignment chain: %s"(*alignmentChain));
447         }
448 
449         ++numLoops;
450     }
451 
452     return numAlignmentChainsAffected;
453 }
454 
455 unittest
456 {
457     with (AlignmentChain) with (LocalAlignment)
458     {
459         auto alignmentChains = [
460             new AlignmentChain(
461                 1,
462                 Contig(1, 1337),
463                 Contig(1, 307),
464                 emptyFlags,
465                 [
466                     LocalAlignment(Locus(0, 1337), Locus(0, 307), 1),
467                 ],
468             ),
469             new AlignmentChain(
470                 2,
471                 Contig(1, 1338),
472                 Contig(1, 309),
473                 emptyFlags,
474                 [
475                     LocalAlignment(Locus(0, 1338), Locus(0, 309), 2),
476                 ],
477             ),
478             new AlignmentChain(
479                 3,
480                 Contig(1, 1340),
481                 Contig(3, 508),
482                 emptyFlags,
483                 [
484                     LocalAlignment(Locus(0, 1339), Locus(0, 403), 3),
485                     LocalAlignment(Locus(0, 1340), Locus(404, 508), 4),
486                 ],
487             ),
488         ];
489         auto acsWithTracePoints = [
490             AlignmentChain(
491                 1,
492                 Contig(1, 1337),
493                 Contig(1, 307),
494                 emptyFlags,
495                 [
496                     LocalAlignment(Locus(0, 1337), Locus(0, 307), 0, [
497                         TracePoint(1, 102),
498                         TracePoint(2, 101),
499                         TracePoint(3, 104),
500                     ]),
501                 ],
502                 101
503             ),
504             AlignmentChain(
505                 2,
506                 Contig(1, 1338),
507                 Contig(1, 309),
508                 emptyFlags,
509                 [
510                     LocalAlignment(Locus(0, 1338), Locus(0, 309), 0, [
511                         TracePoint(4, 103),
512                         TracePoint(5, 104),
513                         TracePoint(6, 102),
514                     ]),
515                 ],
516                 101
517             ),
518             AlignmentChain(
519                 3,
520                 Contig(1, 1340),
521                 Contig(3, 508),
522                 emptyFlags,
523                 [
524                     LocalAlignment(Locus(0, 1339), Locus(0, 403), 0, [
525                         TracePoint(7, 105),
526                         TracePoint(8, 101),
527                         TracePoint(9, 100),
528                         TracePoint(10, 97),
529                     ]),
530                     LocalAlignment(Locus(0, 1340), Locus(404, 508), 0, [
531                         TracePoint(11, 2),
532                         TracePoint(12, 102),
533                     ]),
534                 ],
535                 101
536             ),
537         ];
538         auto expectedAlignmentChains = [
539             AlignmentChain(
540                 1,
541                 Contig(1, 1337),
542                 Contig(1, 307),
543                 emptyFlags,
544                 [
545                     LocalAlignment(Locus(0, 1337), Locus(0, 307), 1, [
546                         TracePoint(1, 102),
547                         TracePoint(2, 101),
548                         TracePoint(3, 104),
549                     ]),
550                 ],
551                 101
552             ),
553             AlignmentChain(
554                 2,
555                 Contig(1, 1338),
556                 Contig(1, 309),
557                 emptyFlags,
558                 [
559                     LocalAlignment(Locus(0, 1338), Locus(0, 309), 2, [
560                         TracePoint(4, 103),
561                         TracePoint(5, 104),
562                         TracePoint(6, 102),
563                     ]),
564                 ],
565                 101
566             ),
567             AlignmentChain(
568                 3,
569                 Contig(1, 1340),
570                 Contig(3, 508),
571                 emptyFlags,
572                 [
573                     LocalAlignment(Locus(0, 1339), Locus(0, 403), 3, [
574                         TracePoint(7, 105),
575                         TracePoint(8, 101),
576                         TracePoint(9, 100),
577                         TracePoint(10, 97),
578                     ]),
579                     LocalAlignment(Locus(0, 1340), Locus(404, 508), 4, [
580                         TracePoint(11, 2),
581                         TracePoint(12, 102),
582                     ]),
583                 ],
584                 101
585             ),
586         ];
587 
588         assert(alignmentChains.attachTracePoints(acsWithTracePoints, 101) == 3);
589         assert(alignmentChains.map!"*a".array == expectedAlignmentChains);
590     }
591 }
592 
593 private struct LasDumpLineFormatTuple
594 {
595     char indicator;
596     char subIndicator;
597     string format;
598 }
599 
600 private enum LasDumpLineFormat
601 {
602     totalChainPartsCount = LasDumpLineFormatTuple('+', 'P', "+ P %d"),
603     totalTracePointsCount = LasDumpLineFormatTuple('+', 'T', "+ T %d"),
604     maxChainPartsCountPerPile = LasDumpLineFormatTuple('%', 'P', "% P %d"),
605     maxTracePointsCountPerPile = LasDumpLineFormatTuple('%', 'T', "% T %d"),
606     maxTracePointCount = LasDumpLineFormatTuple('@', 'T', "@ T %d"),
607     chainPart = LasDumpLineFormatTuple('P', '\0', "P %d %d %c %c"),
608     lengths = LasDumpLineFormatTuple('L', '\0', "L %d %d"),
609     coordinates = LasDumpLineFormatTuple('C', '\0', "C %d %d %d %d"),
610     numDiffs = LasDumpLineFormatTuple('D', '\0', "D %d"),
611     tracePointBegin = LasDumpLineFormatTuple('T', '\0', "T %d"),
612     tracePoint = LasDumpLineFormatTuple(' ', '\0', " %d %d"),
613 }
614 
615 private enum ChainPartType
616 {
617     start = '>',
618     continuation = '-',
619     alternateStart = '+',
620     noChainInFile = '.',
621 }
622 
623 private struct LasDumpReader(S) if (isInputRange!S && isSomeString!(ElementType!S))
624 {
625     static alias dstring = immutable(dchar)[];
626     static alias LasDump = ReturnType!getDumpLines;
627     static alias LocalAlignment = AlignmentChain.LocalAlignment;
628     static alias TracePoint = LocalAlignment.TracePoint;
629 
630 private:
631     LasDump lasDump;
632     bool _empty;
633     AlignmentChain currentAC;
634     id_t currentACID;
635     Appender!(LocalAlignment[]) localAlignmentsAcc;
636     Appender!(TracePoint[]) tracePointsAcc;
637     dstring currentDumpLine;
638     size_t currentDumpLineNumber;
639     dchar currentLineType;
640     dchar currentLineSubType;
641     size_t maxTracePointCount;
642     debug dchar[] allowedLineTypes;
643 
644 public:
645     this(S lasDump)
646     {
647         this.lasDump = getDumpLines(lasDump);
648         debug with (LasDumpLineFormat)
649         {
650             this.allowedLineTypes = [
651                 totalChainPartsCount.indicator,
652                 totalTracePointsCount.indicator,
653                 maxChainPartsCountPerPile.indicator,
654                 maxTracePointsCountPerPile.indicator,
655                 maxTracePointCount.indicator,
656                 chainPart.indicator,
657             ];
658         }
659         this.popFront();
660     }
661 
662     void popFront()
663     {
664         assert(!empty, "Attempting to popFront an empty LasDumpReader");
665 
666         if (lasDump.empty)
667         {
668             return setEmpty();
669         }
670 
671         readNextAlignmentChain();
672     }
673 
674     @property bool empty() const pure nothrow
675     {
676         return _empty;
677     }
678 
679     @property AlignmentChain front() pure nothrow
680     {
681         assert(!empty, "Attempting to fetch the front of an empty LasDumpReader");
682 
683         return currentAC;
684     }
685 
686 private:
687 
688     static auto getDumpLines(S lasDump)
689     {
690         return lasDump.enumerate(1).filter!"!a[1].empty";
691     }
692 
693     void setEmpty() pure nothrow
694     {
695         _empty = true;
696     }
697 
698     void readNextAlignmentChain()
699     {
700         currentAC = AlignmentChain.init;
701         localAlignmentsAcc.clear();
702         tracePointsAcc.clear();
703         peekDumpLine();
704 
705         while (true)
706         {
707             debug _enforce(allowedLineTypes.canFind(currentLineType), format!"forbidden line type `%c`"(currentLineType));
708 
709             with (LasDumpLineFormat)
710             {
711                 switch (currentLineType)
712                 {
713                 case totalChainPartsCount.indicator: goto case;
714                 case maxChainPartsCountPerPile.indicator:
715                     break; // ignore
716                 case maxTracePointCount.indicator:
717                     _enforce(currentLineSubType == maxTracePointCount.subIndicator, "expected `@ T` line");
718                     readMaxTracePointCount();
719                     debug disallowCurrentLineType();
720                     break;
721                 case chainPart.indicator:
722                     if (readChainPart() == No.wasDumpPartConsumed)
723                     {
724                         debug allowedLineTypes = [chainPart.indicator];
725 
726                         return; // chain completed; stay on current dump line
727                     }
728                     else
729                     {
730                         debug allowedLineTypes = [
731                             chainPart.indicator,
732                             lengths.indicator,
733                             coordinates.indicator,
734                         ];
735                         break;
736                     }
737                 case lengths.indicator:
738                     readLengths();
739                     debug disallowCurrentLineType();
740                     break;
741                 case coordinates.indicator:
742                     readCoordinates();
743                     debug
744                     {
745                         disallowCurrentLineType();
746                         allowedLineTypes ~= [
747                             numDiffs.indicator,
748                             tracePointBegin.indicator,
749                         ];
750                     }
751                     break;
752                 case numDiffs.indicator:
753                     readNumDiffs();
754                     debug disallowCurrentLineType();
755                     break;
756                 case tracePointBegin.indicator:
757                     readTracePointBegin();
758                     debug allowedLineTypes = [tracePoint.indicator];
759                     break;
760                 case tracePoint.indicator:
761                     readTracePoint();
762                     debug allowedLineTypes = [tracePoint.indicator, chainPart.indicator];
763                     break;
764                 default:
765                     error(format!"unknown line type `%c`"(currentLineType));
766                 }
767             }
768 
769             if (popDumpLine() == Yes.empty)
770             {
771                 finishCurrentAC();
772 
773                 return; // EOF reached
774             }
775         }
776     }
777 
778     void peekDumpLine()
779     {
780         auto currentLine = lasDump.front;
781 
782         currentDumpLineNumber = currentLine[0];
783         currentDumpLine = currentLine[1].array;
784         currentLineType = currentDumpLine[0];
785         currentLineSubType = currentDumpLine.length >= 3 ? currentDumpLine[2] : '\0';
786     }
787 
788     Flag!"empty" popDumpLine()
789     {
790         if (lasDump.empty)
791         {
792             return Yes.empty;
793         }
794 
795         lasDump.popFront();
796         ++currentDumpLineNumber;
797 
798         if (lasDump.empty)
799         {
800             return Yes.empty;
801         }
802         else
803         {
804             peekDumpLine();
805 
806             return No.empty;
807         }
808     }
809 
810     void readMaxTracePointCount()
811     {
812         enum maxTracePointCountFormat = LasDumpLineFormat.maxTracePointCount.format;
813 
814         currentDumpLine[].formattedRead!maxTracePointCountFormat(maxTracePointCount);
815 
816         tracePointsAcc.reserve(maxTracePointCount);
817     }
818 
819     Flag!"wasDumpPartConsumed" readChainPart()
820     {
821         enum chainPartFormat = LasDumpLineFormat.chainPart.format;
822         enum yesComplement = AlignmentChain.Flags(AlignmentChain.Flag.complement);
823         enum noComplement = AlignmentChain.emptyFlags;
824         id_t contigAID;
825         id_t contigBID;
826         char rawComplement;
827         char rawChainPartType;
828 
829         currentDumpLine[].formattedRead!chainPartFormat(
830             contigAID,
831             contigBID,
832             rawComplement,
833             rawChainPartType,
834         );
835 
836         auto flags = rawComplement == 'c' ? yesComplement : noComplement;
837         auto chainPartType = rawChainPartType.to!ChainPartType;
838         auto startingNewChain = currentAC == AlignmentChain.init;
839 
840         if (startingNewChain)
841         {
842             currentAC.contigA.id = contigAID;
843             currentAC.contigB.id = contigBID;
844             currentAC.flags = flags;
845 
846             return Yes.wasDumpPartConsumed;
847         }
848         else if (isChainContinuation(contigAID, contigBID, chainPartType))
849         {
850             _enforce(currentAC.flags == flags, "matching both strands in one alignment chain");
851             finishCurrentLA();
852 
853             return Yes.wasDumpPartConsumed;
854         }
855         else
856         {
857             finishCurrentAC();
858 
859             return No.wasDumpPartConsumed;
860         }
861 
862     }
863 
864     bool isChainContinuation(in size_t contigAID, in size_t contigBID, in ChainPartType chainPartType)
865     {
866         return currentAC.contigA.id == contigAID &&
867                currentAC.contigB.id == contigBID &&
868                chainPartType == ChainPartType.continuation;
869     }
870 
871     void finishCurrentLA()
872     {
873         assert(localAlignmentsAcc.data.length > 0);
874 
875         localAlignmentsAcc.data[$ - 1].tracePoints = tracePointsAcc.data.dup;
876     }
877 
878     void finishCurrentLAs()
879     {
880         if (localAlignmentsAcc.data.length > 0)
881         {
882             finishCurrentLA();
883         }
884 
885         currentAC.localAlignments = localAlignmentsAcc.data.dup;
886     }
887 
888     void finishCurrentAC()
889     {
890         finishCurrentLAs();
891         currentAC.id = currentACID++;
892     }
893 
894     void readLengths()
895     {
896         enum lengthsFormat = LasDumpLineFormat.lengths.format;
897 
898         currentDumpLine[].formattedRead!lengthsFormat(
899             currentAC.contigA.length,
900             currentAC.contigB.length,
901         );
902     }
903     void readCoordinates()
904     {
905         enum coordinatesFormat = LasDumpLineFormat.coordinates.format;
906         LocalAlignment currentLA;
907 
908         currentDumpLine[].formattedRead!coordinatesFormat(
909             currentLA.contigA.begin,
910             currentLA.contigA.end,
911             currentLA.contigB.begin,
912             currentLA.contigB.end,
913         );
914 
915         localAlignmentsAcc ~= currentLA;
916     }
917 
918     void readNumDiffs()
919     {
920         enum numDiffsFormat = LasDumpLineFormat.numDiffs.format;
921 
922         currentDumpLine[].formattedRead!numDiffsFormat(
923             localAlignmentsAcc.data[$ - 1].numDiffs,
924         );
925     }
926 
927     void readTracePointBegin()
928     {
929         enum tracePointBeginFormat = LasDumpLineFormat.tracePointBegin.format;
930         size_t numTracePoints;
931 
932         currentDumpLine[].formattedRead!tracePointBeginFormat(numTracePoints);
933 
934         tracePointsAcc.clear();
935         tracePointsAcc.reserve(numTracePoints);
936     }
937 
938     void readTracePoint()
939     {
940         enum tracePointFormat = LasDumpLineFormat.tracePoint.format;
941         TracePoint currentTP;
942 
943         currentDumpLine[].formattedRead!tracePointFormat(
944             currentTP.numDiffs,
945             currentTP.numBasePairs,
946         );
947 
948         tracePointsAcc ~= currentTP;
949     }
950 
951     debug void disallowCurrentLineType() {
952         allowedLineTypes = allowedLineTypes
953             .filter!(type => type != currentLineType)
954             .array;
955     }
956 
957     void error(in string reason)
958     {
959         _enforce(false, reason);
960     }
961 
962     void _enforce(bool condition, lazy string reason)
963     {
964         enforce!DazzlerCommandException(condition, format!"ill-formatted LAdump output: %s (line %d)"(reason, currentDumpLineNumber));
965     }
966 }
967 
968 private auto readLasDump(S)(S lasDump)
969 {
970     return LasDumpReader!S(lasDump);
971 }
972 
973 unittest
974 {
975     import std.algorithm : equal;
976 
977     enum testLasDump = [
978         "+ P 9",
979         "% P 9",
980         "+ T 12",
981         "% T 12",
982         "@ T 4",
983         "P 1 2 n >",
984         "L 8 9",
985         "C 3 4 5 6",
986         "D 7",
987         "P 1 2 n -",
988         "L 8 9",
989         "C 12 13 14 15",
990         "D 16",
991         "P 19 20 c +",
992         "L 26 27",
993         "C 21 22 23 24",
994         "D 25",
995         "P 19 20 c -",
996         "L 26 27",
997         "C 30 31 32 33",
998         "D 34",
999         "T 1",
1000         "   0 1",
1001         "P 37 38 n .",
1002         "L 35 36",
1003         "C 39 40 41 42",
1004         "D 43",
1005         "P 46 47 c .",
1006         "L 53 54",
1007         "C 48 49 50 51",
1008         "D 52",
1009         "T 3",
1010         "   2 102",
1011         "   3 101",
1012         "   4 104",
1013         "P 46 47 n .",
1014         "L 53 54",
1015         "C 57 58 59 60",
1016         "D 61",
1017         "T 3",
1018         "   3 101",
1019         "   4 104",
1020         "   2 102",
1021         "P 64 65 c >",
1022         "L 71 72",
1023         "C 66 67 68 69",
1024         "D 70",
1025         "T 4",
1026         "   6 105",
1027         "   1 101",
1028         "   2 100",
1029         "   3  97",
1030         "P 55 56 c -",
1031         "L 80 81",
1032         "C 75 76 77 78",
1033         "D 79",
1034         "T 2",
1035         "   0   2",
1036         "   2 102",
1037         "P 1 3197 c >",
1038         "C 0 71 12 86",
1039         "T 1",
1040         "   3  74",
1041         "P 1 3197 c -",
1042         "C 0 8300 0 318",
1043         "T 3",
1044         "   6 105",
1045         "   9 108",
1046         "   7 105",
1047     ];
1048 
1049     with (AlignmentChain) with (Flag) with (LocalAlignment)
1050     {
1051         auto alignmentChains = readLasDump(testLasDump).array;
1052         auto expectedResult = [
1053             AlignmentChain(
1054                 0,
1055                 Contig(1, 8),
1056                 Contig(2, 9),
1057                 emptyFlags,
1058                 [
1059                     LocalAlignment(
1060                         Locus(3, 4),
1061                         Locus(5, 6),
1062                         7,
1063                     ),
1064                     LocalAlignment(
1065                         Locus(12, 13),
1066                         Locus(14, 15),
1067                         16,
1068                     ),
1069                 ],
1070             ),
1071             AlignmentChain(
1072                 1,
1073                 Contig(19, 26),
1074                 Contig(20, 27),
1075                 Flags(complement),
1076                 [
1077                     LocalAlignment(
1078                         Locus(21, 22),
1079                         Locus(23, 24),
1080                         25,
1081                     ),
1082                     LocalAlignment(
1083                         Locus(30, 31),
1084                         Locus(32, 33),
1085                         34,
1086                         [
1087                             TracePoint(0, 1),
1088                         ]
1089                     ),
1090                 ],
1091             ),
1092             AlignmentChain(
1093                 2,
1094                 Contig(37, 35),
1095                 Contig(38, 36),
1096                 emptyFlags,
1097                 [
1098                     LocalAlignment(
1099                         Locus(39, 40),
1100                         Locus(41, 42),
1101                         43,
1102                     ),
1103                 ],
1104             ),
1105             AlignmentChain(
1106                 3,
1107                 Contig(46, 53),
1108                 Contig(47, 54),
1109                 Flags(complement),
1110                 [
1111                     LocalAlignment(
1112                         Locus(48, 49),
1113                         Locus(50, 51),
1114                         52,
1115                         [
1116                             TracePoint(2, 102),
1117                             TracePoint(3, 101),
1118                             TracePoint(4, 104),
1119                         ],
1120                     ),
1121                 ],
1122             ),
1123             AlignmentChain(
1124                 4,
1125                 Contig(46, 53),
1126                 Contig(47, 54),
1127                 emptyFlags,
1128                 [
1129                     LocalAlignment(
1130                         Locus(57, 58),
1131                         Locus(59, 60),
1132                         61,
1133                         [
1134                             TracePoint(3, 101),
1135                             TracePoint(4, 104),
1136                             TracePoint(2, 102),
1137                         ],
1138                     ),
1139                 ],
1140             ),
1141             AlignmentChain(
1142                 5,
1143                 Contig(64, 71),
1144                 Contig(65, 72),
1145                 Flags(complement),
1146                 [
1147                     LocalAlignment(
1148                         Locus(66, 67),
1149                         Locus(68, 69),
1150                         70,
1151                         [
1152                             TracePoint(6, 105),
1153                             TracePoint(1, 101),
1154                             TracePoint(2, 100),
1155                             TracePoint(3, 97),
1156                         ],
1157                     ),
1158                 ],
1159             ),
1160             AlignmentChain(
1161                 6,
1162                 Contig(55, 80),
1163                 Contig(56, 81),
1164                 Flags(complement),
1165                 [
1166                     LocalAlignment(
1167                         Locus(75, 76),
1168                         Locus(77, 78),
1169                         79,
1170                         [
1171                             TracePoint(0, 2),
1172                             TracePoint(2, 102),
1173                         ],
1174                     ),
1175                 ],
1176             ),
1177             AlignmentChain(
1178                 7,
1179                 Contig(1, 0),
1180                 Contig(3197, 0),
1181                 Flags(complement),
1182                 [
1183                     LocalAlignment(
1184                         Locus(0, 71),
1185                         Locus(12, 86),
1186                         0,
1187                         [
1188                             TracePoint(3, 74),
1189                         ],
1190                     ),
1191                     LocalAlignment(
1192                         Locus(0, 8300),
1193                         Locus(0, 318),
1194                         0,
1195                         [
1196                             TracePoint(6, 105),
1197                             TracePoint(9, 108),
1198                             TracePoint(7, 105),
1199                         ],
1200                     ),
1201                 ],
1202             ),
1203         ];
1204 
1205         assert(alignmentChains == expectedResult);
1206     }
1207 }
1208 
1209 trace_point_t getTracePointDistance(in string[] dazzlerOptions = []) pure
1210 {
1211     enum defaultTracePointDistance = 100;
1212 
1213     foreach (option; dazzlerOptions)
1214     {
1215         if (option.startsWith(cast(const(string)) DalignerOptions.tracePointDistance))
1216         {
1217             return option[2 .. $].to!trace_point_t;
1218         }
1219     }
1220 
1221     return defaultTracePointDistance;
1222 }
1223 
1224 unittest
1225 {
1226     with (DalignerOptions)
1227     {
1228         assert(getTracePointDistance([]) == 100);
1229         assert(getTracePointDistance([identity]) == 100);
1230         assert(getTracePointDistance([
1231             tracePointDistance ~ "42",
1232             averageCorrelationRate ~ ".8",
1233             identity,
1234         ]) == 42);
1235         assert(getTracePointDistance([
1236             identity,
1237             tracePointDistance ~ "42",
1238             averageCorrelationRate ~ ".8",
1239         ]) == 42);
1240         assert(getTracePointDistance([
1241             averageCorrelationRate ~ ".8",
1242             identity,
1243             tracePointDistance ~ "42",
1244         ]) == 42);
1245     }
1246 }
1247 
1248 auto getExactAlignment(
1249     in string dbA,
1250     in string dbB,
1251     in AlignmentChain ac,
1252     in string workdir,
1253     in size_t memoryLimit = 2^^20,
1254 )
1255 {
1256     return getExactAlignment(
1257         dbA,
1258         dbB,
1259         ac,
1260         ac.first.contigA.begin,
1261         ac.last.contigA.end,
1262         workdir,
1263         memoryLimit,
1264     );
1265 }
1266 
1267 auto getExactAlignment(
1268     in string dbA,
1269     in string dbB,
1270     in AlignmentChain ac,
1271     in coord_t beginA,
1272     in coord_t endA,
1273     in string workdir,
1274     in size_t memoryLimit = 2^^20,
1275 )
1276 {
1277     assert(ac.tracePointDistance > 0, "trace points required for getExactAlignment");
1278     assert(
1279         ac.first.contigA.begin <= beginA &&
1280         beginA < endA &&
1281         endA <= ac.last.contigA.end
1282     );
1283 
1284     // Translate input coords to tracepoints to get exact alignments
1285     auto begin = ac.translateTracePoint(beginA, RoundingMode.floor);
1286     auto end = ac.translateTracePoint(endA, RoundingMode.ceil);
1287 
1288     // Fetch relevant sequences from DBs
1289     auto aSequence = getFastaSequence(dbA, ac.contigA.id, workdir);
1290     auto bSequence = getFastaSequence(dbB, ac.contigB.id, workdir);
1291 
1292     // Slice sequences to translated coordinates
1293     aSequence = aSequence[begin.contigA .. end.contigA];
1294     if (ac.flags.complement)
1295         bSequence = reverseComplement(bSequence[$ - end.contigB .. $ - begin.contigB]);
1296     else
1297         bSequence = bSequence[begin.contigB .. end.contigB];
1298 
1299     auto paddedAlignment = getPaddedAlignment(
1300         ac,
1301         begin,
1302         end,
1303         aSequence,
1304         bSequence,
1305         memoryLimit,
1306     );
1307 
1308     assert(begin.contigA <= beginA);
1309 
1310     return paddedAlignment[(beginA - begin.contigA) .. min(endA - begin.contigA, $)];
1311 }
1312 
1313 auto getPaddedAlignment(S, TranslatedTracePoint)(
1314     in AlignmentChain ac,
1315     in TranslatedTracePoint begin,
1316     in TranslatedTracePoint end,
1317     S aSequence,
1318     S bSequence,
1319     in size_t memoryLimit = 2^^20,
1320 )
1321 {
1322     static struct AlignmentPadder
1323     {
1324         static enum indelPenalty = 1;
1325 
1326         alias LocalAlignment = AlignmentChain.LocalAlignment;
1327 
1328         private const AlignmentChain ac;
1329         private const TranslatedTracePoint begin;
1330         private const TranslatedTracePoint end;
1331         private S aSequence;
1332         private S bSequence;
1333         private const size_t memoryLimit;
1334         private SequenceAlignment!S _paddedAlignment;
1335 
1336         this(
1337             in AlignmentChain ac,
1338             in TranslatedTracePoint begin,
1339             in TranslatedTracePoint end,
1340             S aSequence,
1341             S bSequence,
1342             in size_t memoryLimit = 2^^20,
1343         )
1344         {
1345             this.ac = ac;
1346             this.begin = begin;
1347             this.end = end;
1348             this.aSequence = aSequence;
1349             this.bSequence = bSequence;
1350             this.memoryLimit = memoryLimit;
1351             this._paddedAlignment = typeof(this._paddedAlignment)(
1352                 0,
1353                 [],
1354                 aSequence[0 .. 0],
1355                 bSequence[0 .. 0],
1356                 indelPenalty,
1357                 No.freeShift,
1358             );
1359             this._paddedAlignment.editPath.reserve(aSequence.length + bSequence.length);
1360         }
1361 
1362         @property auto paddedAlignment()
1363         {
1364             if (_paddedAlignment.editPath.length == 0)
1365                 computePaddedAlignment();
1366 
1367             return _paddedAlignment;
1368         }
1369 
1370     private:
1371 
1372         coord_t aSeqPos;
1373         coord_t bSeqPos;
1374 
1375         void computePaddedAlignment()
1376         {
1377             aSeqPos = begin.contigA + 0;
1378             bSeqPos = begin.contigB + 0;
1379             auto cleanedLocalAlignments = cleanUpLocalAlignments(ac.localAlignments);
1380             auto coveringLocalAlignments = cleanedLocalAlignments
1381                 .find!(la => la.contigA.begin <= begin.contigA)
1382                 .sliceUntil!(la => end.contigA < la.contigA.begin);
1383             assert(!coveringLocalAlignments.empty);
1384 
1385             foreach (i, localAlignment; coveringLocalAlignments.enumerate)
1386             {
1387                 if (i > 0)
1388                 {
1389                     if (
1390                         aSeqPos <= localAlignment.contigA.begin &&
1391                         bSeqPos <= localAlignment.contigB.begin
1392                     )
1393                         addGapAlignment(localAlignment);
1394                     else
1395                         resolveOverlappingAlignments(
1396                             coveringLocalAlignments[i - 1],
1397                             localAlignment,
1398                         );
1399                 }
1400 
1401                 auto skip = skipTracePointsToASeqPos(localAlignment);
1402                 foreach (tracePoint; localAlignment.tracePoints[skip .. $])
1403                 {
1404                     if (aSeqPos >= end.contigA)
1405                         return;
1406 
1407                     addTracePointAlignment(localAlignment, tracePoint);
1408                 }
1409             }
1410         }
1411 
1412         // NOTE: damapper may produce false/bad chains that include a
1413         //       complete stack of local alignments that have nearly
1414         //       100% overlap. In most cases they decrease performance
1415         //       drastically and in some cases they can even cause
1416         //       errors in this procedure. So let's ignore them at
1417         //       this point.
1418         const(LocalAlignment[]) cleanUpLocalAlignments(in LocalAlignment[] localAlignments)
1419         {
1420             ReferenceInterval toInterval(in LocalAlignment* la) pure
1421             {
1422                 return ReferenceInterval(
1423                     0,
1424                     la.contigA.begin,
1425                     la.contigA.end,
1426                 );
1427             }
1428 
1429             auto combinations = findTilings!toInterval(
1430                 localAlignments.map!((ref la) => &la).array,
1431                 longestInputsLength(memoryLimit),
1432             );
1433 
1434             alias Combination = typeof(combinations[0]);
1435             auto combinationScore(in Combination combination)
1436             {
1437                 long coveredBasePairs = combination.region.size;
1438                 long score = combination.region.size
1439                      - combination.totalOverlap
1440                      - combination.elements.map!"a.numDiffs".sum;
1441 
1442                 return score;
1443             }
1444 
1445             return combinations.maxElement!combinationScore.elements.map!"*a".array;
1446         }
1447 
1448         size_t skipTracePointsToASeqPos(in LocalAlignment localAlignment)
1449         {
1450             return localAlignment.tracePointsUpTo!"contigA"(
1451                 aSeqPos,
1452                 ac.tracePointDistance,
1453                 RoundingMode.floor,
1454             );
1455         }
1456 
1457         void addTracePointAlignment(
1458             in AlignmentChain.LocalAlignment la,
1459             in AlignmentChain.LocalAlignment.TracePoint tracePoint,
1460         )
1461         {
1462             auto aSeqBegin = aSeqPos - begin.contigA;
1463             auto aSeqEnd = nextTracePoint(la, aSeqPos) - begin.contigA;
1464             auto bSeqBegin = bSeqPos - begin.contigB;
1465             auto bSeqEnd = bSeqBegin + tracePoint.numBasePairs;
1466 
1467             auto tracePointAlignment = findAlignment(
1468                 aSequence[aSeqBegin .. aSeqEnd],
1469                 bSequence[bSeqBegin .. bSeqEnd],
1470                 indelPenalty,
1471                 No.freeShift,
1472                 memoryLimit,
1473             );
1474 
1475             appendPartialAlignment(tracePointAlignment, aSeqEnd, bSeqEnd, No.freeShift);
1476             aSeqPos = nextTracePoint(la, aSeqPos);
1477             bSeqPos += tracePoint.numBasePairs;
1478         }
1479 
1480         void addGapAlignment(in AlignmentChain.LocalAlignment afterGap)
1481         {
1482             auto aSeqBegin = aSeqPos - begin.contigA;
1483             auto aSeqEnd = afterGap.contigA.begin - begin.contigA;
1484             auto bSeqBegin = bSeqPos - begin.contigB;
1485             auto bSeqEnd = afterGap.contigB.begin - begin.contigB;
1486             auto aSubsequence = aSequence[aSeqBegin .. aSeqEnd];
1487             auto bSubsequence = bSequence[bSeqBegin .. bSeqEnd];
1488 
1489             if (memoryRequired(aSubsequence, bSubsequence) <= memoryLimit)
1490             {
1491                 GC.collect();
1492                 auto gapAlignment = findAlignment(
1493                     aSubsequence,
1494                     bSubsequence,
1495                     indelPenalty,
1496                     Yes.freeShift,
1497                     memoryLimit,
1498                 );
1499 
1500                 appendPartialAlignment(gapAlignment, aSeqEnd, bSeqEnd, Yes.freeShift);
1501             }
1502             else
1503             {
1504                 auto numIndels = cast(score_t) absdiff(aSubsequence.length, bSubsequence.length);
1505                 auto numSubst = cast(score_t) min(aSubsequence.length, bSubsequence.length);
1506                 auto indelOp = aSubsequence.length < bSubsequence.length
1507                     ? EditOp.insertion
1508                     : EditOp.deletetion;
1509 
1510                 logJsonWarn(
1511                     "info", "faking too long alignment gap",
1512                     "gapSize", aSubsequence.length,
1513                     "acFingerprint", [fingerprint(&ac).expand].toJson,
1514                 );
1515                 auto fakeAlignment = typeof(_paddedAlignment)(
1516                     0,
1517                     chain(indelOp.repeat(numIndels), EditOp.substitution.repeat(numSubst)).array,
1518                     aSubsequence,
1519                     bSubsequence,
1520                     indelPenalty,
1521                     Yes.freeShift,
1522                 );
1523                 fakeAlignment.score = fakeAlignment.computeScore();
1524                 assert(fakeAlignment.isValid());
1525                 appendPartialAlignment(fakeAlignment, aSeqEnd, bSeqEnd, Yes.freeShift);
1526             }
1527 
1528             aSeqPos = afterGap.contigA.begin;
1529             bSeqPos = afterGap.contigB.begin;
1530         }
1531 
1532         void resolveOverlappingAlignments(
1533             in AlignmentChain.LocalAlignment lhs,
1534             in AlignmentChain.LocalAlignment rhs,
1535         )
1536         {
1537             auto overlap = getNonOverlappingCoordinates(lhs, rhs);
1538 
1539             assert(aSeqPos >= overlap.begin.contigA);
1540             assert(bSeqPos >= overlap.begin.contigB);
1541 
1542             auto aSeqBegin = overlap.begin.contigA - begin.contigA;
1543             auto aSeqEnd = overlap.end.contigA - begin.contigA;
1544             // Crop alignment according to aSeqPos
1545             _paddedAlignment = _paddedAlignment[0 .. aSeqBegin];
1546             // Use resulting position on contig B as it may differ from
1547             // trace points due to previous overlap resolution
1548             auto bSeqBegin = _paddedAlignment.query.length;
1549             auto bSeqEnd = overlap.end.contigB - begin.contigB;
1550 
1551             GC.collect();
1552             auto overlapAlignment = findAlignment(
1553                 aSequence[aSeqBegin .. aSeqEnd],
1554                 bSequence[bSeqBegin .. bSeqEnd],
1555                 indelPenalty,
1556                 Yes.freeShift,
1557                 memoryLimit,
1558             );
1559             appendPartialAlignment(overlapAlignment, aSeqEnd, bSeqEnd, Yes.freeShift);
1560             aSeqPos = overlap.end.contigA;
1561             bSeqPos = overlap.end.contigB;
1562         }
1563 
1564         auto getNonOverlappingCoordinates(
1565             in AlignmentChain.LocalAlignment lhs,
1566             in AlignmentChain.LocalAlignment rhs,
1567         )
1568         {
1569             alias TranslatedTracePoint = AlignmentChain.TranslatedTracePoint;
1570             alias ResolvedCoords = Tuple!(
1571                 TranslatedTracePoint, "begin",
1572                 TranslatedTracePoint, "end",
1573             );
1574 
1575             alias resolveOn(string contig) = () => ResolvedCoords(
1576                 lhs.translateTracePoint!contig(
1577                     mixin(`rhs.` ~ contig ~ `.begin`),
1578                     ac.tracePointDistance,
1579                     RoundingMode.floor,
1580                 ),
1581                 rhs.translateTracePoint!contig(
1582                     mixin(`lhs.` ~ contig ~ `.end`),
1583                     ac.tracePointDistance,
1584                     RoundingMode.ceil,
1585                 ),
1586             );
1587 
1588             if (
1589                 lhs.contigA.end > rhs.contigA.begin &&
1590                 lhs.contigB.end > rhs.contigB.begin
1591             )
1592             {
1593                 auto resolvedOnContigA = resolveOn!"contigA"();
1594                 auto resolvedOnContigB = resolveOn!"contigB"();
1595 
1596                 return ResolvedCoords(
1597                     minElement!"a.contigA"(only(
1598                         resolvedOnContigA.begin,
1599                         resolvedOnContigB.begin,
1600                     )),
1601                     maxElement!"a.contigA"(only(
1602                         resolvedOnContigA.end,
1603                         resolvedOnContigB.end,
1604                     )),
1605                 );
1606             }
1607             else if (lhs.contigA.end > rhs.contigA.begin)
1608                 return resolveOn!"contigA"();
1609             else if (lhs.contigB.end > rhs.contigB.begin)
1610                 return resolveOn!"contigB"();
1611             else
1612                 assert(0, "unreachable");
1613         }
1614 
1615         void appendPartialAlignment(PartialAlignment, FreeShift)(
1616             PartialAlignment partialAlignment,
1617             in size_t aSeqEnd,
1618             in size_t bSeqEnd,
1619             in FreeShift freeShift,
1620         )
1621         {
1622             if (freeShift)
1623             {
1624                 auto firstSubstitution = partialAlignment.editPath.countUntil(EditOp.substitution);
1625 
1626                 _paddedAlignment.score += indelPenalty * (firstSubstitution < 0
1627                     ? partialAlignment.editPath.length
1628                     : firstSubstitution);
1629             }
1630 
1631             _paddedAlignment.score += partialAlignment.score;
1632             _paddedAlignment.editPath ~= partialAlignment.editPath;
1633             _paddedAlignment.reference = aSequence[0 .. aSeqEnd];
1634             _paddedAlignment.query = bSequence[0 .. bSeqEnd];
1635             assert(
1636                 _paddedAlignment.isValid(),
1637                 format
1638                     !"exact alignment invalid after stitching: %1$d %2$d [%3$d, %5$d) [%4$d, %6$d)"
1639                     (fingerprint(&ac).expand),
1640             );
1641         }
1642 
1643         coord_t nextTracePoint(
1644             in AlignmentChain.LocalAlignment la,
1645             coord_t contigAPos,
1646         )
1647         {
1648             alias isAlignedTracePoint = (pos) => pos % ac.tracePointDistance == 0;
1649 
1650             return min(
1651                 la.contigA.end,
1652                 isAlignedTracePoint(contigAPos)
1653                     ? contigAPos + ac.tracePointDistance
1654                     : ceil(contigAPos, ac.tracePointDistance),
1655             );
1656         }
1657     }
1658 
1659     return AlignmentPadder(ac, begin, end, aSequence, bSequence, memoryLimit).paddedAlignment;
1660 }
1661 
1662 unittest
1663 {
1664     enum tracePointDistance = 100;
1665     enum complement = AlignmentChain.Flag.complement;
1666     alias Contig = AlignmentChain.Contig;
1667     alias LocalAlignment = AlignmentChain.LocalAlignment;
1668     alias Flags = AlignmentChain.Flags;
1669     alias Locus = AlignmentChain.LocalAlignment.Locus;
1670     alias TracePoint = AlignmentChain.LocalAlignment.TracePoint;
1671 
1672     auto ac = AlignmentChain(
1673         0,
1674         Contig(1, 1092),
1675         Contig(12407, 12767),
1676         Flags(complement),
1677         [
1678             LocalAlignment(
1679                 Locus(0, 439),
1680                 Locus(6604, 7076),
1681                 67,
1682                 [
1683                     TracePoint(16, 105),
1684                     TracePoint(17, 106),
1685                     TracePoint(11, 108),
1686                     TracePoint(19, 111),
1687                     TracePoint( 4,  42),
1688                 ],
1689             ),
1690             LocalAlignment(
1691                 Locus(400, 439),
1692                 Locus(7034, 7076),
1693                 4,
1694                 [
1695                     TracePoint( 4,  42),
1696                 ],
1697             ),
1698             LocalAlignment(
1699                 Locus(590, 798),
1700                 Locus(7263, 7475),
1701                 41,
1702                 [
1703                     TracePoint( 1,  10),
1704                     TracePoint(20, 107),
1705                     TracePoint(20,  95),
1706                 ],
1707             ),
1708         ],
1709         tracePointDistance,
1710     );
1711     auto begin = ac.translateTracePoint(400, RoundingMode.floor);
1712     auto end = ac.translateTracePoint(600, RoundingMode.ceil);
1713     auto aSequence = "agtgctggccccagtcaagggcatgtacctgggttgcaggttccccagcc" ~
1714                      "cccgtaggggtgtgtgtgtggaaggcaaccaattgatgtgtctcctttat" ~
1715                      "gttgatgtttctctttctctctccctcctccctgtcttccactctctcta" ~
1716                      "gaaatcaatgggaaaaatatcctcaagtgaagattaaaaaaaaaaaaagg";
1717     auto bSequence = "agatgatggccccagtgcaagggcatgtacctggtgttgcagcggttcca" ~
1718                      "gccagcacccgtacggggcgtgcgtgtggaaagggcaaccaatttgatgt" ~
1719                      "ccctacgttttcatgttgatgttttctcttttctctctctctctcccttc" ~
1720                      "cttcccacttttcttcccagcttctctctagaaaacaagggaaaagtgtc" ~
1721                      "ctttcagtgtaggatttttaaaaaaagccaaaaaaaggg";
1722 
1723     auto exactAlignment = getPaddedAlignment(ac, begin, end, aSequence, bSequence);
1724 
1725     assert(exactAlignment.toString(50) ==
1726         "ag-tgctggccccagt-caagggcatgtacctgg-gttgcag--gttcc-\n" ~
1727         "|| ||*|||||||||| ||||||||||||||||| |||||||  ||||| \n" ~
1728         "agatgatggccccagtgcaagggcatgtacctggtgttgcagcggttcca\n" ~
1729         "\n" ~
1730         "-ccagcccccgta-ggggtgtgtgtgtggaa-gg-caaccaatt-gatgt\n" ~
1731         " |||||*|||||| ||||*|||*|||||||| || ||||||||| |||||\n" ~
1732         "gccagcacccgtacggggcgtgcgtgtggaaagggcaaccaatttgatgt\n" ~
1733         "\n" ~
1734         "gtct-ccttt--atgttgatgttt-ctcttt-ctctctc-c-ctcc-t-c\n" ~
1735         "**|| |*|||  |||||||||||| |||||| ||||||| | |||| | |\n" ~
1736         "ccctacgttttcatgttgatgttttctcttttctctctctctctcccttc\n" ~
1737         "\n" ~
1738         "c--c----tgt-cttcc-a-ct-ctctctagaaatcaatgggaaaaatat\n" ~
1739         "|  |    |*| ||||| | || |||||||||||*||| |||||||*|*|\n" ~
1740         "cttcccacttttcttcccagcttctctctagaaaacaa-gggaaaagtgt\n" ~
1741         "\n" ~
1742         "cct--caagtgaag-att---aaaaa-----aaaaaaaagg\n" ~
1743         "|||  || |||*|| |||   |||||     |||||||*||\n" ~
1744         "cctttca-gtgtaggatttttaaaaaaagccaaaaaaaggg");
1745 }
1746 
1747 unittest
1748 {
1749     import std.range : repeat;
1750     enum complement = AlignmentChain.Flag.complement;
1751     alias Contig = AlignmentChain.Contig;
1752     alias LocalAlignment = AlignmentChain.LocalAlignment;
1753     alias Flags = AlignmentChain.Flags;
1754     alias Locus = AlignmentChain.LocalAlignment.Locus;
1755     alias TracePoint = AlignmentChain.LocalAlignment.TracePoint;
1756 
1757     // TODO reduce test case to two consecutive "overlap" scenarios
1758     auto ac = AlignmentChain(
1759         574,
1760         Contig(5, 33046024),
1761         Contig(344, 73824),
1762         Flags(complement),
1763         [
1764             LocalAlignment(
1765                 Locus(8372381, 8419325),
1766                 Locus(0, 46966),
1767                 388,
1768                 array(chain(
1769                     only(TracePoint(3, 19), TracePoint(4, 102), TracePoint(5, 101), TracePoint(3, 99), TracePoint(4, 100), TracePoint(5, 98), TracePoint(3, 99), TracePoint(2, 100), TracePoint(3, 99), TracePoint(1, 101), TracePoint(5, 101), TracePoint(5, 103), TracePoint(8, 102), TracePoint(8, 102), TracePoint(6, 101), TracePoint(3, 101), TracePoint(3, 99), TracePoint(7, 103), TracePoint(5, 99), TracePoint(4, 99), TracePoint(2, 102), TracePoint(5, 101), TracePoint(6, 96), TracePoint(3, 99), TracePoint(4, 100), TracePoint(7, 101), TracePoint(1, 101), TracePoint(4, 98), TracePoint(3, 101), TracePoint(4, 102), TracePoint(3, 101), TracePoint(4, 104), TracePoint(7, 105), TracePoint(2, 102), TracePoint(8, 102), TracePoint(3, 99), TracePoint(10, 104), TracePoint(5, 99), TracePoint(5, 95), TracePoint(3, 101), TracePoint(3, 101), TracePoint(4, 99), TracePoint(3, 99), TracePoint(4, 100), TracePoint(7, 98), TracePoint(8, 105), TracePoint(4, 98), TracePoint(6, 98), TracePoint(8, 100), TracePoint(4, 100), TracePoint(8, 101), TracePoint(2, 98), TracePoint(5, 99), TracePoint(3, 101), TracePoint(3, 98), TracePoint(1, 101)),
1770                     TracePoint(0, 100).repeat(17),
1771                     only(TracePoint(8, 104), TracePoint(2, 102), TracePoint(2, 100), TracePoint(3, 101), TracePoint(2, 98), TracePoint(9, 97), TracePoint(3, 99), TracePoint(4, 98), TracePoint(2, 98), TracePoint(2, 99), TracePoint(4, 101), TracePoint(3, 99), TracePoint(5, 101), TracePoint(4, 99), TracePoint(5, 100), TracePoint(4, 98), TracePoint(1, 99), TracePoint(1, 99), TracePoint(3, 99), TracePoint(3, 101), TracePoint(5, 101), TracePoint(4, 100), TracePoint(4, 102), TracePoint(1, 99), TracePoint(4, 104)),
1772                     TracePoint(0, 100).repeat(138),
1773                     only(TracePoint(3, 102), TracePoint(5, 99), TracePoint(4, 100), TracePoint(5, 101), TracePoint(5, 97), TracePoint(1, 101)),
1774                     TracePoint(0, 100).repeat(163),
1775                     only(TracePoint(7, 103)),
1776                     TracePoint(0, 100).repeat(47),
1777                     only(TracePoint(3, 102), TracePoint(5, 99), TracePoint(1, 99), TracePoint(4, 100), TracePoint(4, 104), TracePoint(4, 99)),
1778                     TracePoint(0, 100).repeat(11),
1779                     only(TracePoint(0, 25)),
1780                 )),
1781             ),
1782             LocalAlignment(
1783                 Locus(8419319, 8419520),
1784                 Locus(46907, 47119),
1785                 27,
1786                 [
1787                     TracePoint(10, 87),
1788                     TracePoint(16, 104),
1789                     TracePoint(1, 21),
1790                 ],
1791             ),
1792             LocalAlignment(
1793                 Locus(8419355, 8446270),
1794                 Locus(46907, 73824),
1795                 31,
1796                 array(chain(
1797                     only(TracePoint(6, 47), TracePoint(24, 99), TracePoint(1, 101)),
1798                     TracePoint(0, 100).repeat(266),
1799                     only(TracePoint(0, 70)),
1800                 )),
1801             ),
1802         ],
1803         100,
1804     );
1805     auto begin = ac.translateTracePoint(ac.first.contigA.begin, RoundingMode.floor);
1806     auto end = ac.translateTracePoint(ac.last.contigA.end, RoundingMode.ceil);
1807     auto aSequence = "aaagaaggtgtagagattagccaaagaacatatatgcaaagcccacgaacccagaaaacagtgtggtgaaggccagagaggggtgggtagggactgggtggaagtgggcaaaggggggaggaatgggggacatgtgtaatagtgttaacaataaaaaacccaaaaccaaaaccaagaaggaatattaccactattcactattaatgattaagacatcacatgtgactcatcaggaaaaaacagtttacagtttcatatcctaagtggagtacgtacatttaatagttaaacaagttggctttgggatatggaaattataagggagaataaatatttaagcccatgatgcaatttaatttattttttctgtgacttactgaactaagcatgatcagtgagactatgacttagagaatcaaatgtctcctttgagccttgctgaggcaagatttatatttctcagatgtctacacttggaagacatgatcagaaataaaaatgtctgtgaaacgccgccgattgacttgcacggtttgcagaacaggatccagttttaagtgctgtcttttgtgttcctgcagtagctgtgagggagggaactctggtccatgcaaggaaaaggagcagatagctccctgttccaacaagaacagtttgtgcaattaagtttttcacagccaccttgcatgatagatagtaggctcatttcacggaacttttatcaacgtgtaaatccaatcagcactccaacatcctgtttattagaaggtttctgaaatcatagctccattgtgtgtgtgtgtgcgcacatgtggatgtgggttgtcattaatgtgcctgcttcccactgcagcccctgcttgataaagaatgtgaagtatgagggtggagcatgactgaatttttctgagttgagaagcccggtcagccctccctggcatggctgagctaagcgtgtgtggtcccatttgtgtgtcacagttttggtcctttggcaatcaggctgagagtattttgtgattctgcatcaagtttatctgtttaatattgtcacctgcattttaagggcctccttttgcattaaatagctgtgtcaagcaatcaactggatttctttgagatgtgccaagtgtataacactgaactaccaatgtgaccatagaaatccagatacttgtacatcataaagttgtgtattataataaaatggacaatattgataaatttaaatgtgacatgtaccatttgccccatggttttactttattgaggataaattattactgataaatttgttttattattgtgcaatttgttgttgtttatcctcactggattttttttcattgatatttttagaaagagtggaagggaggggtggagggagagagaaaaatatcgatgtgagagagacacatcgattggttgcctgcccaggtatgttcccttgactgggaatcaaacctgagacccttcagtgcacaggctgatgctctaaccgctgagacacactggccagagcatcttatgcaattttcaaaatactttgtggtgagttgtgagagagcagacaagaacaggaattaaaatcagggacgtagacccagaaggatcatgcagtcaaatgtttggactgttaacaaataaactatacttgtaagtatgaaataattttaaatcttatcatgttatgttttcaaattttggtggtagagtggagttttagtcaatgttgctactgaattatttacaaataatatgaaaataccaggtgataactacaatggaatgtgtgacactgtagctgaatagtataacacaccactcagatttcaaggtcacctgtgagtcatcttttggggacaatttccattaggattttaatttttgtcatctgagtcttaggaacccaggcaatggcttcatgggtttgcttatggaaactgtctggcttgggccatctgagagaaatttttatttgtaatgcttggtttaggtctcttgattaaaaaaaaataatttcattttatttcattcgttgggataatttttgtattgaaccttcccctctcacaagtttaaagtataatttacatccagtgaaatgcacacatcctaaatgtatacaaatatataagtactagtggcccggtgcacaaaattcgtgcatggagcggggggttcccttagcccagtctgcaccctctccaattggggaccccttgggggatgtcccacagggatttggactaaaccggcagtcagacatccctcttgcaatcggagactgctggctcctaaccactcacctgccttcctgcataatcgtccctaactgcctctgcctgcctgattgcccctaacccctctgactgcctgcctgatcacccctaactgccctcccctgccggcctgatctcgccccctacagccatcccctgcagcctgatctcgcccccaactgccctctcctgccagcctgattgcccctaactgcctctacctgcctgataatccctaactgccctcctctgctgtcctgatctgcccccaactgctctcccctgcaggcctgatcttgcccccaactgccctcccctgctggccaatttggttctgattggtcagtttctatgccagtcagcatcaaaagctctgcctcctaggcagccattggctcctcactgttcacccagatttggttctgattggttagtttctatgccagtcaacatctctgggcctatctccaggcctgatcagagaggtgggactgataagcagccccctcacggaggccttgagagaaagaggcgtggctgctggtgaagcccggggagagagagagagaggcaacagttgatcaacagctgccatggaggctgcagatcagaccgtgcctctctctctgggcctgatccgcagctccctcggcagtcagtgctgggttgctgtgcccgcaccggcggtagtcagtgctgggtcgccacggcagcccagcactgactgcaggactgctggtgttcagttgagccttcggtaggtcgttatggatcctgggtttttatatattaggatacttacaccatatgtatatgtactcaaatatccatgtgactaagacctggacaaaatgtaaaatgtcaccgtcagctgagaaagttcccctgtgcccctttcaatcaataagcccacctttctcagaagataaccactattctgacttctatcatcacaacttgcttgtttattaacctcacgtaaatgtgattatacattagctacccttttctgtctggcttcatttgttcaacataatgttattgagactcatccatgttggagaatgtatcagtaggtacacctggtattatcaatcttttacattttagccatatagtatatgtatatatatgtaaacattcttaattggtatttttttatggtttaaagtattacatatgtctcccttttccctgattgacccgccccccaaccactcccacccctgaggaaaagtccccaccgccccagtgtctgtgtccattggctatgctaatatgcatgcataatgcatacaagtcctttagttgatctctaaccaccgcccatgccacttgtccctggatctatttttgttcacagtttttgttgatcattatatcccacatcttagtgagatcatgtgatatttatctttctccaactggcttatttcccttagcataatgctctccagttccatccatgctgtttcatccatcccccagcccagcttgcaccctctccaatccagggccccttgggggatgtccgactgccggtttaggcccgatccccagaataaggcctaaaccggcagttggacatccctctcacaatctgagactgctggcttctaactgctcacctgcctgcctgcctgatcacccccaactgccctcctgctggcctggttgcccccaactgccccccgctggcctggttgccccatgcagcctgctgttcagtggtttggtcgtccctctctaacccccctgcaggcctggtcaccccacgcagcctgctgttcagtcatttggtcatccttcactaaatccctgcctgcctggtcgccttatgcagcctgctgttcagtcgtttgattgtccctcactaatccccctgccggccttgtcgccccatgcagcctgctgttcggtcatccagcccgttgttttggttgtgacagcccctggctttttatatattaggaatagtattccattgtgtaggtgtaccacagttttttaatccactcatctgctgatgggcacttaggctgtttccaaatcttagctattgtaaattgtgctgctatgaacataggggtgcatatatcctttctgattggtgtttctagtttcttgggatgtagtcctagaagtgggattactgggtcaaatgggaattccatttttaattttttgaggaaattccatactgtcttccacagtggctgcaccagtctgcattcccaccagcagtgcatgaaggttcctttttctccacatccttgccagtacttgtcctttgttgatttgttgatgatagccattcttacaagtgtgagatggtatctcattgttgttttgatttgcatctctcagaggattagtgactttgagcatgttttcatatgtctcttggccttctctatgtcctcttttgaaaagtgtctatttaggtccattgcccattttttgattggagtgtttatcttccttttgttaagttgtatgagttctctgtaaattttggagattaaacccttatctgaaatagcattggcaaatgttctcccatgcagtgggctttcttgttgttttgttgatgatttcttttgctgtgcagaagctttttattttgatgtagtcctatttgtctatgttctccttattttccattgccctagaacagccgtgggcaaactacggcccgccggccggatccggcccatttgaaatgaataaaactaaaaaaaacccaaaaaacagaccatacccttttatgtaatgatgtttactttgaatttatattagttcacacaaacactccatccatgcttttgttccggccttccagtccagtttaagaacccattgtggccctcgagtcaaaacgtttgcccacccctgccctagaagctgtatcagtaaagatattgctgtgacatatgcctgatattttgctgcctatgtagtcttttaagatttttatggtttcccatcttaaatttaagtcctttaaccattttgagtttgtttttgtgtatggtataggttggtgatctagtttcatttttttttttggtatgtatttgaccaaatttcccagtaccatttattgaagagactgtcttaactccattgtatgctcttgcttcctttgtcaaatattaattgagcataatggcttgggtcaatttctgggttctctgttctgttccggtgcaaaagtatatttgtatacaaaagtatgaaggttaatttcatttgtcaacttgactgggccatgagtgctcagaaatttggttaagcataattctggatgtgtctgtaatggtgtttattctctgcatgagttcagcatttgaatcgatagactgagtaaagcaaataaccttctttaatgtaggcaagccccatcgaatccactggaggactggatagaataaaaggcaggccaagaaagcattctttctctctgactgcctatctttaagctgggacatccatcttttcccgcctttggacttagacttgaactagaatttacactagtggctctcttcgttctcaggcctttaggctcaggctggaactataccctcagctctccttgtgtccctaccttttgactgtagatcttaaacttctcagcctccattattgtgtgagctagttccttataataaaaccaaacaactaatctctctctctctctctctctctctctctctctctctctctctctctctctttctctcaatatatatatgtacacacacacacacacacacacacacacacaaattcattctttctttattataaagtgatctaggcttactggttatttctgcctccttttcagatgttatcttatctacccatttccctgatgttgtaccatgaaacatacaagacaatggctaacaaagagtctttcttgcggggaaggaaggaggcacaagctaatacatattgtctctgctgcaaataattttaaaaataacatcaagatgctttctgataacagttaacaatcactttttatgaagctacaaacactcagctctccacattatgagactctccaagagtagtgttttctaacagatattttctgaagtcatttcctctagaggacacaatgagtcaccttaaaatggctcaaaactcattccctcctctccctcgccatgaaatgacttacaacatttcattgttacttcaactttagggacaacctttcttagtattattactgtgtctggtatccaattaaacaataaggagtaatgtagaaagaattgcccataaaatgtatttatttttctgaattgaaaattaaaaataagagtttttgaatgtgctcagtgagagtctatacctactttgcttccaatatttgagttcctctataggactgtagggacacaactagatgctgatctgatgcccagagcagagtggcagggtggagggacgcatgtgctgtctatgtcactggcaggtcagctttcctctccctgtctccagttgcaagagtgtggacaggcttgtatgatccagcgcacacctggatactcacatctaaagaaagagaccagagctttcctgtttgtttcactcatcaagtcagagtcagagcttcctgttccctatatactttggctgtcactgacaggtctaaagtttcatggtgatataatcctatctaataaaagagaaacatgcaaattgaccataaccccaacacccttcccaggatatgcaggatatcagcaggatatgcaaattaactgccaaccaagatggcggccagcagccacgcagctgaagcgaacaggaggcttgcttgctccagtgatggaggaagccaacattcccggcctgccatgaccagcctctgagctacactctaagcaactatgttgcaattatagaagctaaacaagctccagagacctgctttcagccagcttaggcctcagagcttagagctgcagtgatggcaacagagtttcaattatagaagccaaacagacccagaacctgctttcagcccccagagctggagcctgctctcagctccagtgacagctatacaaggtaaataaatcccagaataaaaaaaaaaagaaaaaaaaggagaggctgggagcttaagttgccctccagcctgaaaatggctctcaggccctcacccagactggccaggcaccccagtggggacccccacctgaagggggtgtgaccagatgcaaacagccatcatcccctcatgcaggctggccaggcacccaagcgggacccccaccctgatctgggacactttcagggcaaaccagccagcccccacccgtgcaccaggcctctatcctatactactggcagggcgggacagctcgagctgccatccgggccccatgtgcagccctgggtagtggggcataagtcctgcccccagccgggatccctgtgtgccacctgatcccccagctgggatccccgtgctgcctaagccctgcactgctaagccccgcccccagccgggaccctatgactattggagagcaggaaagtgccgctgggccctgggtcactgtgttgatctggccatcggtaagtagaaaagtgcagcctgtaggcagcccccagctgcacctgatctgggagcaggaaagcaagcctggcctgtgggcagccccaagctgggcctgaaacgggagcaggaaagcctggcctgcgggcagcccccagataggcctgcttcccagtcaccatggggatccgggaacaggagaacaagcccggcctgcgggcagctccaagctgggcctgctccctggttgccatggagacctgggagcaggaaagcccagcctgtgggcagctcccagctgggcctgctccccggacgccatggcgatccaggagcaggaaagtctggcttgcaagcacccccatcctggccatagctcaagggagagtacaacatgcagtggaggcccggagcctaagagatcaacacagaggggctcctgctccaagcctctatggtgtggctgggggcaggcccccagcagacacacacacacacacacacacacacacacacacacacagtgcctgctctgagcctccaatgtggctggggacagacccccagtggggacacacacacacacacacacacacacacacacacacacacacggcaattgctttgagcctacgtggcgtgccgggagccggtccatgcttgctgtttcaagggacctggcatatatggcatactgttcttaatatgtttgctcaccttcttggcactatgtgttttaaccaaggtctctgagaaaggttgtttccccaggtagggatttttcactgaagttagggagggaataaaacctcttaactaagtgccaggcgggtaattaatcactttaactatgaacaatcatgcttaagctacataatctttactccctggaatggagataagaaacgccctaacctttggaatagagattgataggattggaatcaactggtataaatacagatgtaacaacacagaacttaggagacagaacttagaacacagaactaagaagacagaactaagaacacagaacctacacagaacctagagacagaagaactttgctggagagaacatggcaaaagatcctggactgaacctgactacagaaattggcaagagaacctgactagaacctggtgactgagcctggctggagaacctggacacaacctggctggagaacctagtgagggaacatggctacagaacctggctggagaacctggagaacctagcaagagaacatggacacagaacctggctggagatcctaaccagaacctcactggagatcctgaccagaacgtggctagagaaccttgctatgatgatcatctgaatgccctctccgtgtcattccttcttcgctgactccgtccacacctttggggacccctggacccgctggggttggaccccggcagtggcgcggatgggggcaggccctagcagacacacacacacacacacacacacacacacacacacacacacgggacgcctgctccgagcctctgctgccagactgtggccatcagtaggacatccactgagggctcccggactgtgagagggacaggctaggctgagggaaccccacccccccagtgcacaaattttgtgcaccaggcctctagtcctatataataaaaggctaatatgcaaatcaacccaatggtagaaggactggttgctatgatgcgcactggccaccagggggcagacactcaatgcaggagctgcccccctggtggtcagtgtgctcccacaggggaagcgccgctcagccagaagccgggctcatggcagtctgacatcccctgagggctcccagactgcaagagggtgcaggccaagctttgggaacccctccctgaatgcacgaattttgtgcactgggcctctcgttttatatgaaaaggaaacatggtgtgtaccccctgaaaagccttcatgtcacaaaggaaaaatgccgtgaccaaaaaggcaaatttgttccatgcaagttgtctactgaaatcatttcctatttccaaatgtcttcattaaaacaaaacagataataaatcacttacacatctcccaacaatcctctcaatttaaaaaaaaaaaaaaatagagccctggctgatgttgctcaatggttggagcgtcggcctgcatgttgaagagtctcaggttatatttccaatctagggcacgtaagttcccttgggttgcaaatttgatccttggcctcggttagggtgcacacggagggaaccaatcgattgtctctcttacatccatatttctctctgtccctctctcaccccctccctcccactatctctaaaaatcaatgggaaaaatatccttgggtgaggattaagaacaacaacaaaaaacagtaaaaattcagtcacatgattttccattttgaatgaattaatctaagtggatgggaaacccaaatgcagcagagtccaggaggccattttcagttagctccattcaaaatgattaggaacactttttagaaaagtcttctttttgagtaatgccattggatatagaaaagctgctttcccaaaatgaaaaatcattggtaaaaataactgacatttatcttccagcatgatttgatttttaaaaaaatatattttattgattttttacagagtggaaggaagaggaatagagagttagaaacatcctgcacaccccctattggggatgtgctcacaactaaggtacatgcccttgactggaattgaacctgggacccttcagtccacagaccaacactctatccactgagccaaaccggttaccaccccagcatgattttaaatagcgttcctatatgtcttaatttattaagaaaagcatcaataccaaaatatttcattctatatcataaagtagaatgtacttttaataaaaatcggaggaaaaatttcagatgtttaaagaaaatgtagtttgacataataatttggagctctatccttgaggtgaaaaatgaattttactaaaagcaatataatttttttctggaaattaagcaaaggattattttttttttgtggtctcttagccaacggacaagggggaaaagaaaagaactggtctattctttttagaatcagagagaaggcaattgaaagttccctgtgaaagtaattttttttcccctcaagggctcttgagggttcacactgaaatgtcaatgaccttgtttgttcagagtttgtaacagccattttcaactgccctctgtgcttcgataaatatgttccagtcattgtaggttctgtgtctaaaacctatcaacatcattccctaagcatcaaggggatgctttttatgacaaaacatgctttatgcaggactctcactaaacccatgtgattccgagaacatccatcccaaagtgcagcgttaagaaattacacaatataaaattatttggaggcagttctggtgaattattttaatagtcctaaggcttggaaattccagacaatctcaacaaaccatacaacggacaactggcaaagatggtctatattttttaagaaaagccaagttattatttcaccatcttttcttaggataggtttcaaaattctgatcaactctagaccaccaggtagcccaacttagaacagtttaccagaagggccttacttatttctgctacttagtatagaaaatttttatttgagtgaagtaagcataaagagactttgactgatttgggattttcctcgtcaacacatattaaaaactaaaattaaattatatatattttaatttattttctctccatcataatcataaatgatagctactatttatggaattcttctattcctaggctttgcaataatactgttccatgtattttatcacttaatatttacactgatgtgtgaggtgttccacaatgatcttttcttatcactgaagaaattgaaaattagagaggttaagtaatttatccaatgatcttgctcaagaactccaagacacacacacacacacacacacacacacacacacacacacacacactcatatatattaaccactctgtcatattgcttagtatgtttaactaggggcccagtgcacgaattcatgcaccttgaaaggaactgtgggctgcgaggctgcagtgggcacaggggcgggtctcggctcatcctccatgcccttgcctggactctcccactgcacccccagtcccttgtctgccagcagccctgctcccgccaccaccactccagcgcactgacagtgctggccctgctcgtacccactgaaggtgcggagcaattgggtcctggcaccagcaatgggtatgagtagggctggcgccatcatcgggtgcaagcagcagttgctgccctgttcgcccctcaggagcaggtggaggtggagaagccctcaggggcaattgggattggcagccacagcttgcacccactgatggcaccgagagattggggctggcgccgggtgccagcagtgggtgcgaatggggccagcactggcagcaggtgcaagcaccaggcaggaccatggtacacaagagcaaagaattttcagtaaccaccagatgcttgcctcaatgacagcaaccggcgccctgccttggtctggtgcccccactcacctgctccaccatcccgtcgcagcggatgcctgccatgttttgcgcacgccccctggtggtcagtgcatgtcatagcaactggttgttcggttgttcggctgttctaccatttggtctatttgcatattagccttttattatataggatactgtcgtggaccttttatgattattctacatcagcaggctataaataggctgcagaggatatggatgtagaactgtaaaaaccagaaacacaattctctttttccttctttttcccagacctcccttcttcctctcttttttcttctatgccacaaggtctgccctcctatggtttgaaatataattgagaagacaacatatgtaattctcaaaagtaaaacatagaaaagtttgataacattgagtaaagatggagaggaagtaggtaatcatatttttttaatttctgaaatattccaatagccaaataaatgaatcattctcaaaaataggaaaataaagttttaataggaaaagagaatggagtaactgggggaaatcacaatgtgcctttcttatagtttttaattcctttaagtttattgaatgattcagattttctattcctttttatgtcagtttgagtaagttgtattttttcctagaaacttgtccaatttacctaaattttcaaatttaggaacataaagttgttaataataaatatcattaaatgattttataataataaaaaaaatatctgtaggctctgtagtgagatatccctttccatttctggttcattttcttgaggtacaggacattgtatgtgaaaaaaaagttacaattatttgaggcctagaattatcttcctccaaggaagacttacatttgcctttggtaggtggttagggccccagctatcttagatcaccttcatctaattatagggactgaacccagctggcatggctcagtggttgagcatcaacctatgagccaggaggtcacagttaaattccccattagggcacacaccgggtttcaggcttcacctgcagtaggggtcttgcaggaggcagctgatcaatgagtctcgtcactaatgtttctatctctctcatttcctctctgaaatcaataaaaaatattttttaattatagggactgtgatgatttaaactttagtgcagctccaagttttttccagttactccttactcataggtggagccctttgggatcccatctagatgtgtgaacatttaccagagttcactcctgaacagaactttgattttttgctaaattatatatatatatatattttatttactttaaccatctagccctatgggtctgccaaaacctctgccgaaattctaccttaatcatctcttccagaaccatcaaccaatcctaggattcacctctctggtttccttttcttctttatttcataattctttactatcttggtggctataagatttaaatttttttatcttgctttgttaatttgtcttagcagaggtagtttatgttatgtgcatgttttgtgtatttttaaaaaaatattcctgcaaagcaaaggttcttcacactttgttttaaatattttaccattgatctgtgatttcttggggtcattttgcatatgttcatatttttccccataatatggaagttcattagggctatgacaagtgctgagatggctgggatcacccaattggtccaccagctggtattagaatcaacagtaatactaggagcttctgaagacttggttatcatttgtctgtcctctggacgaagctcccgagtgctatatgttttggacagttctctggcatctgtagagttccaggtgtcctcaaagtttttggtagcatcacctccagcttgttccctcaggacttctgccccaccaggatgctcctccaaaaatctggtcaaatcatgcaccttttggtgcaggatcggtcaggtgctcctgctgtggctgtgcttctggagcccttccagggtgtatgtctgatggccttgggaacgaggcaagcctctggccctgcagacaccagtgagctgggcagaggtaatagctgcaattttaaagcccactttcagaggaatgaacacaatgtaccagctactgagtagcatcatttttttttttttttttttgagaagtgaggttgtaaacttagaacaggataaaacatcagtgagagagaaacatcatcaattggcagtttcctgcatgcccccgactggggactgagccagcaacctggcatgtgccctgactgggaattgaacctgaatctgagacctcttggttcatgggttgacactcaaccactgatccatagcggcaagactgtttcttaacatttttgagcatcattctgagatcaaggatgttgggccgatgcaaattatgatcatcatggtaaagcagtgaaaattcttgattaccccgctcataacataaattcactcagtggttactcatacttgtttagaaggttccgctatagcccatgtattttggtaatattagcttcagattgttttggaaacttattcgggaagatctacctttcttgttcttttatatctctggatctaaatttcatgagtgtacttctctgatttggtctttgcaaataaaagcaggttgatcacttgcatttacctgtatagaaatctggtttggccttgtgatctccaggtaggcccaggagtatctttaactgtcacctttatcttcattaaatggtaataggtttttgaaaattgctcccaaaggagtcctactttgattgctggaatacctgaagttgtagttgacttctctgtgttaaataagaagggattgtgaaaaatagtgtaaaatgaagaaatttgaatgcaaatataataatttactttggggagtgctcacgatttatttttttttctttaggaactgatcattttttagtttctagtccactgcttctgactttacatcataatcccaattcacaaacacataggggtgcacccaccaccaccacacatttatatcctttccctcccccgctttgtctcactaaagtgaagtatggtagctatggttcttgtctgcctatcatgtctttccttaatccctgctttctagaaatagaaatcctctttctcataaatttattccatgtggtccaagtggacagtcttctcagaatcgttagcaatggggacaggaagcataaagctactagtgcccatcttggcaaggctgtctgacaataaagccaatgctcaggtaagagggcatgggctaaaaggagagtgagaatgcctgggtgatatagtctttttcagcttaaatttttttttttttttttttttttactatttttgtaagtactcaaatagtcataaaatcattctatgcacataaactttctcctttttaaaaatatattttttattgatttcagagaggaagggagagagagagaagtagaaacatcaatgatgagagagaatcattgactggctgcctcctgcacaccccctactgggaattgaaccgtgaccttctagatcagcgttcaaccacggagccatgccaggtgagctcttttgtagctttaaaatgaggggttggtccacagtcatgtcagtggctaccatacactgaaatttcattttatgctaaactaggggctcagtgcacaaattcatgcaccttgaaagaaactgtgggccgtgaggctaccataggcataggggcaggtctcagcccatcctccatgcccccgcccagcccctcccaccacagccccctggtcccctgtctgccagcagtcccactcctgccgctcccatgcactgacgatgccagcccctttcatacctgctgaaggtacggagtgattgaggctgatgctagcagcgggtgtgagcagcagctgctgtcccgatcacccccaggagcagggggaggtggagaagccctcaggggcgatcagggccagcagccacacttgcacccattgatggcattgagtgattgggactggtgcagagcatcggcagtaggtgtgagcggtggttccagcactggctgaaggtgcgagcagtggctccggtgctggtagcaggtggaagctctgggcaagaccacagtgcacaggagcaaagaattttcagtaaccacaggaggctcgacccgatgacaatgaatggcacctcaccttggtctggtgtcccctgctcacctgctccaccatcctgccatggccaacacccaccatgttccgcacatgccccctggtggtcaacgcatgtcacagtgactggttgtttggttgtttggttgtttcaccgttcagtctatttgcatattagccttttattatataggatattttccatgcattaatttcatttaatattaatgacaactctatgaggcatatgtttttatctttctcccactcctataactaagaaaactgaataccactgagaaaagcaatgtagtgtaagctagtaagagaagttatattcatcattttaagtttgtttctatcagtgtctggcttaatggtgagatatcaaaaataattgatcaagtaaatagataaagcacgatctctttgacttcaaagtccatgtccataaccaccatcaataacttaaaacattattaacttgagcaactgtctaaactcactgagtggtatctaggtcaataagtctatgtttctaacaaatttgaaatgattaaagatagaagcaaacatgtacttccctcaccattcctaaacacaaaactgaatctacccctggttctaatacaagaataggtgaaataggctttggggtttggcctgagacgctgaccaccctctggaacaaaaaaaaaagcattcaaaagccctagccggtttggctccttggatagagtgctggcctgcagactgaagggtccttggtttgatactggtcaggggcacatgcccgggttgcgggctcgattcccagtagggggcgtgcaggaggcagccaatccatgattctctctcatcattgatgtttctattatctctccctctcccttcctctctgaaatcaataacaataaaaaaaaatgcattcaaaggtctaaaaaaaaccccacaaaaaacaaaacccacagatttacaagcaaacttttgcaatcttgccaactcgaaactgaacacagagagagaaacatcaatgtgagagagacacaccaactggttgcttccagcatgcatcagagtgggggcagggagcaaacctcaaacccaggtacacgcccttgaccaggactcccagagatactatttaagaaagcattctagatgcagtgcagaactttatggcaagaaaactcagtgcttatttagtttctcaaatagagggatttaggttgtttgtgaggtccagtttctgtgcttcaaaaaataacacgtgacttagagaggttttgagttcatatcaaaacatggatcatgatcctaatactcagaaccagaccatttctgtatttataccacataaacagattctaagaactctatccccaacacttaaatcaccggtctatggaattgaaatgaaaattagaatcaggtgaaggtttatgtttaaggaacatctgatccaaaacaaaatgaaacttaaggcttaaattctgttcttcctgagattctagtttctccctgtcattgcaattggccaaaactctttgtgtgaagaaccagctctttggaaagcttttaggaaataactttcagagactgtgttctgctaacatgccatgaaccatagctgcaggaactatgaccaggtctccaagtcattttatgcctcctacttggtgtgggaattctatgatcaaatcccttagcgttagtttggttgccaaccctggtcccttccaatgaaattgtggtaattagagtatgttaaacagtccagtggcaccaactgtgtagtttgtttggtgaaacattcagggcttgtactgacaagtccagagaggtgttaagcataagatcaagaggacaagtaggaactggagaagaaatctattgaaagctaattttttttcaggcactttgtctggcttttcatatatgtgtatatatatcctatataataaaagcctaggtggcattgtgcaacgtcctcacatgatatcatcacaagatggtttccacgacattgtcacaagatggccaccacaagatggccgccataggatggctagcaagggagggaagttgtgggcaatcaggtcggcaggggagggcagctgggagcaatcaggcctgcaggcgagggaagttgcgggggaccaggcctgcaggggagggcagttgggagtgattgggccagcaggggagggtagttggaggtgatcggccggcaggggagcagttaggcatcaatcaagctggcaggagaagttaggaggtgatcaggctggcgggcagaagctgttaggggcaatcagacaggcaggcaggtgaatggttaggagctagcagttctggattgtgagagggatgtccgactgccagttgagggattcacatggattaggcctaaaccggcagtcagacattttccacgggggtcccagattggagagggtgcaggctgagctgagggacaccccccctcctgtgcacgaattttgtgcactgggctatacatatatatatatatatatatatatatatatatatatatatatatatatgtctcatctatttattcttccttaaatgtcctagaaacaactactcctgcacaattctggatgctaacaccatcagaaagatatttgtgatatttgtgcctggctggtgtggctcagtgattgagcattgacccatgaaccaggaggttacagttcaattcccagtcagggcgggctcaaaccccagtaaggggcatgcagaaggcagtgatcgagggttctctctcatcattgatgtttctctttctccctctcccttcctctctgaaatcaataaaaacatattttttaaaaaataaataaaacagagtgttttctggtttttgttgttgttttgttttgttttttgttttttttaagatacttgtcctcatgtgcctcagtcaggagacttctaagatcatgaattggctgctaatacctagcccctctctctggtctaacccatagactcagatcatctcatttcctcctggggacacaaacactccataaaggcagctcttctagggctctgagcaggaccatcctgacattgttttcactcctcttccagcctgcaagcaaatgaaaaacagctccatctcacaggagggctggggaaccttgataaagaacagatctccaattgccttttagactgaaataattggacctgttagatatttttgaaagatttatttactgcatttcaacaattaaggtgaattttataggcccatctctcgtgggtatatttgttttaatcatgaattaaagactataccctggctgatggctcagttggatggagtgtcatcccgaacaccaaaaggttgtgggtttgattcctggtcagggcacatacccaggatgcatgtttaatcccccgtcagggtgtgtctgggaggtaaccaatccatgtttctctctcacatcgatgtttctttctctctctgtgtcccttcctctgtctctaaaaatcaattaaaaacatatcctaaggtggagattaaaaaaaaaaaaaaagattagcgcacatttcccccctgcacaaacatcatgacccataaggttaacgcatgaaatcctttcatcacgaactaggattacactctattccaaatcaagccacgtcaacatagatgatgccagaccaccaaacagtgttttctcaggctctaaaagggcagagtaactgtcacccgtgggccatacgtgtggtccggtgggaagagtaagctttggaggatggaatccgagctcacgggctgggaggtccaggtccatcacttaatctcccgggaacccggtgctcagtgtcaggaggctcttgactcacaacagagtatagtagacaacaaagagagactatacattttcactggcttgaaaattcctagaagacagggcctgcgtgtttagcttgtagcaacaatgtttatcacttatctaataaatcctatattaacatgttcatcagaaaaaaacacctaaatgtatcgacaatcaagtatacctgttaagattctccttgcaaaagccacatagggaagacaaaacaaaactcaattcacattttccaggggacattgttcagtgtcattgaaacaaagcaacatctgttttcctagaagttactatatatgactaaatattctaattagtatttctcttaggccataaatattttaccataattttaaaaacttgataaactaatgtaaaagatggctctttgaaaaaaaattaataaaacagacaaacatcacagcaagattgattaggaaaaagacaggaaaaaacacaatgctagtatataattgcattagaaataaaaaaagaggctacaaatactgtgcatatttataaaaataacttgagacaacaaatataaaactaaaatttttctaacatatataccttttgtttttaagaagtgaagcaaggcagtgctgtggtgaagctggtatagttcatactgtgagggcaggttgtttaccctctttaagcctttgctcatttgtaaaatggaggtggtagtgtcctcacttcttatttacaggattattatgacatcagtggaattaatactgtacatgcaaaagaaatttaatctaaggaagagatgagagagagagagagagagagagagagagagagagagagagagaaagaaagagagagagaagacactagtagatattgagaataaatatgaatgaagagtctatgaatcaggctgacttggaaggaaagaatggaaatctgagtagaaccaatgatgactgagaagtatagcttttcccccagaaggtctaccactaacttcaccctcccagacaccagcaggctcatgtcgtaaggagtcccggtctcttagtttccttgactggggcagggagtccttaactcacttggacaggagaagggaagcatgaaaatattccttgtgttcccagtagtttcttgggcatattaagttagaaagacattattaaacatagtgttgggtgctagaatatttctggtgatgcttaagggacatggtaaataaaacctgagagtgaattctggcttgcagctgattatgggatggtattagatgcctggcatcttaggggttaaaaagtctttggtaggccagccagaaagtttaataagcatgtaaaacacattccaaacaaatgactgctggaacataaccctgtaagttggggactgcctgtttttataacctttgccaaagttactaaaattagcagcataatcgaaataaaaaaattatatattcttatgactgaacagcatggcagcatcttcaaacccagagctgaattttaaagtcaaaattattttttgggcattttctagaaccaaagagaatgatttggcaagccatcccctctggcaatatgaatggagaaattttgcttttatctttataattaaagtagctctgtgcagtttcattcccagtgtgaacgggggcacaggtggtatctgctgatgttacaaaacagagaaatagcaaatatgttaggtaaaagacacaaacattctgtttctcacccatctccttgatttctcaaaccaaaatgctaacagttcagaggatcattttgtatatgtaatggaggaaagcactggaatgtttagcttagaaaagacttcttataccagggaggaggaaagtcattaaatagaagtgacgttctttctgatttggtctatggcagtggttctcaaccttctggccctttaaatacagttcctcatgttgtgacccaaccataaaattattttcgttgctacttcataactgtaatgttgctactgttatgaatcataatgtaaatgcctgatatgcaggatggtcttaggcgacccctgtgaaagggtcgttcgactgccaaaggggttgcgacccacaggttgagaaccgctggtctgtggggatttagtggacacatctaagaccaatggtggtagattttctccagtgtaggaaaggttaacagaattcttcaacaacgagatagtaagtttccttcctttattcaaaggacgactatcatgaatcaagtacaggagattcttacactgggtagaaagttaaagcagatggtctacttttccaatactaaagttctgtaagttcatgaaaatcataataactagtccctctttctttcccaagcaatatgatatcctcaaaattgccaatgaggaaattcaagtggatgctaagaccccagttggtcatacaattgtgcaatcaagttttaagtgtttctgatggagccagccaacctgagactgcccatctccttactcatggagatgtagttttggaaaccaaaatctgtagtaggcaaaaactggtccaagccatgtggctttccaacagggtatgattataaaagaggccagaattattgtagctcagaaggggaaatgtcagcggaatttattatccttataaaacatattctttgatccttccttctatcgtattgcacgcctgtttcttggactgactagtggtcagtgaggtcaagataatgttgtatagatcacattgcctttattttggctttagagagactaagagccaagattagtctctttttagaatagttattatttaggagatgtaagtttttcattgttggaagccatttggagtgctctaataggtggcagttgcctctgcctgatctttgtaagtgatagcaccagggcgttgcagtgtgctcaggaagaaagtcacaatggaagtctatttgtcttctttacaaacccaccacacttgtaattattttttaaatatatttttattaatttcagagaggaagggggggggagagagagagagagagagagagagagagagagagagatcaatgatgagagagaatcattgatctgctgcctcctgcacaccccctactggggattgagcccacaacccaggcatgtgcccccaaatctgggacccttcagtccacaggctgacactctatctatggagccaaaccagctagggccacacttgtaatttttttttttttttacatattttattgatttttttacagagaggaagggagagagatagagagttagaaacatcgatgagagagaaacatcgatcagctgcctcctgcacatctcctactggggatgtagcccgcaacccaggtacatgcccttgaccggaatcgaacctgggacctttcagtccgcaggccgacgctctatccactgagccaaaccggtttcggctaattttttttatcaacattggccgtccatctggaatgcaatttcatgagagtaaggccaaattatcttgtgcagcattccatttccattagctagcacagtgccagcactcaaaaaatatccaattgaaacaataaatctagcaaggaaatgatgacccgctccctccaatgctctttatatcttccacatcaattcctctcccccttacacacatggggcaggatagtcctaagaataaaattatattggtttgtatattgtctacttattccataaacacctatgcataatatttcatttgatctttgtaataatacttcaaggtagttgacagatattattattattgctattcccatctttttttccatatacaaaactgcaggatttaaatgaccatttgagggttaaagtgctagctagaggcaatgctgggtcttgatctccaggagacaaagcagtgtgtgtagcaatagcaggactaaagcctgtggaattccaactccatctgttatctatgtaacctcggaaaaatcctttgcatttgcttagctttagttttttaaaaatatgtctttattgatttcagagaggaagggagaggggagagagagagagaaacatcaatgataagagagaatcattgctcagcagactcctgcatgcccccaactggggattgagcccatagtgctcaaccactgagtaaccatggccaagcagctttagtttttttaaaaatatatatttttaaatggggctaaagaggtaaaaagggaagggcagaggcaatatttatcttgttcacctcatggaattattatgagatggggataacaaaggtcgtagggtgtaactgtgagatggtgtcactctaagaagaataatacttcccaacagagatgaatagatggggctttggggcccgagggtccgagcaccatgcctttctagcggtatgacctcaactgagttcctgaacctttagtttcctcatctttaaaatgggaacaataaaacctgcctcataaggctgacatgtggattaaatgagattaagtacagaaaatagaagtaactaactgtatgataatagtggtagcagaataatttaaatagtaattttctctccagttgaagaaactgaggtttagaagcttactagatttggtttcagggtacccagagagcagagcagagacttggtagaccagtgtgaagtcttgaatttgcttcggaagaacagatctggtgagtggaagctggacgagaagaaacttttttcttttcctctccatcccaccagctccacccacctcagtccacaaggtctatttctttaggatttgggaaaacaggagccactggagggccaaggagaaaccagtacaagagcgatttccctcctttattgctgtcagaatcccaagaaatcgtagacaagagattggatgtttagtggtgccattgcttaaagcccaggttttccccaattactgggacaacatttccttccctggaggacctggtctgaatttctggcccccttaggcaactagaagtaaacctttgagataaaatcctcttgtatgttggtgctccttctgtgccgggagccggtccatgcttgctgtttcaagggacctggcatatatggcatactgttcttaatatgtttgctcaccttcttggcactatgtgttttaaccaaggtctctgagaaagggtgtttccccaggtagggattttcccctgaagttagggagggaataaaacctcttaactaagtgccaggcgggtaattaatcactttaactatgaacagtcatgcttaagctacataatctttactccctggaatggagataagaaacaccctaacctttgaaatagagattgacaggattgaatcaactggtataaatacagatgtaactagacagcaagacacagaacttagaacacagaacttagaataagacagaacttagaacacagaactaagaagacagaaccaagagagacagaaccaggaggcacagaacctacacagaacgttctctagagacagaagaacctcactggagagaacatggcaaaagatcctggactgaacctgactacagaaattggcaagagaacctgactagaacctggtgactgaacctggctggagaacctagcgagggaacatggctacagaacctggctggagaacctggagaacctagcaagagaacatggacacagaacctggctggagatcctagccagaacttcgctggagatccagaccagaacttggctggagatcctggatgggctgctgatcagctgaacgctgtctctgtgtctttccttcttcgccaactccgtccacacctttggggacccctggacctgctggggttggaccccggcacttctgctggttggctgaaagcttactatgacagagcaacgtgactcagaaaattctgaaataaacaaatagtagtcatttcctgggtcataggagaaagtttccaagaataaagaagaactgtagtcatgtgtggcatttgtattgcacaataattcatttaaaaatagttcaaatctcccaggaaagggagtagacactggggaagcaaagagcatccaaacagtcaaatagctgggccacttaactgctctgagcttctgattattctgtaagttgaggattctccgtcagaattgttttgagaattaaataaaataagataatgtaagctattcatacattctaaaccactatacaaatagtagcaattatttaaatcttttttcttctttccatgttcattgccctttacctctgttacagactttaacatattgcatgagagaaaagtttacgtccagtttcctgactggaatgtgattgactcaagaagaaaggctattacactttttatacaacccacaacccaaaacactgaccagcacatacaggtcagtgctttaataagtaccttttccctttaatttttttgaaaaccagaactaagtgatcctttctagtctaacatttatttccccaaattccctggaaattccacatagtgacttctgagatttatgagttgattgctttatcactctctgggcttgttggtgtaaatgaatttaaagtagcttctcgccctgaccaggtggctcagttggttggagcatcatcccatgcaccagaaggttgtaggttcaactcctagtgagggcacatacttgggttgcgggtttgatccctagtcagggtgtgtattagaggttgccaatcagtgtttctctctctcccccttcctctctttctaaaaatcattaaaactatgtcctcggggaataattaaaaaaacaaaacacataaaaaaaaaataaagcagcttctccaccaatatgtcagctgattttgatctgcaatgatctgttcaaagtgtaaagtttatgattagaccttccttaggctcctgtttgtcaaccgttttcatctgctaacttcacagtatcccctggcagtcaactgacccttcaattctccttctccactctgaacactacttcaaaaggtccccttctgtgggtgcctcccactaataatttatcttcaatttccagactttttttttttttttacttctccttcaccagtatctgacattggaatctggaaagagtggcagcgttaataataaaatgaagcagaatccttggacctgtcatcattaattgctttcaaagtctggcctgtctttatgagatttgtttctttatttcctctttgaaaacaaacctgggttattgaacatctgggctggaatttctgctgaagaaaaacattcagaggctcttgaagtctgtccaagctctcaagtctttttcgtaagtggatagaaccacttgcttgagaaaagaatcccccccccacccccttcccacttcagtgtttttttgtcatgtgtgctggtgagcaactttcattgtaggtatgagttacatatggggatagagagtgataggctaaaggatggacttttgtactgtattgagtgatttcatgggaagaggagtttataggtcttttaattttgaatcataagatcagatgagaatatatagttttttaaaaagccatgtatggatttgcaaattactcgtgattaagagagaaaaatcaggacatgttcaatctgtgttttaatcttcttattcatcacagtcactatttacttgtataaaacgtttaacaattattcctgttgcaggccactctcttaaagaaaactgccaccagttttgtgtttctagcatcagtgtatttaaaatctaacccagtaaaagtcacaacaggctgagcttcagggatagtttgttggccacatcttacatgcacctgtgttccactctggactcaaatgaaaggcctcccaatagggcatggaagccatctttccagggagccatagaaggggccagatgacacaaaggaagtgcagggagtaacttggatcaatgagaaacaggaggtgggtgagttccttctcatgaactgccgcaaagcacggccttcacagagcctgtccacaggtaccccgtgtggccgtgcagaggcacttgccacattacctgctgggtttctcctgtctctttgggaaggaagcacggcccccccccccccaccccccgccttacttctcttgttcttcactctcacattagcaagttaactctgttttccagggaacccaggataagatggctattggttaaaaagatctactagccagcacactgaaggttcacttcaatcctggataattttgtgaatctgctatgtgcaaggcactaggttaggtatcctaagggaagcaaaggtgaatgaataccattcaatccctgaccttaggtgccaatagtctagaacaaccgtgggcaaactacggcccacgggccagatccggcccgtttgaaatgaataaaattaaaaaaaaaaaaaaaagaccgtacccttttatgtaatgatgtttactttgaatttatattagttcacacaaacactccatccatgcttttgttccggccctccggtccagtttaagaacctattgtggccctcgagtcaaaaagtttgcccacccctggtctagaagataaaacaattgcaaagtcagcatcactctgaaagaggaattcaggactgttaggctcccctgagagcaatagaagccaatgatctgttacacagggaattgaggacctcatccacagtacattttataattattactctggctgctgtgtttaataagcacagaagaaactccatttatagtggctcaggtgggaaacaatgatggtggtgtgaactttgatagcaataacaagaatggaatcgagatagattgaagacatgtttagattggtaatgattatggatttgatattaggaatgagagagaagacagtatcaaggataactctatgctgtttgttttgaggatctgggaaaatgtgaactattcactgagatccctacacagaaagagttttggctaagaagataatgaggtcacttttagaaatgctaaatgttaggaatctgtaagctatctttataggcagatggataatatagacctagtgctcagaagagacctagcctaaagatctgaaatggaaattcatgagagtctcatttgatgtgtgaatgggagttgacaaggtggctggtggaagtgtggcctgaaaagagaacctaagaaacatcaagtaagtattaggtagaggaaaagaagcttgtaaagaaaaatccaaggaagtgcaaactgaaaagataaaaaaaaaaaagaaaggatgtgacagctctggaaatgtgccactctgatctccctccaagaaacagctttctgtttaactgtcaagaagacatttgctaccagcctctatctgtagcacatttgggatctgtcacatgtttgaaccaaggccctaatcttcctggtaagcccacagctaatggatgagcatggcaggggcactagggcctggctgttatgcccaatgtggggcccctctcacaagccgtcttcactaagagttccccactgggttggccaagactttgtccaatttgcagagaatgtgaggctgtatctaccaagtcctgcttcctctctctttatctttcacagatgttacctcctgcaaaccacatgcattcccaactctgtctcagcatttgctccagtataactgaacttggttaatgggtcatttgatttggtatgtagatgttttttcaatatgtccacctaaaatgaacatactgaatttggctgcatgtggccttgaaagacagcagcaagagaaaatcttcctaatgggaggatttcatggaaaagttgcctgagataagactacatatggaatcgtgagcaaaaggattgcccagctagtcaggggcctgggaagaaaaagttaataattttgcagagaaaaaaatctgggctagatgcttgtggatgaatatatgagagtgggtatgaaatgtgaagatcttcgtatcatatattaatgaccaccaaagcttggccaccacagaagaagcattgaatagccaagacaaaattgattagttgacagtagccatcttttgtcattggccgctccagaattagcatgattggcacattaacagaatgaacattacatgtcaatagcatggactcccctacttatcaaggctgatctactactactatcattgagagtccaaccagtttgcaagagagatcaatgtaacactattacttgaggggaccacatggccacttggtggcaagatgaccactttgagccttttcttactggaagggccagtgatttattgttatagtagtaggcacttattcttggcagcctagagcctcagctagcaccactatctgggagatttcaaagtatctgatctaggcatacagtctcatataacataccatctgatcatgagactcacttcacagagaaggagatagggaggtaagcccatggccatatcaagtgccacacaatccagaagcaattggttttacagagcaataaagtcacctgccaaaggcacatggaagtgctaacttgtaggtaatacttctgccatgataggataccatcatgcagattacagaatgatttacaaactatttggatgttgtgtcctgttagcaagaatatataggtctaggaaccaagggacagaagcagaagtggtcccagttgccattatggtcaatgacccaccacggaattctgtgcttcccatccccacaattctaggctctgcagggaactacaagctatgattcctacctggacattttggattccttatgttcagggacataggatggcagtaattgatgggagtaactgattctgatcaccaggaagaggtagtagacatttgttatataatgatggcagggggaaatatgtatagaattcaggaatccacctggccatgtcatagtacttgctcaattatgactaaaatagtcaattctagcaaccctagtgtgagaagagtatggttaccacaggcctagatctctgaggaatgagatcttggatcatatcagataagtcatcaaggccagaagggatgagagctggggaatttagaatagtcagtggaggatagagatggtaagtaccaattgtggtcctgagaccaactgctgctattgaaaatgaagatactagacacactataaaaagaaagcattgcatccccaagtcaccctacttttcttggtcatgagagacatactttctggaaagtgccaagtttcttatgaataaagggaattcataggtacgttagaatgtctattatctcacagaactgaatcagacagaacctgaacggatcctttcatgcttcatagagcacgtttcagagcttctggatttgctgttttagatcctggggatggaggctggttgtgaaatggcaacttggttatgctacctaagaagaccagccaagcattaagtagggacatctggagagtttggaaatactaacaggcttttcagccaaaatctgacattgaacagataaaatccttcttacagagttatggaccctattgggacagaaatacccaatagggtccagtcagaatttcggccactttacactacaaacagtaactccccagggataagtatctctggcagaagacaagaaatcctgtaaatctttaaaatgtcccttttgcatggaggacatgtgtttgctcttcttactaaggctgcacagccctcaagcctaacagctgctgaatggtccctgcgtgagagcttgacctctacaaactgatgtaatgcccacactgccgggtctgggtctcagacccactactgcattagtgataatagttaacttgtgatcaaagctttattctggtttaccaggtatttcatgtagagctgctagcattatctcgcgtgatcctcaaaaccactctgtgacgtagctgcagcagatctcattagtttgttcatcttgtgagttgagcaaatggtctcccacatagtgagttggtggagagctagaatttgaatgcacatcttcctagaaacccaagcccagagtgggctggaaagaaatagctgagaagtaagctctggtaaacagaaggggaagactactctatttctctcattgaaccttttttaaaaaaattcatagcaaattttctttattagcatgttgattaaattttttaatggagaaacacaataagacatagaagccaatgttattatgagaaaacccattacaaaaaggatactgtttgagttctgcagcttccctagaacagagaaaagtcctttgcataagtattggctctttaaagggtaaagagttaacggacaatgactcatgcattgctatgaagtcacctctttctagaagtgttgacgaagattgccatgggatgcaaaagggataagcggttggtgttcctcagccaactggctagaaacacttcactggccggggactcttaaaacctgtgacatggggaagcaatttcatggttctaagcctctgagaataagcatgggcattcttgctgttcagatggttagatataaggtgtaaggcatttagacatggagtgggactgctctctactccagtgtgtcagaaatgaatctgcctgaaacatcctgaaagtacctaaagcttggcctcttgacctgtgttttcccctcaggccacttgtctggctgcccacaccctttccatccctacagtgtgcatgaaacatgaaaacactgctaagtatgtaaaaaaagccaggcacagaggtcacatgtgatatgattccatttatataaactgtccagaatagacaaattcatagagacagaagcagattagtggttgcttgggcaatgggcagagaggggatggggagtgactggagagtcaagtgcagggtcttaccttccaccctggagcttttctgtcactatctgggaactctttcttcatttatacacaagaaacatggaaggttggtgcaattaacaccctgacccaggggactagaagctgatggatgtaatttccctttcctccctcctagagcaagggtcctgagatgcatagcatcggaattcacagggagtcccatgggatcagcaactatttgcttcgaagcagcagccgactctccctccctgccaatgtcattcccccatcctaccgttctgctcccccaaatcacacaccccagtaagaattgtcacacaggctctgatttctggagaacccaggctaagacattaactcttgatttcactcagactccacacagtctgttaggaaatcctgttggctttgccttcatcataaaatccaagattcaaccatatttcacctccttccccgctgttaagctggtccaagccattgtcatcacttactgattaacaaacacagttgccagggtgatcctaataaaatgtaaatcagttcatgtgattttttttgaactaaatatcctattaagcttcttatcccatttagaacatatacatatttaatgtcttatagtgtctacaaagtacatcatctgtcttataacttctctgatctcattttccataaccctctctggttcagccacatttctctctttattacttcttaaatatacacatttctcttcttgttacttctaaaatatacatgcatgctcctcccatgtactcctacctcgaggtctttgcatttattcagaatttctctcccaagattgatattcaaatgtcacctgctcagtgaggccttctcggaccatttaaaatgcaacttccagcattccttctccctctcccttaatttttctccattatacttatcaccatctgacattctatgtgtattattattattagtattattattatctggctatttccccctgctagaatgtcagatcaatgaggtcatgtattagatatcaccaatgcctaaaatagtacctgtgtatataggtgatcaataaatatttattgaaaaaaaagaagaatggtaggaaggaagaaaagctgcaataaaatgccacctgtccagaggtaaggtttctggtcatctcaaatccttggatggccacgtagccagaaataactgagaagagtgttgagaaggaaacagagcagtcacaaagcccactcaacactgttatgttcattgttttgtacagatcatttccctgaggtcaggtttctcctttcccgttcacaaaagatggaaataacaaaatacggagtgacagtagctggcagtaagaagagacgctttcaatccctgctctggagtaattacaataacactcaggagtgggtaaaattaagctaatgcctccaagatgtaagcccaaggcttcacatatcatataaatacagggatcaggtttttcaaagaaaaactgggacacagggaactcttcccaggtgcaagaggggtgtggaaaattgagttaagatgatgatgaaatatttgagtttctggactatcctgagggtaaaatattttaaatagggctggagcagaaaaatccagggcatatttacatgtcactacatgtaagagaacaacatttataagatggtttaaaaatcacgactgctgctttgtcattttaagaaaaaaatttttgaaacaccctgaatgttcttaaggattcaaccaaagtcaagcaaagtaagtagagtcttatacctaaatggaaccctttaaaaatccaatttgcagctttgaacacatgggaccatttcccccatacatttccttcaactgtgttttaattgcaccaatgaaaggcaaaatggtaattaagcatcccacccagcgtcagtgaaaatggaaacagggccagtatggagcatttaatgtcagatacgaggcatcagaaacttgtaaaatctgtttgtaaactactttaaaatacatttagtgaacttctgcatgaagagcctatttaagggctaagtgcaagacatcacgaggtaatacagtgtttcagagaaaaaatatcacatccgaaggtcaggcaaatttttatttatttaaaaatatgttttagtgttctttatcctatataataaaagcctaatatgctaagtgtctggtcatccagtttgccattcaaccaatcaaaacgtaatatgctagtgatatgctaaggctgctcaaccgcttgctatgacatgcactgaccaccaggggagcagactctttgactggtaggttagctggctgctggggtctggcggatcaggactgagagagatgggaccgacacgccctggagccctcccagggtccctccctggctggccaacctcccgcatccctccctgaccccgattgtgcactggttgggtccctcagcctggcctgcactgtcttgcaatcccagctgagaggacctcccaccccgagtgcacaaatttcatgcactgggcctctagtaataaaaggactgagtcacatttaataccaaatatacagttattaaacaaaaatgccaaattctcccaaaacacaatttcttgacttttttgggggtggggttaagggcacctctggaaatctgagcaaggttatagattcagcagtttacttgtattcaaaatttcatttcatcacagattcccaggaggactgaatggacccccacgaagcccatgaacctccaggtaattaatcttcatctgaagggtaataaacatactttgtacaacaaggaaattttaaaataatcaaatagatcaattcaagttaacaatttctcagctctggagggaaaaaactttttcaattgattgataaagcagataaaattcactcacttaataatatgttttatgtgtattactatgctatcagagctgaagtaagaaaatagatgccattttgctgcctgtaaagtcttgtatgatttttatggtttcccatcttacatgaaaatcctttagccattttgagtttgtttttgtgtatagtgtaagttggtcatctagtttctttcttttcttttttttttttgcatgtatctgaccaaatttcccaacaccatttattgaagagactgtcttgactccattgtatgctcttgcctctcttgtcaaatattaattgagcataataatggcttgggtaaatttctgggttctctgttctgttccactggtctgtatgtctgttcttgtgccagtaccaggcagttttgagaaccgtggcttggtaatatagcttgatatctggttttgtgatccctccaactttgtttttctttctcaggattactgtggctatttggtgtctttttttattccagatgaatttttggagagtttgttctagacctttgaaatattttagtggggattgcattgaatctgtagcaattcctttaccgacacagctcctagggcaatggagactaaggagaatataaacaaataggactacatcaaaataaaaagcttttgcacagcaaaacaaactatcaacaaaacaacaagaaagcccactgcatgggagaacatatttgccaatgctatttcagataagggtttaatctccaaaatttacaaagaattcatacaacttaacaaaaggaattcatataacttaacaaaaggaagataaacaatccaatcaaaaaatgggcaaaggacctaaatagacactttttgaaagaggacatacagaagggaatccactgtgacttttcaaaactttgtggggcagtagagttctattgccccacaaaattttcaaaaatcagtttgctactgttgtagatggttgtagtcattatttaaattagcataccactttacccatacttttaaatgttctcttctctatctcagaataacttaattcttcagaatagtcccaccgccttcctccccctaattgtttttaaggtcagattctccttgctcctgtctctatccagttcttagcctttgtggctcagtggattccctcttgagcatcacagacatcagaaagcacctaactaaaccctctgttttacccagaaggaaacttgggttcttctaaatccatgatctcacctaatttcaccatcagtgagaggggggtaaagagaatgcaggtttcctgactcctcctccatcgctctttctctgatgttaccctttctcctcatcatttggtggaattgacaggatgaatccaaattcacttccactgtcagagcctcatttctcttcaatgacttggaattctgccacctttaaagaagtccccgaagaagtgatctctctggccttctttatcctcacagcgaataatgagactagcatatttatgttcggtcattcactcacttaaacttttttttgaacatcaaagtagttctaaggttgaatgacagggtttccgtgttagaggagctcgtgtcctacttggtaggcatgcaaagaaataagttacagaaaagcatgccatgtttaaggactaggatagaggttgtgcacagaatgttggggggggggggggagctcacccagtctaggggtcatagatcaagagaggcttctggaaagaacggacccctaatctgtatctttaaaaaatatatgcttatattgatttcagagaaaggaagggggagggagagagagatagaaagatcaatgatgagagaatcatccatcagcagcttcctgcactcccctcagagcacagaacctgtactgcaacattaatcaccaaacaacattcaccactatttaatgtccctatcagctacagtcctgagaaaatcctgaagtgcaacttcatcattgcttttcatgtatctacattaataacttgtattatcttacctgaactaggtttaaccagcctaggttgttttactatttttttttttctttgtgctccatttttaaccacccaagaaagcctttttcctggcttttgtctgtaattatcctttcaatggtcattgatttaaatgcctcttaaggaataagcatacaatagaacttgttgacccttgtttttaatttggcaacttttaaaaataaattttatctattacagttgacatacatattacatgaggttcaggtgtacagcatagtgattagacatttatataacttactagaagcccggtgcacaaaatgcatgcatgggtagggtccctaggccaggccagtgatcaggaccaatctgtggggcgactggtggggtgatcgggggggcccccgctggcacccatcttggccggcctggtgccgctcaccagctagccccatcccctgatcgccccatcccctgatcaccccgtcgctgcagccagttgcctccctctgcagggtgacccacaggatgatcggggggatcccccactggcacctgcctaggctggcttgggacctgcaggctgggggcagctcctgcgttgagcgtctgcccccctgtggtcagtgcacatcatagcaaccggtcgtcccgctggtcattctgctgtttggtcaatttgcatattaggcttttattatataggatgaagtgataacaccccaataaggctaataaccatctgacaatttttggcatcatttaatatgcagataaaatgcattttatgaaattttacctctttattcctctcccatgcctatggcaaaggaaacttctggatccactatgttagtgggcagttaacctttgagaaagtaactcaggtacttcatacaaagttaccttcatggaaaatataaagggtatattttaaaaaagaatacctgactaaattttttcaactatttgatgaaccagcaagaccagctaagataatttcccattttggaagactaaaaggagaaatacattttttttgcaaatttttattacccaatatcaaatagaaaagtcagttcttgaattgctactaatcttgttcatacctgaggtaaaaggaagatttagcctgggcgaggagcagggaatatgcagaatagctcatcttgtaccaaagttttgttaccgtttcctgtgttattaaataactatgaggtcagcaatcccaaatttatgggattgagtttttattcattgcgcgcgcgtgcgcgcgcacacacacacacacacacacacacacacacacacacacaccctttggcctgttctcaagttctttttgtgcacttccgtccacctttccggctgcccttggcctctgggacctaccctctcctctcccaggtactcacagagtctgtgtcccgggcggtaggactgggatgcgatggatatccttgcaggtgactctgaagttgtcctcctggtggcactcacagggcggagaagcacaccctttcccccctaagctcctgggcaggccgagaagcagcgccagctgcagtaggggcatcggcctcattttccaggggtccgaggccatttcttcttcctccactatcgcctcattctcagccctctgtgctccgagctccccgcgcggaaggagcgagggaggagtgggagaaagaggactcctggtggttgtgacttcagcaccaaagaggaggctggagagggcaaagaggctctcaagtgttctgtttccctaggcaactctccacgcaaacaagcccattggctttactttcctagccctgagccacgcagcccgctctgtctgttccctgacacttaacaccagtccctgctgtccccaccctgctttccagccagcctcgcgcgttccttccattagctctttccagagcctagttcagaagcgccccccccccccgccgccctttccttgtcgttaagtccagtgggccaccttttcccgtgggtagattccgaaaagcgctgcctgtcaggacccctctcccccgctcccccttctcccccaaggctcgcacgcactgcagagaaaacttcccgcaaaatgcaccgtccagggcagacgcgggtcaagatccttctggcctaaggtgcctctgaggatgccagcctgaggtgcggagaagccggggcgcaatacctctggcggaccctcactgctccccaacttcagcgcaaacccgaccgctttgcacagaaattgtattgaggagtcacctggaggcgtcagctcacattgcagcaggcggacttggctctaggcgggccgggaactaggctctggggccatctgctgggcagaaccaggaatggcaggaacaggtctggtgctctggagccggccttccgacctggcttgaagcctaaaggtaaattctggactgccaggaatgttattgcatcatatagctccgctttccccaaaatccccccaaagatgggaagaaaaataacacttaagtggcacaccatcatctacttaccattcttcacacagtagccaaaaataataatttaaaatataaatcagattgtgccatttccttcttaaaagccttcagttactaattcttcttgggatgaaaagaacatcaattcttcacgtactgtgtaattccatttatatgactttctgggacagaatggggggaactatagtgatggaaatcagaacacgatttaggggagactgattcaaaaaagataggagagcactcaccagggttatagaaatgttctatatcttaattagactggaggtaagagaatgcataatttttttcaaagttcattggactgtatgtttaaaatttgtatatttcaatggttttagggctttattcataattaacccaaactggaaacaacccaagtgttcctcaactaggcaatgggtaaacaaacagtgatacatccatacagtggaatattatccaatataatcatcctatataataaaagggtaatgtgcaaattgaccctaatggcaaaatgaccagaatgaccgctggaccagtcactatgaggtgcactgaccacctcttggtccctttccctggccagcaggctctgatggcctgatggccgtggcaggggtggggctggcgagtgggcaggaacagccaacctctcggtcccttcccctggccgtcaggcaccgatcattcaatggcaaacagggaaccgggggttggtggcggcgggggcagggccggctgtgggcagctggggaagatggccttgatcgcaggccaggacaaggaaccgtatgtatgcacgaatttcatgcaccgggcctctagtttttcaataaaaaaacaaaacaaaaaacagccctggtcagtgtggctgagttggttggagcatcattcagtacactgaaaggtggatttgattccaagtcagggcacatatctatgttgtgggttcaatccctggttggggcacatgtaggaggcaactgattgatgtttctctctcacatcaacgtttctctctctctctctctctctctctctctctctctctatatatatatatatatatatatatatataaaatctcactccccccttcctctctctaaaatcaattaaaaaaatacataatcttgcttttactttgaacacttttgttaacatttcttgtggtacagatctgttgggtatgcattattccatcttttttgtgtctgaacaagtattaattttgccttcatttttcaaggatagcattactaggcacagagttatagattgaaagtatttttttcctttcagtactttaaaaatattgctccactatcttattatttccaaggacaaatgttctgtcattattttcttttgcaacatgcttttttctccctcagaatgcttttaagactagttatcgctggttggaagtattttgattatcatatgctttgatgtactttcttcatatttcttctgcagtattttgaggttcttgaatttttgcatttatgtttgtttttaaaaatgttttattgattttagagagagaggaagggaggtggagagagagagagaaacatcaatgagagggaaatatccatcagcttcctcctgcacaacctctattggggactgaatccacaacctgggtatgtgccctgactgggaatcgagcccactatcttttggtgtactggacgatgttccaaccaactcaactatactgctagggtgaatttctgtttttcgtcacttttggaaaaatttctgccattatttctccatttatttttatgtccccttatgtctctctactctttccaattattacacgtatattaggctaattaaagttgttctacaaaaaagctcatttatgctttttttgtttttgttttttgtttcattgatgcttttttaaattacaaattaattttctctctttttcattttgcatagtttcttttttatatcttcaaattcaatattctttttttttttttttctgaaatagttcatctgccactgattccatcagtatattttttatgtcaggtgaaatgttcatgtctagaagttcaatttgggcctttttcatatttcccctgtctctatgtaacttttgacatctaaaacacttttgatgtctgtgttagactcctagagctactgtaacaaagtaccacatacttgatggctgaaagcaacagaaatgaattcactcacagttctggtggctggaagtccaaaattaagctgttggcagggtcatggctccctctgaaggttctaagggcgattccttccctgcctcttcccagatcctgatggttgccagcagtccttggtgcccttggttcgcatcaccccagtctctcctctgtcatcacacagtattctctcttgtgtctctgctctaaattccctctttccatgaaagcatcagtcactggattagggcccatattaattcagcatgacctcaatttaacttgcatatctgcaaacaccctgtttccaaataaagtcacaatccaggtattagaacttcaacttatcttttcaggggacacaattctacccctaacaatgtccctgtttgataactctaatatctttatcagttctgggtaagtttcaattggttgatttttctccttaaaatgagttgcatttttcttcttctttgcatgactggtaatctttatttggattgtagacattgtaaatttcacctctttgggtactggattatttgttttcatagaaatatttttgagatttgttatcagacgtgggaaaattacttggaaatagtttgatatttttgggtcttgctttcaaggcttgtgaggtgggatgggaatagcatttaatttagcaccaattactcctccctcctgaggcaagactcttctgagcactctacataatactacgtgaattaagaggtgtctccatccggctggtggaaataatcatcattcctggccctgtgtgagtgacaggtactttctcctccccagtcttggtagatgcttccttctccagcctccgtttcttcacgtattcatctgaatattccaagaagacgttccctagttctttagagttttctctgtatgtagctctcttctctttgctactttgtccatctacactggttttcacaaactcccatctctgtcttgtcaactcaaagcatccacggagctctgcctgggttccttctccttgagccatggcctggaaattctctcaaggtaggaagttgtggggatcacaaggctcatgttgtttctttcctgtctgttaggaaatcactggcctttatataatgtctattgtctgaaaaagcattgtttcatatgttttgtatgtttcctttttcttttttaaaaatatttttttgttgatttcagagaggaagggagagggagagagatagaaacatcaatgatgagagaatccttgattggctgtctcctgcatgctccccattagggatcaagcccaaaacccaggcatgtgccctgattggaattgaaccttgacttcctggttcataggtcaactggctcaaccatgctggctagtggtgtctttttactttttaaaaattctgggtgcagggaaagtctgctctctgcttctatgctattccatctttgctgggaacataagtggtatgccaaaaatctgtaacattttaatcaagacttaggcaacatgaaattggctcagtggtggtcataatgtaggtaaaatcttccatgcttacctgtttcatttcaggaaagtggaccaaatcttcagggtaaccatctctatatataaaaagccagtaactgaaacactgtaactaccagaacgaccagccgctatgacgcccactgtggccagagaaccggcctgatctgggggtggggccagctgaccaatctcctgcagcccctccccctggccaaccccacccctgatttccccctctaccctaatagggggcagggccagccagccaactgcatgaagccgctccccctggctggcctcacccccaatgggtccccaccaccccaatcggcctgaggcccctccccccagccagctctgtccccgatcacaccccacacaccaatcgggggtggggcttgcagaccaaactcccatggcccctcccctggccaatgacccctgattggcccccccccccccccccccaatcgggggcagggcctgctggccaatcacccacggtccctccccccagctggcccgaccctgatctgcctgattggggcagactggctggccaacctcctgccatctcctcttccctacaggcctggacccaatctgcccctattggggctgggctggctggaccccacctgtacacaaattcatgcactgggcctctaatattttaataaccttctcaacaaattgctatgataatcagtaaggcacttttctttccttagcctaactttgaaactggggccaataaaagcatgggatgagaagatcaaccagtttaagtaagagagaagtgtttacattctgatccattaaccacaaaaatgcttttaaatataacagatgaatgagaatggcttaagaacattacattttccttcttgtagaagaattgttagcatggtccagatatcagaaagtagtatttagcatggcattactgtctagtgttttatccaccccccccccccccccacacacacaaaggtacctattaaagtggagcacaggctatctgagttagctataataaaaatattttatgattttggatttgtgcattaatagttattcattttgacttaaatgcaaacacattgctgtgttttacatttgtgatgctcttgacagtgacatatacctataattaataagtgtgtggctgaaaagaatattatgggatcaatggcacacatgaatagctgaagatgattccttgagtagttggcaataccatctttgcaacatcttgtcttctattttaactttcccagattttcagttagcttgaaaagttgatgcaggaagagttaacaatcattaatgatcattttgtgctttggttcagagagaattttgggtagttccaattaggatgttggagatttctctttaaccttacccatagcatcccacttcggagctctgtcttccttcttatcccttataattgtctggtgatattacaaggaggctcgaccatctagtggctagatggaacaatgtggaatggaacagatggttactacttgtgctggacaaccactgggtaaaatgcaggctgtcttctcttcacgccaatgcgcggacagcgaggtccagactatgaatagtacttgcttgagtagaagctgagcagtctatggacatgggctttcctccaccaccttcaggtctgcattgcacccccagccccgaaatctccaactctccattctgggactcttttctctgtttcttatcacattcccactcaggaatcccttttctcttggcactgctcttcttcatattttcttgactctgagcaacatgagggcagaaactaggttgttattattctcattttcattacatttcaagtgtctaatataatgtctggatcctagaaagattcagcatacatttgctgaattaaatgaatgagtacaattgttttttttagtacaattgttttgaaaaattaaaatgtaaacaaataaaggtactctaatgcttctgcatgtgcatggggcaatttaatgactgttcctatttatgttaacatttgtgtccggtgcagtgtattctcttttcttttcttctttcttttcttttctttttcttttcttttcttttcttttcttttcttttcttcttttctttttttcttttcttttcttttcttttcttttcttttctttttctttcttttttctttcttttctttccttttcctttccttttcccttccttcccttcctttcctttcctctttcttttcttccttcccttcccttcccttcccttcccttcccttcccttcccttccttcccttcccttcccttcccttcccttcccttcccttcccttcccttcccttcccttttctttttttcttttctttcttaatatggaacgcttcatgcatttgcatgtcatcctcgagcaggggccatgctaatcttctctgtatcattccaattgtagtatatgtgctgccgaagcgagcatgagtgtattcttgaatacagctgtgagcaagcaacgtgaagacagatgcaaatacataggtatgtccttagaataatcatcttatgttcaaaccggttataaaataacatctacctcacagtattggccagccaggaaaaatattttaatgcttctgatatatcaaatatgatataatagatcataattattaatgtgtgtgtgtgagagggagggagagagagagagagagaggcagagagagagagagagagagagagacagagagagagagaagggaggatggaaattttaacaggttaggtccctgtgtttgaaaggcttagaatctaatgtaagtaccaacagggaatggataattacagcacacaatgattaggatagggaatccatgtcgttaaagtgatcttgctttatctgtgtaggaaggcttcacagataaagtaaaacttgactttagccttgaggaggaaagaaagcatcaacagcagaaggaaaggaccatcttcgtgacgaatttggattttcacttcttcttagcttttccaaagctcagttccttgttgaagtttttaaagttactttttcgcaattgaaaccgacttctctgagcggactatgtctctttcaggttccacaataagcggagagatgacaccctggcagcagttttgaagtgacattctcaaattccacttttaatttgtcatcagtgagcttactttttccaggcctaaaaggtgtgactagataatcttctgtttccttccggatctaagaatttgtgaaccacaagacattgggcactggtattattacacgaaggggattttaacatttctctgctggaaattacacctgcctgttccccaaggaataacaaaaatatagtctatgttagtattgcaactactttgattttttttttaagtaaaggaaatatcttagactttcctgattaaattacagtctgggccaaggcagctctctctctagctggtgtttgtcttcaatatacgcttctggccctttaaaaaaacaaaacagggaaagagggtgcgtcctagggcctcaggtgccgacgaatcctgcttcttccgggaaagtcaccgaagacaatcgtttggcatcctgggacttgcagtttcttttaggggcagagtcccttagttaactcattgctttagaggacgatgcggactacaactcccagagggccaagcggcaccagtagggtcctgtactcttgccgcggctgggcggctagttcaagttgccatagctgccagtctgggcacagctcagttcgttgcgccaccgcgtcgggtcagttgcaccttctcggtcacaggtggccgtgggagccgggtggggcctcggcgatgatccggcattaaggacctgggctctcaatctcaggtaattgcccgccaccgcccgtggccctttctttgccacttccctgtcctgtagacgttttaagtcccacttctccttgtcccaggggtggggacactggccgcgcggagatgtggggtggggccgcggcttggtagtgtgagcgccagggtggggagagacggggtgcccggcctgcgaccccctccgcatgggcatccacagcctggtctgtgccaagctattcagggaatttgaacttcctggtcctccttttgcgagcggcatgtcctgagctgtcaaggagatcggactcattttggaggtgactcgcctggcctgggatagagctgggatagcgctggccgttttgtacctgttctctagtcacagacgctgtatcgctgtatcggcctctcctgtgccaccatgggaattgccccaacgttagctgcccgtgatatgagttatttcgtaggaacatctgacattgaattccagctgcaactgtgatcggtttgatggggcccagtggcttccgcaggctcctcgtaggaccgcactgtaacgttgttccaagtatgaagcaaagtatctagcacagggcctgacacattggaagtactcagtatttgttatttatatttactaattatgcagaacagtgccgtcaagtagaaatattatgtgagacacacacacacacacacacacacacacacacacacacacacacacgtttaaattttccagtatccacattgtaccagtaaaaaggaaactggtgatgttaattttaatagtttatttagcacaatttatttatatacaaaatgttttcatttcaacatgtaatcagtatggaaacttattgattgaggcattttacagccttaaatgtatatatgtttttaatgtttctaagtgtccaaaatgtgcattgtatatttacagcacctgtgagctcaaactgggcagatttcaagagctcagtagccacgtatggagtgactactgtattgcacagtgtagattcaaggtgaaatctttagcagcttcatattggcacagagaaattgggatgtgtctgtgacccagcccatggggtgaaggttatgctctgttagaagagtgaagtatgcataaaacctgagtgctctcagcgtgtccgctctgccattgcaagacctgtgttgtttggagctctccacagtttattttgattaacttttccaggatagttttttactttttcttgaagtggtttatagatgtagtctcaggtcttcttatgtgacaactactagttctctggatacttcctgtgatttcctacagactgctttaaaagcagtcacaaagaattgaaaatatttctgtccacagactttcttacaaccaaagacattttatggttacttatttaattgcagtactaaatgtgtgtggtactgggtagcttacgaaatatatcgtttaggcgtactagcatatatagttcgttaactggcttagaaattcaactgggtcattaaattcagaagtatcatgttaaattaattatttgacttagtttaccctctgatagacatgctgaaagagagagtccttttaggtggacttgggctacaattgcatgatctggccagccctagggatgaccaggcatgggcagaagtaacaagtgaggagtatgttggttgggcacttggatgtggagagtttttttgagttcagtggggcttttgcatgacccaaggagcattttctctgttctctgatatgccatgaactgtatatagtgagggtgagcttattttcatcttttatttctttaacaaatttaaagttgtaggtgtgttttaatccttgtttcatttatctgtaggtggtttggggaaaatatagaggcaaccaaagattatctacttgactaggccttttgccctgaacctcatgaagaaatgataggaggcagacatatgtgcctaagaagagcactgagctcagtggagagcaacacggcgatttgggggtgtgctttgtaagtatgctttctcccggtgtttctaaacacatttttgcatatttatagtggaaaagagtatgtcaacaaataagcttttgagaggcaagaagtccctttctaacagcgtaaaatttaaatagaatggttgtgcgtgagagttataaaaattcaagggaacagactagataccttaacaaaaacaaaaatgatgcaattgtttccattagacaaacatttgagaacctattttgccccaggcacagtaatagaaaccttctatgtgacatatatatataatatttttattacactattctttaaaaaaaatatttttattgattttagagaggaagggagagggagagagagagaaacatcaatgatgagagagaatcatggattagttgcctcctgcacaccccctactggggattgagcccacaacctggtcatgtgccctaaccgggaatcgaactgtgacctcttggttcatagtagaggcttaatcgctgagccatgctggctggacattattatgctactagagttccgatacatgaaatttgtacaagagtaggcctttcttcccttggctgccggcaccaggacctgggcttccttcgcagagggaggccctgtccacctgccacagcccaccagtcggtggccttggcctccctctttggggtgatgtggagccccctattgatcgccccgcaggccggtggcctgggtcttcctccacggggcgataatggggcgatggccgggcctccgaccaatcgcatcgcacctaccttggctggcctggcgccagtgggtgtcatagcatggtcccggatggttgtccggatggtcattccgctgttcagacgtttggtcgattttcatattatgcttttattatataggattctttgttttataaatgagacagttgaagctcagatggtacatgtgatttgctggggattctggagcacaaagagtggttttcattcccgggtgttctttctacaaatctcaatcctgcccagtatgaagaggtgccgttgctgtctaagattgccctcagtgggatttgattgacagtcagcatttttagattaagagtaccatataagcaaaatttactgttatttatcttttatttatttattcagaaaaatatatttttattatagatttcagagaggaagggagatgggctatttaatctctttaaatattcaaagggttctattatcttagtttttccaagatataatggttcttacattattattgaggtaaagagttataattcgtgattactgtaaaatgaacatattcagcatgaattgtcattgataatgaaggatgtagtattaagaactcaagttttcagatgtagaggtaaaactctggacactttttagcattttcctcttgcattttgataagggacacttaaacctatataggagaccctttagggccttatgaaaacactgtccaagatctatgctgcacaaagttcttagaattaaagaattttagagctggaagagccagataaggtcatataatcacaaattctcagatgtaatcttgacatatttgggaataatataaaaataatctagagtagattcagtggtatccatgtagtatcactgagctgattctaatatgaaatgtttcaatatattcatgttattctgtagtgtgtatttaattgttgcaataatgtagtattacatgtcagtacataaaattctataatgtctcttttaactgctacataatattccaaatgactatatttaatgtatcccttatcagtagatttgtaattctttcttattttcttttttactattttaattctgcaattaaactctttctacatatacacatgaaatgtatacatacatcttaaacatcttttgttttcctaggatactatacagggtggggcaaaagtaggtttatagttgctcgtatggaaaacaatacaataattaataaataataatataagaataaactctgttgtgcatacttacaactataaacctacttttgccctaccctgtacatagaattagactatctgggtcaaaacatacgcatattatttattttttacttgtcttgccagattgttctccagaaaagctatctatttcagttaatattcttacagcaaggtgtgcccttgtcaatcctggaggtgagagctattttttttaaatttgtacaaaacagataaacaagcatatcatattattattttaattttcaattttttgattaggaatataaagaacattctatacttgttagggccatttgtattctgtgaattgcctgtttatataccctttgcatcttttagtactggtttgttcttttaaaatcattttttcagagagatgatgaatcctttggtttcgatataatgcgaatatttttccgtttgtccttctttccccctaaccttgcttctagtatctttttatcaataattgtaaatgctcctaacaggctacagggtccttatcagatattcactcctccctgaaattcacgtaacccttaacattgatcagtacaatgctgttgtgtgggtgtttgggagatggacatatatgtgtttatatgtatccatttagctgaagaacatagaatctacaatgtgcacagggtttgccatgacacaggtgctcaggatgaatggataagtgaagaagagagacattgtggaagaagtgaacgatgattgttattttaaagaaagaagagataagcagttttaaaaaattatttgtttatttttaagtacagcttacattcagtattattttgtatgagtttcaggtgtatagcatagtgactagaccaggggtggacaaactttttgactcgagggccacaatgggttcttaaactgtaccagagtgccggaacaaaagcatggatggagtgtttgtgtgaactaatataaattcaaagtaaacatcattacataaaagggtacggtctttttttttattttattttagttttattcatttcaaactggccggatctggcccgcgggctgtagtttgcccacggctggactagacaatcatatactttacaatgttccccctgatatttctagtactcacctgtcaccaatcatagttattacaatattttggactatatttcctatgctatactttatattcccttaactgttttgtgtaactagcaatctgtacttttcaatcccttcacctttttcacccagtctcccagccccctctcctctggcaaccattagtctgttctctgtgtctatgagtctgtttctgctttgtttgcttgtttgtattgttctttagattccatgtataagtgaaatcatatggtatttgtctttctctggctgactatttcacttaccataataccctctaggggagataaagtaattttatagattgtagaaaaaaggaaaaggctccatggtgagagactgaaggctttaaattggcaagaaatgggtttgcttatgagcagcaattggtacattgaaagcatactgagaatggacctagatgataaagaacctaagtaatttggagtgtagtgctttatagtacagacaaataataattgaggagtgcagatagctgtagatgacaataatgtatttaaatgatttgcaataattcaaacaagcttaagtgtgattcatttgttttaacgcatttattcaaatggatttaaagtgtttatctttgttttctctataaaggacatattatcaagtgttcatatttttactattttgcagtttaatccatattgtttggagatgtaagataattaatctaggccatttctacaagaatcattttacccaacaattatttaattagtatttactaattgcaaggtcctgtactttgaaattcatctctggattaatcagacaggataataagaaaatgtaattgtaaattcatttaaaagtgttccaactacatgccaggaaatatgctaggtgttgaaatacaaccatggattacataaatagtctctggtaagatttccagtctagtggggagagagacaagtaaatagccagttagcaatctctgggcagtgttgagttggtatccactatggatacttggtgggctaggggtggggggagtgaaagaagactttctaggggaagtttcctttgagctgagacttgaaagaaaagtagaagttagcaaagggaaaacgaagtgaaaagaatgttccaagtggtgaatccagcataggtttgatgacaaaatgaacatgacatattctgtgacagccctggccagatggctcagttgattgcagcatcatcccttacaccaaaaaagttttgggtttgattccccatcagggcacatacctaggttgtggcttgatccctggtctggacacctgtgggggcaactgattgatgtttctctctcacattggtgtttctctctctctcccttcctctctctcagaccaataaaacatatccttaggtgagcataaagaaaaatttaaaaaaattctgtggcacaaataattgtatttcatgcagatattttgacatttcataaatgttttgctataattatagagctatacaaatgcagaggaaggaaagatcacttctggctagaggtggacagcttcattgagattgtgttatttgagctatgcctggacaagtgagtgtttaaataggtggcaatactagatagcagaaggcattccacccagacctaatgacattaatgaggggtggcagtgggggaagaatagttgttgtaatagctgaatgtaggccttaagttttctgtggttcttttattaagacaggtggcttgccgagggccttgaattcctgattcaagaacctagattcagtcaggtggccagtggagttggaatgtataggaaaagactcaaaagtcaaagaaaccattttggtggctagaaatgagttgagaaaaagtcttaaatttatgagtagtgcacgtggcagtgagcacggttagatggtcagaaatctggaccttggagaaagattcaagactcagctctgctacttagtagctgtgtagccttgagtacctttcttagtgcctttaagcctcttaggtatatatcatgacagtgtactgaccttgtagggttattgtcaaacaacaatctgatattgaaacattagttattcagcattattcaacttgagtttcttgaaggtttttgaaattgaggttcttaacttcagtcctcatccatttactcccaataattcaatttcattataattggatttctatctaatccagtatctttaaaaattttttatgtacatttaaaaacattccaggaaacagttcatggggaaatcaaagttaagaacttcaattagctttttttcttaaaatttagaagtctatacattttgaaaagaatatgccaattattctgtttatccactttgaaaattaagaacaagaaataaaattgaggatattatgttaagcaaaataaagtcagccagaaaacctaagaaccatatgatttcactcatatatgggatataaaactgaaactcatagtttccagaggagagggggtggaggattagtaaaggataaatgggaccaaatatatggtcacagaaaatggtttgactttggatggtgggcacacagtgcaatatatagatcatgtatcatagaaatgtacccttgaaacctatataatcttattaaccaatgccaccccaataaatttaataatgataaaaaagaaatacaaaactcttgaaatgtatttcagtcctttattatagaagagttaaaaggaaagatctaaatatataaagtaagtgtggggcaaggaagaaatatgggaattgagaagagtggaggaaaaagagaaatagaaaatacgtgtacaccaaaaagaaaatacaatttaaaaaataaaatttaagattttatgatgtttctgaaaaaaaaaaagaaaaaaaaagaatgcttttctgcctgaagaggtaaatattcttaacaagagaaagggttatttaaaagacattttaaaatagttgtagaaagttggatttataggatatttacatattatcttgtattcattatagaaatctctagttttaagtagataagcacatatggatgacatactctggccttaaatgcatttcttgggggagtgagggtgataagaaatatgatagggatggacaaagaaaatatttcagtgattgggcaatgtatttagaatttacacagttctgctcctagaaatgtagtggaaaagcactggcttagagacacaagatatgggttctagttctgttactgtaactttttatcaaagcaatggtgggcaagtattttattcaaagctttgtttttcttagcagtaaaataaagagttgcaattatacccactttgtaggattgaatggaagtcaaaagagaaagtgctaaagaaaatgctttgtaagctataaagcaacatagaaatttgaggtataaatagcagtcagaaatatttccctttcttaaaatttttagtggtgttttttcactaggaaagtcaagaggagtttctgtttctactttaaaaaataggaggaaaaaaaccaagtattatgctttataatatatttaagaaacagcttgtttcttatgaaaatcttaatgttatggggagtagattaaatagccccaattgctatttatgattgcctagtttttctctctcaataattgctagtctgtcacttgtcatatcaattgcttatgcttattttgtgcatcctaatttgaatttgccctccccactccctcctcaccaccagaagacagtctgaaaacaacttgttttctcaaggagagtagtctttagaggaaacttcaggaaattaaattttttctgaaccaataaaatgatttattagtaaactacatattgtcaacagatgatcattggtgccaattcaacgccaacaggaatccccaatttggggtgtggtgaagaaggaaaatttattcagtgcaaaacagctcaattgtgataaagcatagccgtgaaaacagcaagccgatgaggtggacctggggccaggcccctctctgctagagatctctgttccttgctggtctgttcagctatgtgctggtctgctcttctccattctgtgtttggtgcctgtcttctctggcccatgttggtctgttcagctctgctccagtgagacagagatatctttggtgttttatctcagccagggatcagtctttggtggaaaggaaaggtgcactctcagagccagaggggagctggcttatatagacagaagcccttgcctgtgaccttgattggtctgtcctcatgcaaacaaggactccagatccttgtagttggatttgtccaaaaggcactttcctgattggtcagaaaagagctgctctgattggtcagtgaagatgttgatggggataaagctgtaagctctgtttggatgggggaagcctcagtcccattggttgaaataggattccagcacttcctttataagggctggctcagcagacagaaaaacagtgcaggcaggcagttcagtgcaggcttctgtgtgaagtgcagtttgtgtggaaatggctgctagtctctgtttttaaaatttgagcccagttagccaccaggagaccttcttggtaggtatcttctttcgcagtgttcacaatatatataccacttagcttttgctgcataacaaataaaacatacaaggcgtccgcatatataaaagcctaagcgactgatgactgaatgactggaacgaccagttgaccagttgctatgacgtgtgctgaccaccagggggcagacgcttaatgcaggagctgccccctggtggtcagtgcgctcccacagccaacctcctgtggctggtcggcagcttcagcgtccggcgtccattccttccgtgaccgtcgcctcctctcccaaccctgctgggacctttggcccagggctgggatggggaaccagggtgggtggtggtggatgtgacccctttcccagggggctgagggccggttcccttgtagaggctggaggccaacctcccataccccatccctcctcccagctggcaggccccaattggcctgcagtccctctctttgtccccccccccccccagttgtcggcccagccctaatcactggccaggcctagggaccccacctgtgcacaaattcatgcaccaggtttctagtacaacaataactttatcgcttgtacagttagtcagctaagctatctgcttattatgactggactcacttatttatctgggttcagctggctgatcacctggtctgctgacctaagctggaagcctctggtgcaactatgtattcccacaccttccaacaggataacaggatatcctgggcatgtcttcttggagatgggataggtgcagggaggcaagttccagtgtgtaatgccttttcgtgactccacttgtgccatgttagctagaatcccattgaccaaagcaggtcacatagctgagcccagaggaaggggcctgggcaggtaactcgacctctgagagcagtgcacgtagggatgggtaaagaattggagctaaatcatgaaaggtactacattttttatctgaatttagtttttgatatatatataatagtaggaaaatgttctaagtagatggattaaaatcaagcccttttgtttgtagtggtattttgttctggttttggagttagtaggcatggctgatatcaaagctgttacattaactctcaagcatttaaccctgactaattcttcccaatcaaattggtcatttttaactagggaaactcctttaaaaaggtaaacttcctttgtcttggttctttattcaaaatagggttgcaaactttgctagtaagtgggaccaatcggttaacacactgggtgaagcagctagctagtgggaacttaggccaacagaaaggtacatgcccaactccaaggcattcatggtttatgtccatgttgccatgggaaaattcatcccagtgttgccagatctctgggttttttaagagaagctggaaattaaaatttttatatgaaatttagttgttttaaaattagtgttttaaaataacatagtgagtgttattttcaaatacatgttagtcagctgggtttggtctatagtcctccagttttcatcctctgatttaagcaaagtggtgtcagaaactcgggattctagctccatctttgtccaggctgatctcttgcccagcctggggcagtttggacaatcagtagaagttgtctgtacccccagaggactttattcgtatctgctattttaagaatattgtagtatatatttcattttcaatcggttagaaaatgcaaaagtccaattccagggtgtaactacaagtaatagatccatctcgagtttgctagcatttctctgtaattttcttgagtttttgttgttatttttaatcttgactttttaaaagctttttacttaattttgtaaactgcctcatttattttagaagtgtgtgtgggtgtatgaatctaaaatatctgtgtcattctgcttgtgagacagggaggaggttattaagtaactttcttttaaaactgcattgatctttaacaagtggcacatatgtaattatctgagcaacaactaatttttttctctgtgcctgaataaggagtcattttgtatgctgcttgtccttttccagaaatgcccctgctccctgcttggtgggcactggtccctgtgactctttccctggactcaggagctggagcgaggggagcaaagtgcatctgggagctgagcctgacccgctccacccataggatagagggtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtataagagagagagagggaggaaagggagggcagcaacaggaataagtggaacatctatactaataaaagggtaatatgctaattagaccagacatcttctggatgaccgtctagacgtccttccggacacagccactgtggtggggccgaggcagaggcagttaggggcaatcaggcgaaaaggggggagtggttagggggatcaggcaggcaggggagagcagttggagtgatcgggcaggcaggcagagaggttaggggcgactgggcagttggacatcccccgagggatcctggattggagagggtgcaggccaagctgagggaccccctcccccccccccgccgtgcacgaatatcatgcaccgggcctcttgtacaatataaaatcaaatctagtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtataataattaaaaacattaattctctaagtcagaagtctcaaagtgggatgattttacccagtcacccctccctaccccagggacatttagcagtactggagatgggtttaaatccattccctgatggaagaatgcaaggtgctttgcttatttgtaccttctctagttcttctgaaaaagaagttttctaactggatcccctatccttgtctcatctatttgtcaaacttatgctcctccagatgggtgggaggatcctgaccccactgctccggcgttttgttgtagtacttagctccctgtagagagcacttttacagctctttatctcccatcaaggactgtcacttaatttacttactttcatctttaatttctaatgcagatataattgagtaaatgattgcacaaatatgttcaactttctctctctctctccttttaatcacctgaggatattttttttattgattttagagagacagaaagggggagagagagagggagaaacatctatgtcagagagaaacattgattagttgcctctcagtggatctctgatcgggatcaaaccctcaaccttggtatgtgtcctgactaggaattgaaccggaaacctgcaggggatgatgctctaaccaactgagtcacccagccaggattcatgtatctctttgatttttcccctatgtcttatgttatctattgacttcacagggaattggctgttgattttgatcttatatgaacttaccaaaagggtttgaggagattaagttacagaggagtattgatatcaagaagccaggcatgactgagaattgtttgtacatgataacttttcctggctagttcactttgtctcttgaaggaggaggcaaagctctcaagacctgcagctgttgaaacagtgagaaatgccttctaaaatttgaactttgataatgattaagcttagaatacactccccacatcctgtgatccccttaaatatcaacatcatttccttgaatgtgttctttgaggacagttgccttggacagggatggctgaggcacttggaggccacagaagtaatcacctacttaacatgcactagtcagcttttgtaaaatgtcacccttgatgctgggtgcttatcttttatggaacagcttgcaaattctaattgttttaaactggctctgaggaaaagagacttgagaatcaataaattacagctctcatacatacaaatatgcatcattataatgaggacaatctgatggctttttaggtcaaataacagaagaggtggacttgagtttgtacagactcactgccagtcctcttattacagggccaaatgcacttttactccccctgctcacagtgttcaagtttgcacatgggcatttgtttttgatggactttttcctcagtgggagcgaattgaggagcttcattactcactctggtggcggtatgtcatctttataaggctcgttaaggcagaagcaacaatatttatgcctttaaatactgatgtgttgggaacagaaaaataaagtatttttagagttctaccatctggtttcagacccagagctttctctaagactatggagactttcacccaaggttgcctgtcctagtcctctctagtcttggagaaagctcaatgtctgaaaagcagacagatcaatgctattgcttcctgatcaatgagaagttttgttgtactagatggtggtagtaagagaatattttctgctttttctccagttgaaggaaacgcttttctctcctttaatcagaatttgggtgattcagttttttaaaattgttctttaagagttcttcaatcgttcttcaaatttagcaattcttttccccccaacagatgtcatgttgaaacttcttgtgataatgagaatggatttttaatccaactgaactttttccttttgaatatttcaaaagaatgatgaagattttccttgagcatacatagatgtttttctaccagtcttaataattcttctttgggcttcatattgtaattgtggttaatataattcatgaaccatttaaatttgtttttcaaagcagaatttgattgcatactttatgtgagtacacattaatgtgttttcacatagtccttcaacttttaagttgagccttggccaatgtggctcagttggttgagctttgtctcattcacctagaggtcagggaagggcagttgtgggcgatcaggccggcaggggagggcagttgtaggtgatggggccggcaggggagggcagttgtggccaatcaggccagcaggggtgggcagttgtgggcaattgggctggcaggggagggcagttgcgggtgatcaggccggcaggggagggcagtagtgggtgatcgggccagcaggggagcagttaggcgtcaatcaggtcagcaggggaatggttaggggcgatcaggctggcaggcagaagcggttaggggcaatcaggcaggcaggcaggcaggcgagtggttaggagacagcggtccctgtgggatcaggcctaaactggcagtcggatagcccccaaggggtcccagattggagagggtgcatgctgggctgagggacacccccccccacccctgtccacaaattttgtgcaccaggcctctagttggtatataagaaggccatagatttctgggtgttaattttgtatcctgctacattgccgaattcatttattaagtctagtagttttttggtggagttctttagggttttcaatgtacattcccatgtcatctgcaaataatgacagttttacttcttcttttccaatttggatgccttttatttcttcttcttgtgtgattcctatggctagcatttccagcactatgttgaacaggagtggtgaaagtggacatgtctgtcttgttcctgttcttaggggaaatggttttagtttttgcccattgagtatgatttggctgtaggtttgtcatataaggcttttattatgttgaggtatcatccctctagtcccactttgctgagagttttttttttttttttttataaaaaaagggtgttggattttgtcaaatgccttttctgtatcaattgatatgatcatgtgatttttgtttctcaatttgtttatgtgatgtatcacatttattgatttgcatatattgtaccagccttgcatctctggaataaatctcacttggtcatggtgcatgatctttctaatgtactgctggatccgatttgctagaattttgttgaggattttagcatctatgttcattggggatattggtctgtaattctcttttttttgtggtgtctttatctggttttggaattggtgtaatgctggcttcatggagcttggaagtgtgccttcctcttgaatttttggaatagtctgagaattgaactgtgacctcctggttcataggtcgatgctcaaccactgagtcacatcggccgggcaggagttgctcttttaaagcagaaaaaacaaaggagggagaattacgtgaggaagtgacttatattttcaaattgtatgcaattattatgataaaccaaaggactaatgtaataaatactgtaaaagatcaaaagtaaatagctaaattaatctcttattcccattctctttaccatttgcttgaccatgatctgaattcatttttaaggatttgtgtacatcaatggcagaatcctccagtgattcagaccacttccgctgtcatgaccggttgagtcgatgggctgccaggtcaatacacagggatattagaaatcgtcctacagtcgatgtcaccaagaaggtcaacactatcacaagtactttacaggttagtttaacactggatgctttaaatcaatattaagtaataaatagttaagctgagtaatagtttcagtcactgtattagtttttttgcgcccataacaaattaccacaaaattaatgtcttaaataacagaaatttatttttttaaaattctgtaggtcagaatcggacactaatctcattgggcagtaatgagggtgttggtagggctgcatttctttctggagaccctagggaaatacctctcctcttgcctttttctagcttctaggctgcccatattttctgtcttgtggccccgttcatctccaaagccatcaggggtgggttgagtccttctcatagcatatcagtctgactacctctcctgcttctctcttccacttttaaggacatttgtgatcacattgagtgcacccagtaatccaggatattgctcatctcagcatccttaatttaatcacatctgcacaatcccttttgacatgtaaagttccaggaattagggtttgaacatctttggaagcccttattctgcttaccacagttagtaaggatatttgaaatatatgataggttgtggaaagtgttatatttccaaataaaatattataggaatgaaaacattagggattaccatcctttagcttcttgtggacatgtgccttggagaaattgctcatattttggcttgggtaaatatatcaatcagatagattgccaactagatgatatacatttagcttctattgaatttgaaagtgttaagtcagagggggaagaactcaggacttgaattaaactgagagttactgattattcatccaagccaaactatgattattcatggatgaataatagtgagagtccattttgatttggaggctaactttgacagtgtttggagatttgagtgagggcagttttcattcttccataaaaggggcttgggaaatgaggtgggggaaggtactacagtcacaatgggaaaaagaaaactcagaaatagaattttttcctcctacttattactcccttacctttgttttacctggataagcatggattttggctttttattgctctgggttaatatgcttagattaaaaaaaggctaatttctagtggagaggccataatttgatttaatttttctttttaaacttaatatggcaatgaattttggagtacaaattcttgtactttcccaccacagagacacattagtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtattaaattatcacctaaggagtgaggctatttttttgcattggttttaaagagagtggaaggaaggggggttgggtagaaagagaaacatcattgtgacagagagacatcaattggttcctcccacattcaccctgattgggggtgggaattgttatctgtgacccagctacctgcccttgacctggatttgaactcaagacatttcggtgggcaggccagcgctctaaccattgacccacaccagccggggcagtaagtatatatttttgaaagaaatacctccattaatctctcaggtgaaacctagcatatttaggtagaaaccatttttgttatgattagagctaaatttgttgtttatagctaaaggttaaaggatacttttcccccctcctaattcagttctcagataaagcaataattgaaataacatgaatcccaatgatttgcttctcttgtagataatctactagattagttcttttagcattctttttcatgaagggatgtgaattagttgaggtgtacaagttgataatgagggttttttccagtttatggttttgttgctgacttacggaaccttggataaggatttaaattaggtactttagatttcacacaagtaaattgcagatggattttagtttctctctcagttgtgttaagatttatttgatatttatgaaatatagatatctgacttaactatgatataaattgtacctttgcaaatggttgacttagctgtttgaaatactactatgactgattgggaaaaacatcaactaataaagtccattttctttctcaaggacaccagtcgaaacctgcgacaagtagaccagatgcttggacagtatcgagactatagtaacggacaggcgggtgctatagaacacgtaagatgtttgcatgtttgtattttctcttaccttcttgggaatgcagagctgagtattagggatgtgagctaaaggagataataattgttaacagctttaacttttaaactgctccactattttgtagtataaatttaggttagctcatcagttagattgccaactagatgatatacatttagcttctattgaatttgaaagtgttaagtcagaggggaaagaactcaggatatgaattaaactgagagttactaactttgggctctcatggggaggtaaaaattttaaggacagtaaaaaaattttaaagtaatccatgctcattgtttaaataataaaactccgatctcatagcccttttgagtcccattccaaaaaggtaacagttttgatagtttcttatatattttttcaaacattttctatgcatgcataaacttacatgtgattgctttataaattgtaatcatatgtattgttatgcagactttttcatttagtaatatatgtattattttccatatgacacattcttttgaaattacattatactgcatagtataggtgtattaagtttttttgtttttttttttttttggtattgtaaataatgcaccaatgaccatcatggttctcatgtttttctgtaatcatatgtgattcatgggaacagagttgctgggttgaaggatatgtatacttacaatttgctagatatttcaaaattgccctcccaaggaagtgccagtttgtacttacttcacatgggggtgtaagaaggaaacatatccttattctggagtaaaaggaatatttttttattaatattttgttaatgtaatctatattattttctcttttgaaatttgttaattcatctgcaaactcttggaaatccaattaaatttatttctccatgttttcaatatgagtttacactttaataagactagaattatatttgtactataatgattgccatatatatacatatcctatataataaaagcctaatatgctaagtgcctggtggtctgttcaaccaatcaaagcataatatactaatgatatgctaagtctgctcaaccgctcactgtgacgtgcactgaccaccagggggcagatagtcgactggtcgaccagtcactatgacatgcactgaccaccagggggcagacgctgaatgcaggagctgctccctggtggtcagtgcgctcccacaggaggagctccgctcagccagaagccaggcttatgactggcaagtgcatcgggggtggtgggagcctggcagttggcagttggacatctcccaagggctctcagactgctagagggcacagggcaggctgagggacacccccccgttcccccccgagtgcacgaatttcatgcaccaggcctctagtgtgtgatattcaatatatatatactgaattgtcacataacttcctagttataattgaagatttggtattttctttttataaaacagaagaatagttagcattatttgcttgtaattggaaactaattgcttgtgcacagggaaatgtatcttctgtcttaaggtttcccactatcatttagaaaataaacttggatttttgcagctaaaagattttcatgatttatgatgtatttgacagatggagctatagattttgtatatttgggtagctcattaaatgctgtagattaaactactttattaataatgggagttaatactctgccttttaattttagtgaacaattaaaaacactaatagcagagaacaatcctactttgtcatagtgaatattagatgcttgcttatagtagcaaattttctttgaatttgcaattgggaaatgaaattaagaattagagaaacttctgtgcaataagcatttaaaacacaaagtatgttctactatggcagtaagcctttgtggtaggccttgagttgaataatgctgaactctgggggctttatatatgggaagtaaatacattcaacaggtacttcatttccatctgagttgtaagtacatagtatgcaaagccactttataaaaaataagacacaggagagcaagtgtctttctttggcttcagactttttcttagtttggtctagagtctcctcctgaatttctccaaattcttgtcctgttaaagcagttggttctcagtgattttccctgccaacaaatcagaggagtaatgtcactcattgcctgacattatactcacttgatagagcagtgatcttcatcgccccacctataagttacctggggagcatctgccataatctattggaagatgtgcaactcaggaagagtatttctcacaataagtgattctgatgggctcctcaatgaaaattgttttcttagcattaaaatatttactgttatggaatattttacacctaaaaatgagtatatgtaatgtgctaaggatcatgtgatttatctgaatctttctatttatattctttttttaaaaacatatttttattgattttagagaggaagggagtggaagggaaagatagaaatatcaatgatgagagagaatcattgactggttgcctcctgcacacttcctactggggatcgagcctgcaatccaggcacgtgccctgactgggaaccgaaacgtgacctcctggttcattggtcgatgctcaaccactgagcaacaccagttgggctctatttatattcttgaagtccttgaagtacatggtagtattacccattttttcttttcttttaatcaatgtgtgcattgtttagggaggaaccctggaaattgatgtttttatttttgaggctatgctacaacttatacaagtcctcttgctgtgaaacttgttataaaaaaaaaaaaaatcccgttacagtttaatctgttttaaccagtactcaactgttaagaattttctcgctgctaaatatctattgcctcattgaaatgagcaattcttttttaaaatataagggatttcctagaaaatgagatcaactcttttgctttagggaagacaagaatttttattattaacttagattttataaatgattctattgctaagcctcaggacatggatgttttatatatgtatttaacatagttatttgattggggtttaattttgtgaacataatttgccagttggtgagagggaatataaataatcgatggggtgttttcctcctgaggtgacctcacccaacaagagaactatgagaatcattgctgttggttttgtgaggtcttagggtcaagagtcatgttgggtcatatttttgactacttttccttgaaatgtacataatttcattgttgaccccttgaatctttctaacatagatttaattctttttttttttttaaaaaatatatattttattgatttttttatagagaggaagggagagggatagagagttagaaacattgatgagagagaaacatcaatcagctgcctcctgcacactccctactggggatgtgcctgcaaccaaggtacatgcccacaaccaaggtacatgcccttgaccggaatcgaacctgggacctttcagtctgtaggccgacgctgtccactgagccaagccggttagggccatagatttaattctttgcttataatatgtgaagctgagcagtgttagagggtagcagggcatctgtagtgttttgtagagaagcagcacctgccttttcacagtttccctgggaatttctctgtaagcttagcatatcccaggtgcttttaaactatcaactcatttaattctctcaccaatcctgttaggtagatactattattttcctcatttacaagataaaaaactgggacacagaatggttgctaacttgcccatagtctcttagctagtcagtaacagtctacgatgttattctaaaacctagtgcctggattcatttacctcatttacctcatggttgctcacatttttgtgatgttgtgaatctatagttctgaaaattgggggtttgaactcaaagcttaaagggttatataattcacattgataaactagattttggagattaccttaactgattgaaaaacagaagcccattgtcacacaagtcattagtgttgtagctggtgctgtgtcagaaattagagttagatggaacaaatacagaaacagcattgggaagaaatctggtgctgagctaggactggcatggacagattgtcagggttggcagttttggctggcacttaggcaacatggtggattctgcagcttccctacaccatcattgacttccccttttctcccgagtaacaaccattgatttttcactattccagccagtgagccatatttagttggatacaatgacatagggtctttgggctccttgactttcataaccttaaagaagggtcattatttctcattctgtaaatttcttgggaggtctgagaacatttgggtcaaagaggacctgtggggaaggttttagttaaaagcttattttaaacatttgttatatagtgcaattgattagcttggtgtttagaatatttctagaactctgtgttcctattcataagacaatagggactgtttttgagttaatgctataaaaatggagtgccctcaagaaagaatattcaacataaaaagttaatgctttctgcagtggaagtgtagctgatgatgctagagtagttcatattgggtataaaagcaaaatacagttcccttgattcagatcagctgatttattcatttttaaggtaaagatgggtgaaaatatagtaatagcaaattaagccttagaggcttgaattaataggtaatttttctcctaattaagtgttctgtttaaaatttcttttgggagtaataggatgttaccatctggtacatgaactttggaagaatttaaatgttaaataataaactccaggtttttgttctatttggtgggtttcagttgtcaaatatactgagtgataccctaaggttaatggtttagagccaggcacaagccacttgtaatctctcaaagctgaaggcatgtgcccctttctctctggccaggttttactagttgtcctgaagttgagaaccagtcacagtcttttaataactttaatgctagatagtaagagtctatgagtaagaacttgtaatcgaacaattaaagtcttctggcttctttttgtaaatacatttcaaatcttagaaattaggttcttagaattctgtttaaaattttattaccaagtctcacatgaaacaactggataagagaatattttttacagacttttgctttgagtctagaggaaaaatctagattttggaatcagacttccaattgaatattgatttttctgctagaaaccatttagttactttgggcaagctactaaaccacctctatgttcatgagtaaatcgtctgcccacccaaaatacagtaatgtactattaagggtaccttccattttacatagtagtaatgcttcagtttttttcggtctaatatcaaatgagcagaggtaaaagaatagatttaacatgtaaatgaagcatttatagcagattgtgaaaatgtcattgcatgttttgtgatgtgggacagagagaaatggaatcatcacttggtctgtggtgatgttgtcatgctagactctaagaatctgcacttaaaaaaatttttttttttaatttcagagagagaaaaagagagggagatagagatagaaacatcgatgagagagaaatatcgatcgactgcctcctgcttggcccctgctggggatcgagcccgcagactgggcatgtgccctaatcaggaattgaccttctggttcataggttgacattcacccactgagccacactggctgggcaagaatctgtacctttggaatcagtaccacagtctcctgtggagcattgttttcatttcagcatagtagcatactgaattaatgttaaattgagctttattagtgttgttttgttttatttaacttcaagttttagtttgcttttgcacctagttaggagttataatcctaaaacatttgaagaaacttttattattttacattttgtctcttcatctctttgattttgtgtttcaggagtgatcaggttagtttcatgcatcatgcttagattttgtgcagtgtctgctgtgttattgctactttaagtgtatattttaattctgttacatttttagttttgtattgtaggtggaggacatgtttttgccttgatattaaaaaaatctttatcctgcttatttacttttgcatttcattttttggtctttatcagcctgttcatctgttcaatggcttttgatataccttttaaagtttaaaatgaagtttcaaatagtttatcttctgaaatgattaatttctcttgttctgcaatatttttccctatgttccatggtggctttccccccatttttgttaacgggttgtgtgagtgtgtgtgtaagtttattttattcatcagcttttagttacagctcaggatttgttagcaaagaagcccacgtaaaaaggatgctggcatactctgcctctagcatcagtagataagattattttggatctactctcttactctgaacaactagaaagg";
1808     auto bSequence = reverseComplement("cctttctagttgttcagagtaagagagtagatccaaaataatcttatctactgatgctagaggcagagtatgccagcatcctttttacgtgggcttctttgctaacaaatcctgagctgtaactaaaagctgatgaataaaataaacttacacacacactcacacaacccgttaacaaaaatggggggaaagccaccatggaacatagggaaaaatattgcagaacaagagaaattaatcatttcagaagataaactatttgaaacttcattttaaactttaaaaggtatatcaaaagccattgaacagatgaacaggctgataaagaccaaaaaatgaaatgcaaaagtaaataagcaggataaagatttttttaatatcaaggcaaaaacatgtcctccacctacaatacaaaactaaaaatgtaacagaattaaaatatacacttaaagtagcaataacacagcagacactgcacaaaatctaagcatgatgcatgaaactaacctgatcactcctgaaacacaaaatcaaagagatgaagagacaaaatgtaaaataataaaagtttcttcaaatgttttaggattataactcctaactaggtgcaaaagcaaactaaaacttgaagttaaataaaacaaaacaacactaataaagctcaatttaacattaattcagtatgctactatgctgaaatgaaaacaatgctccacaggagactgtggtactgattccaaaggtacagattcttgcccagccagtgtggctcagtgggtgaatgtcaacctatgaaccagaaggtcaattcctgattagggcacatgcccagtctgcgggctcgatccccagcaggggccaagcaggaggcagtcgatcgatatttctctctcatcgatgtttctatctctatctccctctctttttctctctctgaaattaaaaaaaaaaatttttttaagtgcagattcttagagtctagcatgacaacatcaccacagaccaagtgatgattccatttctctctgtcccacatcacaaaacatgcaatgacattttcacaatctgctataaatgcttcatttacatgttaaatctattcttttacctctgctcatttgatattagaccgaaaaaaactgaagcattactactatgtaaaatggaaggtacccttaatagtacattactgtattttgggtgggcagacgatttactcatgaacatagaggtggtttagtagcttgcccaaagtaactaaatggtttctagcagaaaaatcaatattcaattggaagtctgattccaaaatctagatttttcctctagactcaaagcaaaagtctgtaaaaaatattctcttatccagttgtttcatgtgagacttggtaataaaattttaaacagaattctaagaacctaatttctaagatttgaaatgtatttacaaaaagaagccagaagactttaattgttcgattacaagttcttactcatagactcttactatctagcattaaagttattaaaagactgtgactggttctcaacttcaggacaactagtaaaacctggccagagagaaaggggcacatgccttcagctttgagagattacaagtggcttgtgcctggctctaaaccattaaccttagggtatcactcagtatatttgacaactgaaacccaccaaatagaacaaaaacctggagtttattatttaacatttaaattcttccaaagttcatgtaccagatggtaacatcctattactcccaaaagaaattttaaacagaacacttaattaggagaaaaattacctattaattcaagcctctaaggcttaatttgctattactatattttcacccatctttaccttaaaaatgaataaatcagctgatctgaatcaagggaactgtattttgcttttatacccaatatgaactactctagcatcatcagctacacttccactgcagaaagcattaactttttatgttgaatattctttcttgagggcactccatttttatagcattaactcaaaaacagtccctattgtcttatgaataggaacacagagttctagaaatattctaaacaccaagctaatcaattgcactatataacaaatgtttaaaataagcttttaactaaaaccttccccacaggtcctctttgacccaaatgttctcagacctcccaagaaatttacagaatgagaaataatgacccttctttaaggttatgaaagtcaaggagcccaaagaccctatgtcattgtatccaactaaatatggctcactggctggaatagtgaaaaatcaatggttgttactcgggagaaaaggggaagtcaatgatggtgtagggaagctgcagaatccaccatgttgcctaagtgccagccaaaactgccaaccctgacaatctgtccatgccagtcctagctcagcaccagatttcttcccaatgctgtttctgtatttgttccatctaactctaatttctgacacagcaccagctacaacactaatgacttgtgtgacaatgggcttctgtttttcaatcagttaaggtaatctccaaaatctagtttatcaatgtgaattatataaccctttaagctttgagttcaaacccccaattttcagaactatagattcacaacatcacaaaaatgtgagcaaccatgaggtaaatgaggtaaatgaatccaggcactaggttttagaataacatcgtagactgttactgactagctaagagactatgggcaagttagcaaccattctgtgtcccagttttttatcttgtaaatgaggaaaataatagtatctacctaacaggattggtgagagaattaaatgagttgatagtttaaaagcacctgggatatgctaagcttacagagaaattcccagggaaactgtgaaaaggcaggtgctgcttctctacaaaacactacagatgccctgctaccctctaacactgctcagcttcacatattataagcaaagaattaaatctatggccctaaccggcttggctcagtggacagcgtcggcctacagactgaaaggtcccaggttcgattccggtcaagggcatgtaccttggttgtgggcatgtaccttggttgcaggcacatccccagtagggagtgtgcaggaggcagctgattgatgtttctctctcatcaatgtttctaactctctatccctctcccttcctctctataaaaaaatcaataaaatatatattttttaaaaaaaaaaaaagaattaaatctatgttagaaagattcaaggggtcaacaatgaaattatgtacatttcaaggaaaagtagtcaaaaatatgacccaacatgactcttgaccctaagacctcacaaaaccaacagcaatgattctcatagttctcttgttgggtgaggtcacctcaggaggaaaacaccccatcgattatttatattccctctcaccaactggcaaattatgttcacaaaattaaaccccaatcaaataactatgttaaatacatatataaaacatccatgtcctgaggcttagcaatagaatcatttataaaatctaagttaataataaaaattcttgtcttccctaaagcaaaagagttgatctcattttctaggaaatcccttatattttaaaaaagaattgctcatttcaatgaggcaatagatatttagcagcgagaaaattcttaacagttgagtactggttaaaacagattaaactgtaacgggattttttttttttttataacaagtttcacagcaagaggacttgtataagttgtagcatagcctcaaaaataaaaacatcaatttccagggttcctccctaaacaatgcacacattgattaaaagaaaagaaaaaatgggtaatactaccatgtacttcaaggacttcaagaatataaatagagcccaactggtgttgctcagtggttgagcatcgaccaatgaaccaggaggtcacgtttcggttcccagtcagggcacgtgcctggattgcaggctcgatccccagtaggaagtgtgcaggaggcaaccagtcaatgattctctctcatcattgatatttctatctttcccttccactcccttcctctctaaaatcaataaaaatatgtttttaaaaaaagaatataaatagaaagattcagataaatcacatgatccttagcacattacatatactcatttttaggtgtaaaatattccataacagtaaatattttaatgctaagaaaacaattttcattgaggagcccatcagaatcacttattgtgagaaatactcttcctgagttgcacatcttccaatagattatggcagatgctccccaggtaacttataggtggggcgatgaagatcactgctctatcaagtgagtataatgtcaggcaatgagtgacattactcctctgatttgttggcagggaaaatcactgagaaccaactgctttaacaggacaagaatttggagaaattcaggaggagactctagaccaaactaagaaaaagtctgaagccaaagaaagacacttgctctcctgtgtcttattttttataaagtggctttgcatactatgtacttacaactcagatggaaatgaagtacctgttgaatgtatttacttcccatatataaagcccccagagttcagcattattcaactcaaggcctaccacaaaggcttactgccatagtagaacatactttgtgttttaaatgcttattgcacagaagtttctctaattcttaatttcatttcccaattgcaaattcaaagaaaatttgctactataagcaagcatctaatattcactatgacaaagtaggattgttctctgctattagtgtttttaattgttcactaaaattaaaaggcagagtattaactcccattattaataaagtagtttaatctacagcatttaatgagctacccaaatatacaaaatctatagctccatctgtcaaatacatcataaatcatgaaaatcttttagctgcaaaaatccaagtttattttctaaatgatagtgggaaaccttaagacagaagatacatttccctgtgcacaagcaattagtttccaattacaagcaaataatgctaactattcttctgttttataaaaagaaaataccaaatcttcaattataactaggaagttatgtgacaattcagtatatatatattgaatatcacacactagaggcctggtgcatgaaattcgtgcactcgggggggaacgggggggtgtccctcagcctgccctgtgccctctagcagtctgagagcccttgggagatgtccaactgccaactgccaggctcccaccacccccgatgcacttgccagtcataagcctggcttctggctgagcggagctcctcctgtgggagcgcactgaccaccagggagcagctcctgcattcagcgtctgccccctggtggtcagtgcatgtcatagtgactggtcgaccagtcgactatctgccccctggtggtcagtgcacgtcacagtgagcggttgagcagacttagcatatcattagtatattatgctttgattggttgaacagaccaccaggcacttagcatattaggcttttattatataggatatgtatatatatggcaatcattatagtacaaatataattctagtcttattaaagtgtaaactcatattgaaaacatggagaaataaatttaattggatttccaagagtttgcagatgaattaacaaatttcaaaagagaaaataatatagattacattaacaaaatattaataaaaaaatattccttttactccagaataaggatatgtttccttcttacacccccatgtgaagtaagtacaaactggcacttccttgggagggcaattttgaaatatctagcaaattgtaagtatacatatccttcaacccagcaactctgttcccatgaatcacatatgattacagaaaaacatgagaaccatgatggtcattggtgcattatttacaataccaaaaaaaaaaaaaaacaaaaaaacttaatacacctatactatgcagtataatgtaatttcaaaagaatgtgtcatatggaaaataatacatatattactaaatgaaaaagtctgcataacaatacatatgattacaatttataaagcaatcacatgtaagtttatgcatgcatagaaaatgtttgaaaaaatatataagaaactatcaaaactgttacctttttggaatgggactcaaaagggctatgagatcggagttttattatttaaacaatgagcatggattactttaaaatttttttactgtccttaaaatttttacctccccatgagagcccaaagttagtaactctcagtttaattcatatcctgagttctttcccctctgacttaacactttcaaattcaatagaagctaaatgtatatcatctagttggcaatctaactgatgagctaacctaaatttatactacaaaatagtggagcagtttaaaagttaaagctgttaacaattattatctcctttagctcacatccctaatactcagctctgcattcccaagaaggtaagagaaaatacaaacatgcaaacatcttacgtgttctatagcacccgcctgtccgttactatagtctcgatactgtccaagcatctggtctacttgtcgcaggtttcgactggtgtccttgagaaagaaaatggactttattagttgatgtttttcccaatcagtcatagtagtatttcaaacagctaagtcaaccatttgcaaaggtacaatttatatcatagttaagtcagatatctatatttcataaatatcaaataaatcttaacacaactgagagagaaactaaaatccatctgcaatttacttgtgtgaaatctaaagtacctaatttaaatccttatccaaggttccgtaagtcagcaacaaaaccataaactggaaaaaaccctcattatcaacttgtacacctcaactaattcacatcccttcatgaaaaagaatgctaaaagaactaatctagtagattatctacaagagaagcaaatcattgggattcatgttatttcaattattgctttatctgagaactgaattaggaggggggaaaagtatcctttaacctttagctataaacaacaaatttagctctaatcataacaaaaatggtttctacctaaatatgctaggtttcacctgagagattaatggaggtatttctttcaaaaatatatacttactgccccggctggtgtgggtcaatggttagagcgctggcctgcccaccgaaatgtcttgagttcaaatccaggtcaagggcaggtagctgggtcacagataacaattcccacccccaatcagggtgaatgtgggaggaaccaattgatgtctctctgtcacaatgatgtttctctttctacccaaccccccttccttccactctctttaaaaccaatgcaaaaaaatagcctcactccttaggtgataatttaatacacacacacacacacacacacacacacacactaatgtgtctctgtggtgggaaagtacaagaatttgtactccaaaattcattgccatattaagtttaaaaagaaaaattaaatcaaattatggcctctccactagaaattagcctttttttaatctaagcatattaacccagagcaataaaaagccaaaatccatgcttatccaggtaaaacaaaggtaagggagtaataagtaggaggaaaaaattctatttctgagttttctttttcccattgtgactgtagtaccttcccccacctcatttcccaagccccttttatggaagaatgaaaactgccctcactcaaatctccaaacactgtcaaagttagcctccaaatcaaaatggactctcactattattcatccatgaataatcatagtttggcttggatgaataatcagtaactctcagtttaattcaagtcctgagttcttccccctctgacttaacactttcaaattcaatagaagctaaatgtatatcatctagttggcaatctatctgattgatatatttacccaagccaaaatatgagcaatttctccaaggcacatgtccacaagaagctaaaggatggtaatccctaatgttttcattcctataatattttatttggaaatataacactttccacaacctatcatatatttcaaatatccttactaactgtggtaagcagaataagggcttccaaagatgttcaaaccctaattcctggaactttacatgtcaaaagggattgtgcagatgtgattaaattaaggatgctgagatgagcaatatcctggattactgggtgcactcaatgtgatcacaaatgtccttaaaagtggaagagagaagcaggagaggtagtcagactgatatgctatgagaaggactcaacccacccctgatggctttggagatgaacggggccacaagacagaaaatatgggcagcctagaagctagaaaaaggcaagaggagaggtatttccctagggtctccagaaagaaatgcagccctaccaacaccctcattactgcccaatgagattagtgtccgattctgacctacagaattttaaaaaaataaatttctgttatttaagacattaattttgtggtaatttgttatgggcgcaaaaaaactaatacagtgactgaaactattactcagcttaactatttattacttaatattgatttaaagcatccagtgttaaactaacctgtaaagtacttgtgatagtgttgaccttcttggtgacatcgactgtaggacgatttctaatatccctgtgtattgacctggcagcccatcgactcaaccggtcatgacagcggaagtggtctgaatcactggaggattctgccattgatgtacacaaatccttaaaaatgaattcagatcatggtcaagcaaatggtaaagagaatgggaataagagattaatttagctatttacttttgatcttttacagtatttattacattagtcctttggtttatcataataattgcatacaatttgaaaatataagtcacttcctcacgtaattctccctcctttgttttttctgctttaaaagagcaactcctgcccggccgatgtgactcagtggttgagcatcgacctatgaaccaggaggtcacagttcaattctcagactattccaaaaattcaagaggaaggcacacttccaagctccatgaagccagcattacaccaattccaaaaccagataaagacaccacaaaaaaaagagaattacagaccaatatccccaatgaacatagatgctaaaatcctcaacaaaattctagcaaatcggatccagcagtacattagaaagatcatgcaccatgaccaagtgagatttattccagagatgcaaggctggtacaatatatgcaaatcaataaatgtgatacatcacataaacaaattgagaaacaaaaatcacatgatcatatcaattgatacagaaaaggcatttgacaaaatccaacaccctttttttataaaaaaaaaaaaaaaaaactctcagcaaagtgggactagagggatgatacctcaacataataaaagccttatatgacaaacctacagccaaatcatactcaatgggcaaaaactaaaaccatttcccctaagaacaggaacaagacagacatgtccactttcaccactcctgttcaacatagtgctggaaatgctagccataggaatcacacaagaagaagaaataaaaggcatccaaattggaaaagaagaagtaaaactgtcattatttgcagatgacatgggaatgtacattgaaaaccctaaagaactccaccaaaaaactactagacttaataaatgaattcggcaatgtagcaggatacaaaattaacacccagaaatctatggccttcttatataccaactagaggcctggtgcacaaaatttgtggacaggggtgggggggggtgtccctcagcccagcatgcaccctctccaatctgggaccccttgggggctatccgactgccagtttaggcctgatcccacagggaccgctgtctcctaaccactcgcctgcctgcctgcctgcctgattgcccctaaccgcttctgcctgccagcctgatcgcccctaaccattcccctgctgacctgattgacgcctaactgctcccctgctggcccgatcacccactactgccctcccctgccggcctgatcacccgcaactgccctcccctgccagcccaattgcccacaactgcccacccctgctggcctgattggccacaactgccctcccctgccggccccatcacctacaactgccctcccctgccggcctgatcgcccacaactgcccttccctgacctctaggtgaatgagacaaagctcaaccaactgagccacattggccaaggctcaacttaaaagttgaaggactatgtgaaaacacattaatgtgtactcacataaagtatgcaatcaaattctgctttgaaaaacaaatttaaatggttcatgaattatattaaccacaattacaatatgaagcccaaagaagaattattaagactggtagaaaaacatctatgtatgctcaaggaaaatcttcatcattcttttgaaatattcaaaaggaaaaagttcagttggattaaaaatccattctcattatcacaagaagtttcaacatgacatctgttggggggaaaagaattgctaaatttgaagaacgattgaagaactcttaaagaacaattttaaaaaactgaatcacccaaattctgattaaaggagagaaaagcgtttccttcaactggagaaaaagcagaaaatattctcttactaccaccatctagtacaacaaaacttctcattgatcaggaagcaatagcattgatctgtctgcttttcagacattgagctttctccaagactagagaggactaggacaggcaaccttgggtgaaagtctccatagtcttagagaaagctctgggtctgaaaccagatggtagaactctaaaaatactttatttttctgttcccaacacatcagtatttaaaggcataaatattgttgcttctgccttaacgagccttataaagatgacataccgccaccagagtgagtaatgaagctcctcaattcgctcccactgaggaaaaagtccatcaaaaacaaatgcccatgtgcaaacttgaacactgtgagcagggggagtaaaagtgcatttggccctgtaataagaggactggcagtgagtctgtacaaactcaagtccacctcttctgttatttgacctaaaaagccatcagattgtcctcattataatgatgcatatttgtatgtatgagagctgtaatttattgattctcaagtctcttttcctcagagccagtttaaaacaattagaatttgcaagctgttccataaaagataagcacccagcatcaagggtgacattttacaaaagctgactagtgcatgttaagtaggtgattacttctgtggcctccaagtgcctcagccatccctgtccaaggcaactgtcctcaaagaacacattcaaggaaatgatgttgatatttaaggggatcacaggatgtggggagtgtattctaagcttaatcattatcaaagttcaaattttagaaggcatttctcactgtttcaacagctgcaggtcttgagagctttgcctcctccttcaagagacaaagtgaactagccaggaaaagttatcatgtacaaacaattctcagtcatgcctggcttcttgatatcaatactcctctgtaacttaatctcctcaaacccttttggtaagttcatataagatcaaaatcaacagccaattccctgtgaagtcaatagataacataagacataggggaaaaatcaaagagatacatgaatcctggctgggtgactcagttggttagagcatcatcccctgcaggtttccggttcaattcctagtcaggacacataccaaggttgagggtttgatcccgatcagagatccactgagaggcaactaatcaatgtttctctctgacatagatgtttctccctctctctctccccctttctgtctctctaaaatcaataaaaaaaatatcctcaggtgattaaaaggagagagagagagaaagttgaacatatttgtgcaatcatttactcaattatatctgcattagaaattaaagatgaaagtaagtaaattaagtgacagtccttgatgggagataaagagctgtaaaagtgctctctacagggagctaagtactacaacaaaacgccggagcagtggggtcaggatcctcccacccatctggaggagcataagtttgacaaatagatgagacaaggataggggatccagttagaaaacttctttttcagaagaactagagaaggtacaaataagcaaagcaccttgcattcttccatcagggaatggatttaaacccatctccagtactgctaaatgtccctggggtagggaggggtgactgggtaaaatcatcccactttgagacttctgacttagagaattaatgtttttaattattatacacacacacacacacacacacacacacacacacacacacacactagatttgattttatattgtacaagaggcccggtgcatgatattcgtgcacggcgggggggggggagggggtccctcagcttggcctgcaccctctccaatccaggatccctcgggggatgtccaactgcccagtcgcccctaacctctctgcctgcctgcccgatcactccaactgctctcccctgcctgcctgatccccctaaccactccccccttttcgcctgattgcccctaactgcctctgcctcggccccaccacagtggctgtgtccggaaggacgtctagacggtcatccagaagatgtctggtctaattagcatattacccttttattagtatagatgttccacttattcctgttgctgccctccctttcctccctctctctctcttatacacacacacacacacacacacacacacacaccctctatcctatgggtggagcgggtcaggctcagctcccagatgcactttgctcccctcgctccagctcctgagtccagggaaagagtcacagggaccagtgcccaccaagcagggagcaggggcatttctggaaaaggacaagcagcatacaaaatgactccttattcaggcacagagaaaaaaattagttgttgctcagataattacatatgtgccacttgttaaagatcaatgcagttttaaaagaaagttacttaataacctcctccctgtctcacaagcagaatgacacagatattttagattcatacacccacacacacttctaaaataaatgaggcagtttacaaaattaagtaaaaagcttttaaaaagtcaagattaaaaataacaacaaaaactcaagaaaattacagagaaatgctagcaaactcgagatggatctattacttgtagttacaccctggaattggacttttgcattttctaaccgattgaaaatgaaatatatactacaatattcttaaaatagcagatacgaataaagtcctctgggggtacagacaacttctactgattgtccaaactgccccaggctgggcaagagatcagcctggacaaagatggagctagaatcccgagtttctgacaccactttgcttaaatcagaggatgaaaactggaggactatagaccaaacccagctgactaacatgtatttgaaaataacactcactatgttattttaaaacactaattttaaaacaactaaatttcatataaaaattttaatttccagcttctcttaaaaaacccagagatctggcaacactgggatgaattttcccatggcaacatggacataaaccatgaatgccttggagttgggcatgtacctttctgttggcctaagttcccactagctagctgcttcacccagtgtgttaaccgattggtcccacttactagcaaagtttgcaaccctattttgaataaagaaccaagacaaaggaagtttacctttttaaaggagtttccctagttaaaaatgaccaatttgattgggaagaattagtcagggttaaatgcttgagagttaatgtaacagctttgatatcagccatgcctactaactccaaaaccagaacaaaataccactacaaacaaaagggcttgattttaatccatctacttagaacattttcctactattatatatatatcaaaaactaaattcagataaaaaatgtagtacctttcatgatttagctccaattctttacccatccctacgtgcactgctctcagaggtcgagttacctgcccaggccccttcctctgggctcagctatgtgacctgctttggtcaatgggattctagctaacatggcacaagtggagtcacgaaaaggcattacacactggaacttgcctccctgcacctatcccatctccaagaagacatgcccaggatatcctgttatcctgttggaaggtgtgggaatacatagttgcaccagaggcttccagcttaggtcagcagaccaggtgatcagccagctgaacccagataaataagtgagtccagtcataataagcagatagcttagctgactaactgtacaagcgataaagttattgttgtactagaaacctggtgcatgaatttgtgcacaggtggggtccctaggcctggccagtgattagggctgggccgacaactggggggggggggggacaaagagagggactgcaggccaattggggcctgccagctgggaggagggatggggtatgggaggttggcctccagcctctacaagggaaccggccctcagccccctgggaaaggggtcacatccaccaccacccaccctggttccccatcccagccctgggccaaaggtcccagcagggttgggagaggaggcgacggtcacggaaggaatggacgccggacgctgaagctgccgaccagccacaggaggttggctgtgggagcgcactgaccaccagggggcagctcctgcattaagcgtctgccccctggtggtcagcacacgtcatagcaactggtcaactggtcgttccagtcattcagtcatcagtcgcttaggcttttatatatgcggacgccttgtatgttttatttgttatgcagcaaaagctaagtggtatatatattgtgaacactgcgaaagaagatacctaccaagaaggtctcctggtggctaactgggctcaaattttaaaaacagagactagcagccatttccacacaaactgcacttcacacagaagcctgcactgaactgcctgcctgcactgtttttctgtctgctgagccagcccttataaaggaagtgctggaatcctatttcaaccaatgggactgaggcttcccccatccaaacagagcttacagctttatccccatcaacatcttcactgaccaatcagagcagctcttttctgaccaatcaggaaagtgccttttggacaaatccaactacaaggatctggagtccttgtttgcatgaggacagaccaatcaaggtcacaggcaagggcttctgtctatataagccagctcccctctggctctgagagtgcacctttcctttccaccaaagactgatccctggctgagataaaacaccaaagatatctctgtctcactggagcagagctgaacagaccaacatgggccagagaagacaggcaccaaacacagaatggagaagagcagaccagcacatagctgaacagaccagcaaggaacagagatctctagcagagaggggcctggccccaggtccacctcatcggcttgctgttttcacggctatgctttatcacaattgagctgttttgcactgaataaattttccttcttcaccacaccccaaattggggattcctgttggcgttgaattggcaccaatgatcatctgttgacaatatgtagtttactaataaatcattttattggttcagaaaaaatttaatttcctgaagtttcctctaaagactactctccttgagaaaacaagttgttttcagactgtcttctggtggtgaggagggagtggggagggcaaattcaaattaggatgcacaaaataagcataagcaattgatatgacaagtgacagactagcaattattgagagagaaaaactaggcaatcataaatagcaattggggctatttaatctactccccataacattaagattttcataagaaacaagctgtttcttaaatatattataaagcataatacttggtttttttcctcctattttttaaagtagaaacagaaactcctcttgactttcctagtgaaaaaacaccactaaaaattttaagaaagggaaatatttctgactgctatttatacctcaaatttctatgttgctttatagcttacaaagcattttctttagcactttctcttttgacttccattcaatcctacaaagtgggtataattgcaactctttattttactgctaagaaaaacaaagctttgaataaaatacttgcccaccattgctttgataaaaagttacagtaacagaactagaacccatatcttgtgtctctaagccagtgcttttccactacatttctaggagcagaactgtgtaaattctaaatacattgcccaatcactgaaatattttctttgtccatccctatcatatttcttatcaccctcactcccccaagaaatgcatttaaggccagagtatgtcatccatatgtgcttatctacttaaaactagagatttctataatgaatacaagataatatgtaaatatcctataaatccaactttctacaactattttaaaatgtcttttaaataaccctttctcttgttaagaatatttacctcttcaggcagaaaagcattctttttttttctttttttttttcagaaacatcataaaatcttaaattttattttttaaattgtattttctttttggtgtacacgtattttctatttctctttttcctccactcttctcaattcccatatttcttccttgccccacacttactttatatatttagatctttccttttaactcttctataataaaggactgaaatacatttcaagagttttgtatttcttttttatcattattaaatttattggggtggcattggttaataagattatataggtttcaagggtacatttctatgatacatgatctatatattgcactgtgtgcccaccatccaaagtcaaaccattttctgtgaccatatatttggtcccatttatcctttactaatcctccaccccctctcctctggaaactatgagtttcagttttatatcccatatatgagtgaaatcatatggttcttaggttttctggctgactttattttgcttaacataatatcctcaattttatttcttgttcttaattttcaaagtggataaacagaataattggcatattcttttcaaaatgtatagacttctaaattttaagaaaaaaagctaattgaagttcttaactttgatttccccatgaactgtttcctggaatgtttttaaatgtacataaaaaatttttaaagatactggattagatagaaatccaattataatgaaattgaattattgggagtaaatggatgaggactgaagttaagaacctcaatttcaaaaaccttcaagaaactcaagttgaataatgctgaataactaatgtttcaatatcagattgttgtttgacaataaccctacaaggtcagtacactgtcatgatatatacctaagaggcttaaaggcactaagaaaggtactcaaggctacacagctactaagtagcagagctgagtcttgaatctttctccaaggtccagatttctgaccatctaaccgtgctcactgccacgtgcactactcataaatttaagactttttctcaactcatttctagccaccaaaatggtttctttgacttttgagtcttttcctatacattccaactccactggccacctgactgaatctaggttcttgaatcaggaattcaaggccctcggcaagccacctgtcttaataaaagaaccacagaaaacttaaggcctacattcagctattacaacaactattcttcccccactgccacccctcattaatgtcattaggtctgggtggaatgccttctgctatctagtattgccacctatttaaacactcacttgtccaggcatagctcaaataacacaatctcaatgaagctgtccacctctagccagaagtgatctttccttcctctgcatttgtatagctctataattatagcaaaacatttatgaaatgtcaaaatatctgcatgaaatacaattatttgtgccacagaatttttttaaatttttctttatgctcacctaaggatatgttttattggtctgagagagaggaagggagagagagagaaacaccaatgtgagagagaaacatcaatcagttgcccccacaggtgtccagaccagggatcaagccacaacctaggtatgtgccctgatggggaatcaaacccaaaacttttttggtgtaagggatgatgctgcaatcaactgagccatctggccagggctgtcacagaatatgtcatgttcattttgtcatcaaacctatgctggattcaccacttggaacattcttttcacttcgttttccctttgctaacttctacttttctttcaagtctcagctcaaaggaaacttcccctagaaagtcttctttcactccccccacccctagcccaccaagtatccatagtggataccaactcaacactgcccagagattgctaactggctatttacttgtctctctccccactagactggaaatcttaccagagactatttatgtaatccatggttgtatttcaacacctagcatatttcctggcatgtagttggaacacttttaaatgaatttacaattacattttcttattatcctgtctgattaatccagagatgaatttcaaagtacaggaccttgcaattagtaaatactaattaaataattgttgggtaaaatgattcttgtagaaatggcctagattaattatcttacatctccaaacaatatggattaaactgcaaaatagtaaaaatatgaacacttgataatatgtcctttatagagaaaacaaagataaacactttaaatccatttgaataaatgcgttaaaacaaatgaatcacacttaagcttgtttgaattattgcaaatcatttaaatacattattgtcatctacagctatctgcactcctcaattattatttgtctgtactataaagcactacactccaaattacttaggttctttatcatctaggtccattctcagtatgctttcaatgtaccaattgctgctcataagcaaacccatttcttgccaatttaaagccttcagtctctcaccatggagccttttccttttttctacaatctataaaattactttatctcccctagagggtattatggtaagtgaaatagtcagccagagaaagacaaataccatatgatttcacttatacatggaatctaaagaacaatacaaacaagcaaacaaagcagaaacagactcatagacacagagaacagactaatggttgccagaggagagggggctgggagactgggtgaaaaaggtgaagggattgaaaagtacagattgctagttacacaaaacagttaagggaatataaagtatagcataggaaatatagtccaaaatattgtaataactatgattggtgacaggtgagtactagaaatatcagggggaacattgtaaagtatatgattgtctagtccagccgtgggcaaactacagcccgcgggccagatccggccagtttgaaatgaataaaactaaaataaaataaaaaaaaagaccgtacccttttatgtaatgatgtttactttgaatttatattagttcacacaaacactccatccatgcttttgttccggcactctggtacagtttaagaacccattgtggccctcgagtcaaaaagtttgtccacccctggtctagtcactatgctatacacctgaaactcatacaaaataatactgaatgtaagctgtacttaaaaataaacaaataattttttaaaactgcttatctcttctttctttaaaataacaatcatcgttcacttcttccacaatgtctctcttcttcacttatccattcatcctgagcacctgtgtcatggcaaaccctgtgcacattgtagattctatgttcttcagctaaatggatacatataaacacatatatgtccatctcccaaacacccacacaacagcattgtactgatcaatgttaagggttacgtgaatttcagggaggagtgaatatctgataaggaccctgtagcctgttaggagcatttacaattattgataaaaagatactagaagcaaggttagggggaaagaaggacaaacggaaaaatattcgcattatatcgaaaccaaaggattcatcatctctctgaaaaaatgattttaaaagaacaaaccagtactaaaagatgcaaagggtatataaacaggcaattcacagaatacaaatggccctaacaagtatagaatgttctttatattcctaatcaaaaaattgaaaattaaaataataatatgatatgcttgtttatctgttttgtacaaatttaaaaaaaatagctctcacctccaggattgacaagggcacaccttgctgtaagaatattaactgaaatagatagcttttctggagaacaatctggcaagacaagtaaaaaataaataatatgcgtatgttttgacccagatagtctaattctatgtacagggtagggcaaaagtaggtttatagttgtaagtatgcacaacagagtttattcttatattattatttattaattattgtattgttttccatacgagcaactataaacctacttttgccccaccctgtatagtatcctaggaaaacaaaagatgtttaagatgtatgtatacatttcatgtgtatatgtagaaagagtttaattgcagaattaaaatagtaaaaaagaaaataagaaagaattacaaatctactgataagggatacattaaatatagtcatttggaatattatgtagcagttaaaagagacattatagaattttatgtactgacatgtaatactacattattgcaacaattaaatacacactacagaataacatgaatatattgaaacatttcatattagaatcagctcagtgatactacatggataccactgaatctactctagattatttttatattattcccaaatatgtcaagattacatctgagaatttgtgattatatgaccttatctggctcttccagctctaaaattctttaattctaagaactttgtgcagcatagatcttggacagtgttttcataaggccctaaagggtctcctatataggtttaagtgtcccttatcaaaatgcaagaggaaaatgctaaaaagtgtccagagttttacctctacatctgaaaacttgagttcttaatactacatccttcattatcaatgacaattcatgctgaatatgttcattttacagtaatcacgaattataactctttacctcaataataatgtaagaaccattatatcttggaaaaactaagataatagaaccctttgaatatttaaagagattaaatagcccatctcccttcctctctgaaatctataataaaaatatatttttctgaataaataaataaaagataaataacagtaaattttgcttatatggtactcttaatctaaaaatgctgactgtcaatcaaatcccactgagggcaatcttagacagcaacggcacctcttcatactgggcaggattgagatttgtagaaagaacacccgggaatgaaaaccactctttgtgctccagaatccccagcaaatcacatgtaccatctgagcttcaactgtctcatttataaaacaaagaatcctatataataaaagcataatatgaaaatcgaccaaacgtctgaacagcggaatgaccatccggacaaccatccgggaccatgctatgacacccactggcgccaggccagccaaggtaggtgcgatgcgattggtcggaggcccggccatcgccccattatcgccccgtggaggaagacccaggccaccggcctgcggggcgatcaatagggggctccacatcaccccaaagagggaggccaaggccaccgactggtgggctgtggcaggtggacagggcctccctctgcgaaggaagcccaggtcctggtgccggcagccaagggaagaaaggcctactcttgtacaaatttcatgtatcggaactctagtagcataataatgtccagccagcatggctcagcgattaagcctctactatgaaccaagaggtcacagttcgattcccggttagggcacatgaccaggttgtgggctcaatccccagtagggggtgtgcaggaggcaactaatccatgattctctctcatcattgatgtttctctctctctccctctcccttcctctctaaaatcaataaaaatattttttttaaagaatagtgtaataaaaatattatatatatatgtcacatagaaggtttctattactgtgcctggggcaaaataggttctcaaatgtttgtctaatggaaacaattgcatcatttttgtttttgttaaggtatctagtctgttcccttgaatttttataactctcacgcacaaccattctatttaaattttacgctgttagaaagggacttcttgcctctcaaaagcttatttgttgacatactcttttccactataaatatgcaaaaatgtgtttagaaacaccgggagaaagcatacttacaaagcacacccccaaatcgccgtgttgctctccactgagctcagtgctcttcttaggcacatatgtctgcctcctatcatttcttcatgaggttcagggcaaaaggcctagtcaagtagataatctttggttgcctctatattttccccaaaccacctacagataaatgaaacaaggattaaaacacacctacaactttaaatttgttaaagaaataaaagatgaaaataagctcaccctcactatatacagttcatggcatatcagagaacagagaaaatgctccttgggtcatgcaaaagccccactgaactcaaaaaaactctccacatccaagtgcccaaccaacatactcctcacttgttacttctgcccatgcctggtcatccctagggctggccagatcatgcaattgtagcccaagtccacctaaaaggactctctctttcagcatgtctatcagagggtaaactaagtcaaataattaatttaacatgatacttctgaatttaatgacccagttgaatttctaagccagttaacgaactatatatgctagtacgcctaaacgatatatttcgtaagctacccagtaccacacacatttagtactgcaattaaataagtaaccataaaatgtctttggttgtaagaaagtctgtggacagaaatattttcaattctttgtgactgcttttaaagcagtctgtaggaaatcacaggaagtatccagagaactagtagttgtcacataagaagacctgagactacatctataaaccacttcaagaaaaagtaaaaaactatcctggaaaagttaatcaaaataaactgtggagagctccaaacaacacaggtcttgcaatggcagagcggacacgctgagagcactcaggttttatgcatacttcactcttctaacagagcataaccttcaccccatgggctgggtcacagacacatcccaatttctctgtgccaatatgaagctgctaaagatttcaccttgaatctacactgtgcaatacagtagtcactccatacgtggctactgagctcttgaaatctgcccagtttgagctcacaggtgctgtaaatatacaatgcacattttggacacttagaaacattaaaaacatatatacatttaaggctgtaaaatgcctcaatcaataagtttccatactgattacatgttgaaatgaaaacattttgtatataaataaattgtgctaaataaactattaaaattaacatcaccagtttcctttttactggtacaatgtggatactggaaaatttaaacgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtctcacataatatttctacttgacggcactgttctgcataattagtaaatataaataacaaatactgagtacttccaatgtgtcaggccctgtgctagatactttgcttcatacttggaacaacgttacagtgcggtcctacgaggagcctgcggaagccactgggccccatcaaaccgatcacagttgcagctggaattcaatgtcagatgttcctacgaaataactcatatcacgggcagctaacgttggggcaattcccatggtggcacaggagaggccgatacagcgatacagcgtctgtgactagagaacaggtacaaaacggccagcgctatcccagctctatcccaggccaggcgagtcacctccaaaatgagtccgatctccttgacagctcaggacatgccgctcgcaaaaggaggaccaggaagttcaaattccctgaatagcttggcacagaccaggctgtggatgcccatgcggagggggtcgcaggccgggcaccccgtctctccccaccctggcgctcacactaccaagccgcggccccaccccacatctccgcgcggccagtgtccccacccctgggacaaggagaagtgggacttaaaacgtctacaggacagggaagtggcaaagaaagggccacgggcggtggcgggcaattacctgagattgagagcccaggtccttaatgccggatcatcgccgaggccccacccggctcccacggccacctgtgaccgagaaggtgcaactgacccgacgcggtggcgcaacgaactgagctgtgcccagactggcagctatggcaacttgaactagccgcccagccgcggcaagagtacaggaccctactggtgccgcttggccctctgggagttgtagtccgcatcgtcctctaaagcaatgagttaactaagggactctgcccctaaaagaaactgcaagtcccaggatgccaaacgattgtcttcggtgactttcccggaagaagcaggattcgtcggcacctgaggccctaggacgcaccctctttccctgttttgtttttttaaagggccagaagcgtatattgaagacaaacaccagctagagagagagctgccttggcccagactgtaatttaatcaggaaagtctaagatatttcctttacttaaaaaaaaaatcaaagtagttgcaatactaacatagactatatttttgttattccttggggaacaggcaggtgtaatttccagcagagaaatgttaaaatccccttcgtgtaataataccagtgcccaatgtcttgtggttcacaaattcttagatccggaaggaaacagaagattatctagtcacaccttttaggcctggaaaaagtaagctcactgatgacaaattaaaagtggaatttgagaatgtcacttcaaaactgctgccagggtgtcatctctccgcttattgtggaacctgaaagagacatagtccgctcagagaagtcggtttcaattgcgaaaaagtaactttaaaaacttcaacaaggaactgagctttggaaaagctaagaagaagtgaaaatccaaattcgtcacgaagatggtcctttccttctgctgttgatgctttctttcctcctcaaggctaaagtcaagttttactttatctgtgaagccttcctacacagataaagcaagatcactttaacgacatggattccctatcctaatcattgtgtgctgtaattatccattccctgttggtacttacattagattctaagcctttcaaacacagggacctaacctgttaaaatttccatcctcccttctctctctctctgtctctctctctctctctctctctctgcctctctctctctctctctccctccctctcacacacacacattaataattatgatctattatatcatatttgatatatcagaagcattaaaatatttttcctggctggccaatactgtgaggtagatgttattttataaccggtttgaacataagatgattattctaaggacatacctatgtatttgcatctgtcttcacgttgcttgctcacagctgtattcaagaatacactcatgctcgcttcggcagcacatatactacaattggaatgatacagagaagattagcatggcccctgctcgaggatgacatgcaaatgcatgaagcgttccatattaagaaagaaaagaaaaaaagaaaagggaagggaagggaagggaagggaagggaagggaagggaaggggaagggaagggaaggaagggaagggaggagagaaaagaaagaggaaaggaaaggaagggaaggaagggaaaaggaaggaaaaggaagaaaagaaagaaaaaagaaagaaaaagaaaagaaaagaaaagaaaagaaaaagaaaagaaaagaaagaagaaaagaaaagagaatacactgcaccggacacaaatgttaacataaataggaacagtcattaaattgccccatgcacatgcagaagcattagagtacctttatttgtttacattttaatttttcaaaacaattgtactaaaaaaaacaattgtactcattcatttaattcagcaaatgtatgctgaatctttctaggatccagacattatattagacacttgaaatgtaatgaaaatgagaataataacaacctagtttctgccctcatgttgctcagagtcaagaaaatatgaagaagagcagtgccaagagaaaagggattcctgagtgggaatgtgataagaaacagagaaaagagtcccagaatggagagttggagatttcggggctgggggtgcaatgcagacctgaaggtggtggaggaaagcccatgtccatagactgctcagcttctactcaagcaagtactattcatagtctggacctcgctgtccgcgcattggcgtgaagagaagacagcctgcattttacccagtggttgtccagcacaagtagtaaccatctgttccattccacattgttccatctagccactagatggtcgagcctccttgtaatatcaccagacaattataagggataagaaggaagacagagctccgaagtgggatgctatgggtaaggttaaagagaaatctccaacatcctaattggaactacccaaaattctctctgaaccaaagcacaaaatgatcattaatgattgttaactcttcctgcatcaacttttcaagctaactgaaaatctgggaaagttaaaatagaagacaagatgttgcaaagatggtattgccaactactcaaggaatcatcttcagctattcatgtgtgccattgatcccataatattcttttcagccacacacttattaattataggtatatgtcactgtcaagagcatcacaaatgtaaaacacagcaatgtgtttgcatttaagtcaaaatgaataactattaatgcacaaatccaaaatcataaaatatttttattatagctaactcagatagcctgtgctccactttaataggtacctttgtgtgtggtcggggggggggggggtggataaacactagacagtaatgccatgctaaatactactttctgatatctggaccatgctaacaattcttctaacaagaaggaaaatgtaatgttcttaagccatttctcattcattctgtttatatttaaaagcatttttgtggttaatggatcagaatgtaaacacttcttctcttacttaaactggttgatctctcatcccatgcttttattggccccagtttcaaaagttaggctaaggaaagaaaagtgcctactgattatcatagcaatttgttgagaaggttattaaatattagaggcccagtgcatgaatttgtgtacaggtggggtccagccagcccagccccaataggggcagattggtccaggcctgtagggaagaggagatggcaggaggtttggccagccagttcgccccaatcagcagatcagggtcgggccagctggggggagggaccgtgggtgattggccagcaggccctgcccccgattgggggggggggggggggttgggcaatcaggggtcattggccaggggaggggccatgggagtttggtctgcaagccccacccccgattggtgtgtggggtgtgatcggggacagagctggctggggggaggggcctcaggccgattggggtggtggggacccattgggggtgaggccagccagggggagcggcttcatgcagttggctggctggccctgccccctattagggtagagggggaaatcaggggtggggttggccagggggaggggctgcaggagattggtcagctggccccacccccagatcaggccggttctctggccacagtgggcgtcatagcggctggtcgttctggtagttacagtgtttcagttactggctttttatatatagagatggttaccctgaagatttggtccactttcctgaaatgaaacaggtaagcatggaagattttacctacattatgaccaccactgagccaatttcatgttgcctaagtcttgattaaaatgttacagatttttggcataccacttatgttcccagcaaagatggaatagcatagaagcagagagcagactttccctgcacccagaatttttaaaaagtaaaaagacaccactagccagcatggttgagccagttgacctatgaaccaggaagtcaaggttcaattccaatcagggcacatgcctgggttttgggcttgatccctaatggggagcatgcaggagacagccaatcaaggattctctcatcattgatgtttctatctctctccctctcccttcctctctgaaatcaacaaaaaaatatttttaaaaaagaaaaaggaaacatacaaaacatatgaaacaatgctttttcagacaatagacattatataaaggccagtgatttcctaacagacaggaaagaaacaacatgagccttgtgatccccacaacttcctaccttgagagaatttccaggccatggctcaaggagaaggaacccaggcagagctccgtggatgctttgagttgacaagacagagatgggagtttgtgaaaaccagtgtagatggacaaagtagcaaagagaagagagctacatacagagaaaactctaaagaactagggaacgtcttcttggaatattcagatgaatacgtgaagaaacggaggctggagaaggaagcatctaccaagactggggaggagaaagtacctgtcactcacacagggccaggaatgatgattatttccaccagccggatggagacacctcttaattcacgtagtattatgtagagtgctcagaagagtcttgcctcaggagggaggagtaattggtgctaaattaaatgctattcccatcccacctcacaagccttgaaagcaagacccaaaaatatcaaactatttccaagtaattttcccacgtctgataacaaatctcaaaaatatttctatgaaaacaaataatccagtacccaaagaggtgaaatttacaatgtctacaatccaaataaagattaccagtcatgcaaagaagaagaaaaatgcaactcattttaaggagaaaaatcaaccaattgaaacttacccagaactgataaagatattagagttatcaaacagggacattgttaggggtagaattgtgtcccctgaaaagataagttgaagttctaatacctggattgtgactttatttggaaacagggtgtttgcagatatgcaagttaaattgaggtcatgctgaattaatatgggccctaatccagtgactgatgctttcatggaaagagggaatttagagcagagacacaagagagaatactgtgtgatgacagaggagagactggggtgatgcgaaccaagggcaccaaggactgctggcaaccatcaggatctgggaagaggcagggaaggaatcgcccttagaaccttcagagggagccatgaccctgccaacagcttaattttggacttccagccaccagaactgtgagtgaattcatttctgttgctttcagccatcaagtatgtggtactttgttacagtagctctaggagtctaacacagacatcaaaagtgttttagatgtcaaaagttacatagagacaggggaaatatgaaaaaggcccaaattgaacttctagacatgaacatttcacctgacataaaaaatatactgatggaatcagtggcagatgaactatttcagaaaaaaaaaaaaaaagaatattgaatttgaagatataaaaaagaaactatgcaaaatgaaaaagagagaaaattaatttgtaatttaaaaaagcatcaatgaaacaaaaaacaaaaacaaaaaaagcataaatgagcttttttgtagaacaactttaattagcctaatatacgtgtaataattggaaagagtagagagacataaggggacataaaaataaatggagaaataatggcagaaatttttccaaaagtgacgaaaaacagaaattcaccctagcagtatagttgagttggttggaacatcgtccagtacaccaaaagatagtgggctcgattcccagtcagggcacatacccaggttgtggattcagtccccaatagaggttgtgcaggaggaagctgatggatatttccctctcattgatgtttctctctctctctccacctcccttcctctctctctaaaatcaataaaacatttttaaaaacaaacataaatgcaaaaattcaagaacctcaaaatactgcagaagaaatatgaagaaagtacatcaaagcatatgataatcaaaatacttccaaccagcgataactagtcttaaaagcattctgagggagaaaaaagcatgttgcaaaagaaaataatgacagaacatttgtccttggaaataataagatagtggagcaatatttttaaagtactgaaaggaaaaaaatactttcaatctataactctgtgcctagtaatgctatccttgaaaaatgaaggcaaaattaatacttgttcagacacaaaaaagatggaataatgcatacccaacagatctgtaccacaagaaatgttaacaaaagtgttcaaagtaaaagcaagattatgtatttttttaattgattttagagagaggaaggggggagtgagattttatatatatatatatatatatatatatatagagagagagagagagagagagagagagagagagagaaacgttgatgtgagagagaaacatcaatcagttgcctcctacatgtgccccaaccagggattgaacccacaacatagatatgtgccctgacttggaatcaaatccacctttcagtgtactgaatgatgctccaaccaactcagccacactgaccagggctgttttttgttttgtttttttattgaaaaactagaggcccggtgcatgaaattcgtgcatacatacggttccttgtcctggcctgcgatcaaggccatcttccccagctgcccacagccggccctgcccccgccgccaccaacccccggttccctgtttgccattgaatgatcggtgcctgacggccaggggaagggaccgagaggttggctgttcctgcccactcgccagccccacccctgccacggccatcaggccatcagagcctgctggccagggaaagggaccaagaggtggtcagtgcacctcatagtgactggtccagcggtcattctggtcattttgccattagggtcaatttgcacattacccttttattatataggatgattatattggataatattccactgtatggatgtatcactgtttgtttacccattgcctagttgaggaacacttgggttgtttccagtttgggttaattatgaataaagccctaaaaccattgaaatatacaaattttaaacatacagtccaatgaactttgaaaaaaattatgcattctcttacctccagtctaattaagatatagaacatttctataaccctggtgagtgctctcctatcttttttgaatcagtctcccctaaatcgtgttctgatttccatcactatagttccccccattctgtcccagaaagtcatataaatggaattacacagtacgtgaagaattgatgttcttttcatcccaagaagaattagtaactgaaggcttttaagaaggaaatggcacaatctgatttatattttaaattattatttttggctactgtgtgaagaatggtaagtagatgatggtgtgccacttaagtgttatttttcttcccatctttggggggattttggggaaagcggagctatatgatgcaataacattcctggcagtccagaatttacctttaggcttcaagccaggtcggaaggccggctccagagcaccagacctgttcctgccattcctggttctgcccagcagatggccccagagcctagttcccggcccgcctagagccaagtccgcctgctgcaatgtgagctgacgcctccaggtgactcctcaatacaatttctgtgcaaagcggtcgggtttgcgctgaagttggggagcagtgagggtccgccagaggtattgcgccccggcttctccgcacctcaggctggcatcctcagaggcaccttaggccagaaggatcttgacccgcgtctgccctggacggtgcattttgcgggaagttttctctgcagtgcgtgcgagccttgggggagaagggggagcgggggagaggggtcctgacaggcagcgcttttcggaatctacccacgggaaaaggtggcccactggacttaacgacaagggaagggcggcgggggggggggcgctctccggaacatatggctctggaaagagctaatggaaggaacgcgcgaggctggctggaaagcagggtggggacagcagggactggtgttaagtgtcagggaacagacagagcgggctgcgtggctcagggctaggaaagtaaagccaatgggcttgtttgcgtggagagttgcctagggaaacagaacacttgagagcctctttgccctctccagcctcctctttggtgctgaagtcacaaccaccaggagtcctctttctcccactcctccctcgctccttccgcgcggggagctcggagcacagagggctgagaatgaggcgatagtggaggaagaagaaatggcctcggacccctggaaaatgaggccgatgcccctactgcagctggcgctgcttctcggcctgcccaggagcttaggggggaaagggtgtgcttctccgccctgtgagtgccaccaggaggacaacttcagagtcacctgcaaggatatccatcgcatcccagtcctaccgcccgggacacagactctgtgagtacctgggagaggagagggtaggtcccagaggccaagggcagccggaaaggtggacggaagtgcacaaaaagaacttgagaacaggccaaagggtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgcgcgcgcacgcgcgcgcaatgaataaaaactcaatcccataaatttgggattgctgacctcatagttatttaataacacaggaaacggtaacaaaactttggtacaagatgagctattctgcatattccctgctcctcgcccaggctaaatcttccttttacctcaggtatgaacaagattagtagcaattcaagaactgacttttctatttgatattgggtaataaaaatttgcaaaaaaaatgtatttctccttttagtcttccaaaatgggaaattatcttagctggtcttgctggttcatcaaatagttgaaaaaatttagtcaggtattcttttttaaaatataccctttatattttccatgaaggtaactttgtatgaagtacctgagttactttctcaaaggttaactgcccactaacatagtggatccagaagtttcctttgccataggcatgggagaggaataaagaggtaaaatttcataaaatgcattttatctgcatattaaatgatgccaaaaattgtcagatggttattagccttattggggtgttatcacttcatcctatataataaaagcctaatatgcaaattgaccaaacagcagaatgaccagcgggacgaccggttgctatgatgtgcactgaccacaggggggcagacgctcaacgcaggagctgcccccagcctgcaggtcccaagccagcctaggcaggtgccagtgggggatccccccgatcatcctgtgggtcaccctgcagagggaggcaactggctgcagcgacggggtgatcaggggatggggcgatcaggggatggggctagctggtgagcggcaccaggccggccaagatgggtgccagcgggggcccccccgatcaccccaccagtcgccccacagattggtcctgatcactggcctggcctagggaccctacccatgcatgcattttgtgcaccgggcttctagtaagttatataaatgtctaatcactatgctgtacacctgaacctcatgtaatatgtatgtcaactgtaatagataaaatttatttttaaaagttgccaaattaaaaacaagggtcaacaagttctattgtatgcttattccttaagaggcatttaaatcaatgaccattgaaaggataattacagacaaaagccaggaaaaaggctttcttgggtggttaaaaatggagcacaaagaaaaaaaaaaatagtaaaacaacctaggctggttaaacctagttcaggtaagataatacaagttattaatgtagatacatgaaaagcaatgatgaagttgcacttcaggattttctcaggactgtagctgatagggacattaaatagtggtgaatgttgtttggtgattaatgttgcagtacaggttctgtgctctgaggggagtgcaggaagctgctgatggatgattctctcatcattgatctttctatctctctctccctcccccttcctttctctgaaatcaatataagcatatattttttaaagatacagattaggggtccgttctttccagaagcctctcttgatctatgacccctagactgggtgagctccccccccccccccaacattctgtgcacaacctctatcctagtccttaaacatggcatgcttttctgtaacttatttctttgcatgcctaccaagtaggacacgagctcctctaacacggaaaccctgtcattcaaccttagaactactttgatgttcaaaaaaaagtttaagtgagtgaatgaccgaacataaatatgctagtctcattattcgctgtgaggataaagaaggccagagagatcacttcttcggggacttctttaaaggtggcagaattccaagtcattgaagagaaatgaggctctgacagtggaagtgaatttggattcatcctgtcaattccaccaaatgatgaggagaaagggtaacatcagagaaagagcgatggaggaggagtcaggaaacctgcattctctttacccccctctcactgatggtgaaattaggtgagatcatggatttagaagaacccaagtttccttctgggtaaaacagagggtttagttaggtgctttctgatgtctgtgatgctcaagagggaatccactgagccacaaaggctaagaactggatagagacaggagcaaggagaatctgaccttaaaaacaattagggggaggaaggcggtgggactattctgaagaattaagttattctgagatagagaagagaacatttaaaagtatgggtaaagtggtatgctaatttaaataatgactacaaccatctacaacagtagcaaactgatttttgaaaattttgtggggcaatagaactctactgccccacaaagttttgaaaagtcacagtggattcccttctgtatgtcctctttcaaaaagtgtctatttaggtcctttgcccattttttgattggattgtttatcttccttttgttaagttatatgaattccttttgttaagttgtatgaattctttgtaaattttggagattaaacccttatctgaaatagcattggcaaatatgttctcccatgcagtgggctttcttgttgttttgttgatagtttgttttgctgtgcaaaagctttttattttgatgtagtcctatttgtttatattctccttagtctccattgccctaggagctgtgtcggtaaaggaattgctacagattcaatgcaatccccactaaaatatttcaaaggtctagaacaaactctccaaaaattcatctggaataaaaaaagacaccaaatagccacagtaatcctgagaaagaaaaacaaagttggagggatcacaaaaccagatatcaagctatattaccaagccacggttctcaaaactgcctggtactggcacaagaacagacatacagaccagtggaacagaacagagaacccagaaatttacccaagccattattatgctcaattaatatttgacaagagaggcaagagcatacaatggagtcaagacagtctcttcaataaatggtgttgggaaatttggtcagatacatgcaaaaaaaaaaaagaaaagaaagaaactagatgaccaacttacactatacacaaaaacaaactcaaaatggctaaaggattttcatgtaagatgggaaaccataaaaatcatacaagactttacaggcagcaaaatggcatctattttcttacttcagctctgatagcatagtaatacacataaaacatattattaagtgagtgaattttatctgctttatcaatcaattgaaaaagttttttccctccagagctgagaaattgttaacttgaattgatctatttgattattttaaaatttccttgttgtacaaagtatgtttattacccttcagatgaagattaattacctggaggttcatgggcttcgtgggggtccattcagtcctcctgggaatctgtgatgaaatgaaattttgaatacaagtaaactgctgaatctataaccttgctcagatttccagaggtgcccttaaccccacccccaaaaaagtcaagaaattgtgttttgggagaatttggcatttttgtttaataactgtatatttggtattaaatgtgactcagtccttttattactagaggcccagtgcatgaaatttgtgcactcggggtgggaggtcctctcagctgggattgcaagacagtgcaggccaggctgagggacccaaccagtgcacaatcggggtcagggagggatgcgggaggttggccagccagggagggaccctgggagggctccagggcgtgtcggtcccatctctctcagtcctgatccgccagaccccagcagccagctaacctaccagtcaaagagtctgctcccctggtggtcagtgcatgtcatagcaagcggttgagcagccttagcatatcactagcatattacgttttgattggttgaatggcaaactggatgaccagacacttagcatattaggcttttattatataggataaagaacactaaaacatatttttaaataaataaaaatttgcctgaccttcggatgtgatattttttctctgaaacactgtattacctcgtgatgtcttgcacttagcccttaaataggctcttcatgcagaagttcactaaatgtattttaaagtagtttacaaacagattttacaagtttctgatgcctcgtatctgacattaaatgctccatactggccctgtttccattttcactgacgctgggtgggatgcttaattaccattttgcctttcattggtgcaattaaaacacagttgaaggaaatgtatgggggaaatggtcccatgtgttcaaagctgcaaattggatttttaaagggttccatttaggtataagactctacttactttgcttgactttggttgaatccttaagaacattcagggtgtttcaaaaatttttttcttaaaatgacaaagcagcagtcgtgatttttaaaccatcttataaatgttgttctcttacatgtagtgacatgtaaatatgccctggatttttctgctccagccctatttaaaatattttaccctcaggatagtccagaaactcaaatatttcatcatcatcttaactcaattttccacacccctcttgcacctgggaagagttccctgtgtcccagtttttctttgaaaaacctgatccctgtatttatatgatatgtgaagccttgggcttacatcttggaggcattagcttaattttacccactcctgagtgttattgtaattactccagagcagggattgaaagcgtctcttcttactgccagctactgtcactccgtattttgttatttccatcttttgtgaacgggaaaggagaaacctgacctcagggaaatgatctgtacaaaacaatgaacataacagtgttgagtgggctttgtgactgctctgtttccttctcaacactcttctcagttatttctggctacgtggccatccaaggatttgagatgaccagaaaccttacctctggacaggtggcattttattgcagcttttcttccttcctaccattcttcttttttttcaataaatatttattgatcacctatatacacaggtactattttaggcattggtgatatctaatacatgacctcattgatctgacattctagcagggggaaatagccagataataataatactaataataataatacacatagaatgtcagatggtgataagtataatggagaaaaattaagggagagggagaaggaatgctggaagttgcattttaaatggtccgagaaggcctcactgagcaggtgacatttgaatatcaatcttgggagagaaattctgaataaatgcaaagacctcgaggtaggagtacatgggaggagcatgcatgtatattttagaagtaacaagaagagaaatgtgtatatttaagaagtaataaagagagaaatgtggctgaaccagagagggttatggaaaatgagatcagagaagttataagacagatgatgtactttgtagacactataagacattaaatatgtatatgttctaaatgggataagaagcttaataggatatttagttcaaaaaaaatcacatgaactgatttacattttattaggatcaccctggcaactgtgtttgttaatcagtaagtgatgacaatggcttggaccagcttaacagcggggaaggaggtgaaatatggttgaatcttggattttatgatgaaggcaaagccaacaggatttcctaacagactgtgtggagtctgagtgaaatcaagagttaatgtcttagcctgggttctccagaaatcagagcctgtgtgacaattcttactggggtgtgtgatttgggggagcagaacggtaggatgggggaatgacattggcagggagggagagtcggctgctgcttcgaagcaaatagttgctgatcccatgggactccctgtgaattccgatgctatgcatctcaggacccttgctctaggagggaggaaagggaaattacatccatcagcttctagtcccctgggtcagggtgttaattgcaccaaccttccatgtttcttgtgtataaatgaagaaagagttcccagatagtgacagaaaagctccagggtggaaggtaagaccctgcacttgactctccagtcactccccatcccctctctgcccattgcccaagcaaccactaatctgcttctgtctctatgaatttgtctattctggacagtttatataaatggaatcatatcacatgtgacctctgtgcctggctttttttacatacttagcagtgttttcatgtttcatgcacactgtagggatggaaagggtgtgggcagccagacaagtggcctgaggggaaaacacaggtcaagaggccaagctttaggtactttcaggatgtttcaggcagattcatttctgacacactggagtagagagcagtcccactccatgtctaaatgccttacaccttatatctaaccatctgaacagcaagaatgcccatgcttattctcagaggcttagaaccatgaaattgcttccccatgtcacaggttttaagagtccccggccagtgaagtgtttctagccagttggctgaggaacaccaaccgcttatcccttttgcatcccatggcaatcttcgtcaacacttctagaaagaggtgacttcatagcaatgcatgagtcattgtccgttaactctttaccctttaaagagccaatacttatgcaaaggacttttctctgttctagggaagctgcagaactcaaacagtatcctttttgtaatgggttttctcataataacattggcttctatgtcttattgtgtttctccattaaaaaatttaatcaacatgctaataaagaaaatttgctatgaatttttttaaaaaaggttcaatgagagaaatagagtagtcttccccttctgtttaccagagcttacttctcagctatttctttccagcccactctgggcttgggtttctaggaagatgtgcattcaaattctagctctccaccaactcactatgtgggagaccatttgctcaactcacaagatgaacaaactaatgagatctgctgcagctacgtcacagagtggttttgaggatcacgcgagataatgctagcagctctacatgaaatacctggtaaaccagaataaagctttgatcacaagttaactattatcactaatgcagtagtgggtctgagacccagacccggcagtgtgggcattacatcagtttgtagaggtcaagctctcacgcagggaccattcagcagctgttaggcttgagggctgtgcagccttagtaagaagagcaaacacatgtcctccatgcaaaagggacattttaaagatttacaggatttcttgtcttctgccagagatacttatccctggggagttactgtttgtagtgtaaagtggccgaaattctgactggaccctattgggtatttctgtcccaatagggtccataactctgtaagaaggattttatctgttcaatgtcagattttggctgaaaagcctgttagtatttccaaactctccagatgtccctacttaatgcttggctggtcttcttaggtagcataaccaagttgccatttcacaaccagcctccatccccaggatctaaaacagcaaatccagaagctctgaaacgtgctctatgaagcatgaaaggatccgttcaggttctgtctgattcagttctgtgagataatagacattctaacgtacctatgaattccctttattcataagaaacttggcactttccagaaagtatgtctctcatgaccaagaaaagtagggtgacttggggatgcaatgctttctttttatagtgtgtctagtatcttcattttcaatagcagcagttggtctcaggaccacaattggtacttaccatctctatcctccactgactattctaaattccccagctctcatcccttctggccttgatgacttatctgatatgatccaagatctcattcctcagagatctaggcctgtggtaaccatactcttctcacactagggttgctagaattgactattttagtcataattgagcaagtactatgacatggccaggtggattcctgaattctatacatatttccccctgccatcattatataacaaatgtctactacctcttcctggtgatcagaatcagttactcccatcaattactgccatcctatgtccctgaacataaggaatccaaaatgtccaggtaggaatcatagcttgtagttccctgcagagcctagaattgtggggatgggaagcacagaattccgtggtgggtcattgaccataatggcaactgggaccacttctgcttctgtcccttggttcctagacctatatattcttgctaacaggacacaacatccaaatagtttgtaaatcattctgtaatctgcatgatggtatcctatcatggcagaagtattacctacaagttagcacttccatgtgcctttggcaggtgactttattgctctgtaaaaccaattgcttctggattgtgtggcacttgatatggccatgggcttacctccctatctccttctctgtgaagtgagtctcatgatcagatggtatgttatatgagactgtatgcctagatcagatactttgaaatctcccagatagtggtgctagctgaggctctaggctgccaagaataagtgcctactactataacaataaatcactggcccttccagtaagaaaaggctcaaagtggtcatcttgccaccaagtggccatgtggtcccctcaagtaatagtgttacattgatctctcttgcaaactggttggactctcaatgatagtagtagtagatcagccttgataagtaggggagtccatgctattgacatgtaatgttcattctgttaatgtgccaatcatgctaattctggagcggccaatgacaaaagatggctactgtcaactaatcaattttgtcttggctattcaatgcttcttctgtggtggccaagctttggtggtcattaatatatgatacgaagatcttcacatttcatacccactctcatatattcatccacaagcatctagcccagatttttttctctgcaaaattattaactttttcttcccaggcccctgactagctgggcaatccttttgctcacgattccatatgtagtcttatctcaggcaacttttccatgaaatcctcccattaggaagattttctcttgctgctgtctttcaaggccacatgcagccaaattcagtatgttcattttaggtggacatattgaaaaaacatctacataccaaatcaaatgacccattaaccaagttcagttatactggagcaaatgctgagacagagttgggaatgcatgtggtttgcaggaggtaacatctgtgaaagataaagagagaggaagcaggacttggtagatacagcctcacattctctgcaaattggacaaagtcttggccaacccagtggggaactcttagtgaagacggcttgtgagaggggccccacattgggcataacagccaggccctagtgcccctgccatgctcatccattagctgtgggcttaccaggaagattagggccttggttcaaacatgtgacagatcccaaatgtgctacagatagaggctggtagcaaatgtcttcttgacagttaaacagaaagctgtttcttggagggagatcagagtggcacatttccagagctgtcacatcctttcttttttttttttatcttttcagtttgcacttccttggatttttctttacaagcttcttttcctctacctaatacttacttgatgtttcttaggttctcttttcaggccacacttccaccagccaccttgtcaactcccattcacacatcaaatgagactctcatgaatttccatttcagatctttaggctaggtctcttctgagcactaggtctatattatccatctgcctataaagatagcttacagattcctaacatttagcatttctaaaagtgacctcattatcttcttagccaaaactctttctgtgtagggatctcagtgaatagttcacattttcccagatcctcaaaacaaacagcatagagttatccttgatactgtcttctctctcattcctaatatcaaatccataatcattaccaatctaaacatgtcttcaatctatctcgattccattcttgttattgctatcaaagttcacaccaccatcattgtttcccacctgagccactataaatggagtttcttctgtgcttattaaacacagcagccagagtaataattataaaatgtactgtggatgaggtcctcaattccctgtgtaacagatcattggcttctattgctctcaggggagcctaacagtcctgaattcctctttcagagtgatgctgactttgcaattgttttatcttctagaccaggggtgggcaaactttttgactcgagggccacaataggttcttaaactggaccggagggccggaacaaaagcatggatggagtgtttgtgtgaactaatataaattcaaagtaaacatcattacataaaagggtacggtcttttttttttttttttaattttattcatttcaaacgggccggatctggcccgtgggccgtagtttgcccacggttgttctagactattggcacctaaggtcagggattgaatggtattcattcacctttgcttcccttaggatacctaacctagtgccttgcacatagcagattcacaaaattatccaggattgaagtgaaccttcagtgtgctggctagtagatctttttaaccaatagccatcttatcctgggttccctggaaaacagagttaacttgctaatgtgagagtgaagaacaagagaagtaaggcggggggtgggggggggggggccgtgcttccttcccaaagagacaggagaaacccagcaggtaatgtggcaagtgcctctgcacggccacacggggtacctgtggacaggctctgtgaaggccgtgctttgcggcagttcatgagaaggaactcacccacctcctgtttctcattgatccaagttactccctgcacttcctttgtgtcatctggccccttctatggctccctggaaagatggcttccatgccctattgggaggcctttcatttgagtccagagtggaacacaggtgcatgtaagatgtggccaacaaactatccctgaagctcagcctgttgtgacttttactgggttagattttaaatacactgatgctagaaacacaaaactggtggcagttttctttaagagagtggcctgcaacaggaataattgttaaacgttttatacaagtaaatagtgactgtgatgaataagaagattaaaacacagattgaacatgtcctgatttttctctcttaatcacgagtaatttgcaaatccatacatggctttttaaaaaactatatattctcatctgatcttatgattcaaaattaaaagacctataaactcctcttcccatgaaatcactcaatacagtacaaaagtccatcctttagcctatcactctctatccccatatgtaactcatacctacaatgaaagttgctcaccagcacacatgacaaaaaaacactgaagtgggaagggggtgggggggggattcttttctcaagcaagtggttctatccacttacgaaaaagacttgagagcttggacagacttcaagagcctctgaatgtttttcttcagcagaaattccagcccagatgttcaataacccaggtttgttttcaaagaggaaataaagaaacaaatctcataaagacaggccagactttgaaagcaattaatgatgacaggtccaaggattctgcttcattttattattaacgctgccactctttccagattccaatgtcagatactggtgaaggagaagtaaaaaaaaaaaaaaagtctggaaattgaagataaattattagtgggaggcacccacagaaggggaccttttgaagtagtgttcagagtggagaaggagaattgaagggtcagttgactgccaggggatactgtgaagttagcagatgaaaacggttgacaaacaggagcctaaggaaggtctaatcataaactttacactttgaacagatcattgcagatcaaaatcagctgacatattggtggagaagctgctttattttttttttatgtgttttgtttttttaattattccccgaggacatagttttaatgatttttagaaagagaggaagggggagagagagaaacactgattggcaacctctaatacacaccctgactagggatcaaacccgcaacccaagtatgtgccctcactaggagttgaacctacaaccttctggtgcatgggatgatgctccaaccaactgagccacctggtcagggcgagaagctactttaaattcatttacaccaacaagcccagagagtgataaagcaatcaactcataaatctcagaagtcactatgtggaatttccagggaatttggggaaataaatgttagactagaaaggatcacttagttctggttttcaaaaaaattaaagggaaaaggtacttattaaagcactgacctgtatgtgctggtcagtgttttgggttgtgggttgtataaaaagtgtaatagcctttcttcttgagtcaatcacattccagtcaggaaactggacgtaaacttttctctcatgcaatatgttaaagtctgtaacagaggtaaagggcaatgaacatggaaagaagaaaaaagatttaaataattgctactatttgtatagtggtttagaatgtatgaatagcttacattatcttattttatttaattctcaaaacaattctgacggagaatcctcaacttacagaataatcagaagctcagagcagttaagtggcccagctatttgactgtttggatgctctttgcttccccagtgtctactccctttcctgggagatttgaactatttttaaatgaattattgtgcaatacaaatgccacacatgactacagttcttctttattcttggaaactttctcctatgacccaggaaatgactactatttgtttatttcagaattttctgagtcacgttgctctgtcatagtaagctttcagccaaccagcagaagtgccggggtccaaccccagcaggtccaggggtccccaaaggtgtggacggagttggcgaagaaggaaagacacagagacagcgttcagctgatcagcagcccatccaggatctccagccaagttctggtctggatctccagcgaagttctggctaggatctccagccaggttctgtgtccatgttctcttgctaggttctccaggttctccagccaggttctgtagccatgttccctcgctaggttctccagccaggttcagtcaccaggttctagtcaggttctcttgccaatttctgtagtcaggttcagtccaggatcttttgccatgttctctccagtgaggttcttctgtctctagagaacgttctgtgtaggttctgtgcctcctggttctgtctctcttggttctgtcttcttagttctgtgttctaagttctgtcttattctaagttctgtgttctaagttctgtgtcttgctgtctagttacatctgtatttataccagttgattcaatcctgtcaatctctatttcaaaggttagggtgtttcttatctccattccagggagtaaagattatgtagcttaagcatgactgttcatagttaaagtgattaattacccgcctggcacttagttaagaggttttattccctccctaacttcaggggaaaatccctacctggggaaacaccctttctcagagaccttggttaaaacacatagtgccaagaaggtgagcaaacatattaagaacagtatgccatatatgccaggtcccttgaaacagcaagcatggaccggctcccggcacagaaggagcaccaacatacaagaggattttatctcaaaggtttacttctagttgcctaagggggccagaaattcagaccaggtcctccagggaaggaaatgttgtcccagtaattggggaaaacctgggctttaagcaatggcaccactaaacatccaatctcttgtctacgatttcttgggattctgacagcaataaaggagggaaatcgctcttgtactggtttctccttggccctccagtggctcctgttttcccaaatcctaaagaaatagaccttgtggactgaggtgggtggagctggtgggatggagaggaaaagaaaaaagtttcttctcgtccagcttccactcaccagatctgttcttccgaagcaaattcaagacttcacactggtctaccaagtctctgctctgctctctgggtaccctgaaaccaaatctagtaagcttctaaacctcagtttcttcaactggagagaaaattactatttaaattattctgctaccactattatcatacagttagttacttctattttctgtacttaatctcatttaatccacatgtcagccttatgaggcaggttttattgttcccattttaaagatgaggaaactaaaggttcaggaactcagttgaggtcataccgctagaaaggcatggtgctcggaccctcgggccccaaagccccatctattcatctctgttgggaagtattattcttcttagagtgacaccatctcacagttacaccctacgacctttgttatccccatctcataataattccatgaggtgaacaagataaatattgcctctgcccttccctttttacctctttagccccatttaaaaatatatatttttaaaaaaactaaagctgcttggccatggttactcagtggttgagcactatgggctcaatccccagttgggggcatgcaggagtctgctgagcaatgattctctcttatcattgatgtttctctctctctctcccctctcccttcctctctgaaatcaataaagacatatttttaaaaaactaaagctaagcaaatgcaaaggatttttccgaggttacatagataacagatggagttggaattccacaggctttagtcctgctattgctacacacactgctttgtctcctggagatcaagacccagcattgcctctagctagcactttaaccctcaaatggtcatttaaatcctgcagttttgtatatggaaaaaaagatgggaatagcaataataataatatctgtcaactaccttgaagtattattacaaagatcaaatgaaatattatgcataggtgtttatggaataagtagacaatatacaaaccaatataattttattcttaggactatcctgccccatgtgtgtaagggggagaggaattgatgtggaagatataaagagcattggagggagcgggtcatcatttccttgctagatttattgtttcaattggatattttttgagtgctggcactgtgctagctaatggaaatggaatgctgcacaagataatttggccttactctcatgaaattgcattccagatggacggccaatgttgataaaaaaaattagccgaaaccggtttggctcagtggatagagcgtcggcctgcgtgactgaaaggtcccaggtcgatccggtcaagggctgtacctgggtgcgggctacatccccagtaggagatgtgcaggagggcagctgatcgatgtttctctctcatcgatgtttctactctctatcttcttcccttcctctctgtaaaaaaaatcaataaaatatgtaaaaaaaaaaaaaaattacaagtgtggccctagcttggtttggctccatagatagagttgtcagcctgtgactgaagggtcccagatttgggggcacatcctgggttgtgggcttcaatccccagtagggggtgtgcaggagagcagatcatgattctctctcatcattgatctctctctctctctctctctctctctctctctctctctccccccccttcctctctgaaattaaataaaaataaatttaaaaaataattacaagtgtggtgaggttctgtaaagaagacaaatagacttccattgtgactttcttcctgagcacactgcaacgccctggtgctatcacttacaaagatcaggcagaggcaactgccacctattagagcactccaaatggcttccaacaatgaaaaacttacatctcctaaataataactattctaaaaagagactaatcttggctcttagtctctctaaagccaaaataaaggcaatgtgatctatacaacattatcttgacctcactgaccactagtcagtccaagaaacaggcgtgcaatacgatagaaggaaggatcaaagaatatgttttataaggataataaattccgctgacatttccccttctgagctacaataattctggcctcttttataatcataccctgttggaaagccacatggcttggaccagtttttgcctactacagattttggtttccaaaactacatctccatgagtaaggagatgggcagtctcaggttggctggctccatcagaaacacttaaaacttgattgcacaattgtatgaccaactggggtcttagcatccacttgaatttcctcattggcaattttgaggatatcatattgcttgggaaagaaagagggactagttattatgattttcatgaacttacagaactttagtattggaaaagtagaccatctgctttaactttctacccagtgtaagaatctcctgtacttgattcatgatagtcgtcctttgaataaaggaaggaaacttactatctcgttgttgaagaattctgttaacctttcctacactggagaaaatctaccaccattggtcttagatgtgtccactaaatccccacagaccagcggttctcaacctgtgggtcgcaacccctttggcagtcgaacgaccctttcacaggggtcgcctaagaccatcctgcatatcaggcatttacattatgattcataacagtagcaacattacagttatgaagtagcaacgaaaataattttatggttgggtcacaacatgaggaactgtatttaaagggccagaaggttgagaaccactgccatagaccaaatcagaaagaacgtcacttctatttaatgactttcctcctccctggtataagaagtcttttctaagctaaacattccagtgctttcctccattacatatacaaaatgatcctctgaactgttagcattttggtttgagaaatcaaggagatgggtgagaaacagaatgtttgtgtcttttacctaacatatttgctatttctctgttttgtaacatcagcagataccacctgtgcccccgttcacactgggaatgaaactgcacagagctactttaattataaagataaaagcaaaatttctccattcatattgccagaggggatggcttgccaaatcattctctttggttctagaaaatgcccaaaaaataattttgactttaaaattcagctctgggtttgaagatgctgccatgctgttcagtcataagaatatataatttttttatttcgattatgctgctaattttagtaactttggcaaaggttataaaaacaggcagtccccaacttacagggttatgttccagcagtcatttgtttggaatgtgttttacatgcttattaaactttctggctggcctaccaaagactttttaacccctaagatgccaggcatctaataccatcccataatcagctgcaagccagaattcactctcaggttttatttaccatgtcccttaagcatcaccagaaatattctagcacccaacactatgtttaataatgtctttctaacttaatatgcccaagaaactactgggaacacaaggaatattttcatgcttcccttctcctgtccaagtgagttaaggactccctgccccagtcaaggaaactaagagaccgggactccttacgacatgagcctgctggtgtctgggagggtgaagttagtggtagaccttctgggggaaaagctatacttctcagtcatcattggttctactcagatttccattctttccttccaagtcagcctgattcatagactcttcattcatatttattctcaatatctactagtgtcttctctctctctttctttctctctctctctctctctctctctctctctctctctctctctcatctcttccttagattaaatttcttttgcatgtacagtattaattccactgatgtcataataatcctgtaaataagaagtgaggacactaccacctccattttacaaatgagcaaaggcttaaagagggtaaacaacctgccctcacagtatgaactataccagcttcaccacagcactgccttgcttcacttcttaaaaacaaaaggtatatatgttagaaaaattttagttttatatttgttgtctcaagttatttttataaatatgcacagtatttgtagcctctttttttatttctaatgcaattatatactagcattgtgttttttcctgtctttttcctaatcaatcttgctgtgatgtttgtctgttttattaattttttttcaaagagccatcttttacattagtttatcaagtttttaaaattatggtaaaatatttatggcctaagagaaatactaattagaatatttagtcatatatagtaacttctaggaaaacagatgttgctttgtttcaatgacactgaacaatgtcccctggaaaatgtgaattgagttttgttttgtcttccctatgtggcttttgcaaggagaatcttaacaggtatacttgattgtcgatacatttaggtgtttttttctgatgaacatgttaatataggatttattagataagtgataaacattgttgctacaagctaaacacgcaggccctgtcttctaggaattttcaagccagtgaaaatgtatagtctctctttgttgtctactatactctgttgtgagtcaagagcctcctgacactgagcaccgggttcccgggagattaagtgatggacctggacctcccagcccgtgagctcggattccatcctccaaagcttactcttcccaccggaccacacgtatggcccacgggtgacagttactctgcccttttagagcctgagaaaacactgtttggtggtctggcatcatctatgttgacgtggcttgatttggaatagagtgtaatcctagttcgtgatgaaaggatttcatgcgttaaccttatgggtcatgatgtttgtgcaggggggaaatgtgcgctaatctttttttttttttttaatctccaccttaggatatgtttttaattgatttttagagacagaggaagggacacagagagagaaagaaacatcgatgtgagagagaaacatggattggttacctcccagacacaccctgacgggggattaaacatgcatcctgggtatgtgccctgaccaggaatcaaacccacaaccttttggtgttcgggatgacactccatccaactgagccatcagccagggtatagtctttaattcatgattaaaacaaatatacccacgagagatgggcctataaaattcaccttaattgttgaaatgcagtaaataaatctttcaaaaatatctaacaggtccaattatttcagtctaaaaggcaattggagatctgttctttatcaaggttccccagccctcctgtgagatggagctgtttttcatttgcttgcaggctggaagaggagtgaaaacaatgtcaggatggtcctgctcagagccctagaagagctgcctttatggagtgtttgtgtccccaggaggaaatgagatgatctgagtctatgggttagaccagagagaggggctaggtattagcagccaattcatgatcttagaagtctcctgactgaggcacatgaggacaagtatcttaaaaaaaacaaaaaacaaaacaaaacaacaacaaaaaccagaaaacactctgttttatttattttttaaaaaatatgtttttattgatttcagagaggaagggagagggagaaagagaaacatcaatgatgagagagaaccctcgatcactgccttctgcatgccccttactggggtttgagcccgccctgactgggaattgaactgtaacctcctggttcatgggtcaatgctcaatcactgagccacaccagccaggcacaaatatcacaaatatctttctgatggtgttagcatccagaattgtgcaggagtagttgtttctaggacatttaaggaagaataaatagatgagacatatatatatatatatatatatatatatatatatatatatatatatatgtatagcccagtgcacaaaattcgtgcacaggagggggggtgtccctcagctcagcctgcaccctctccaatctgggacccccgtggaaaatgtctgactgccggtttaggcctaatccatgtgaatccctcaactggcagtcggacatccctctcacaatccagaactgctagctcctaaccattcacctgcctgcctgtctgattgcccctaacagcttctgcccgccagcctgatcacctcctaacttctcctgccagcttgattgatgcctaactgctcccctgccggccgatcacctccaactaccctcccctgctggcccaatcactcccaactgccctcccctgcaggcctggtcccccgcaacttccctcgcctgcaggcctgattgctcccagctgccctcccctgccgacctgattgcccacaacttccctcccttgctagccatcctatggcggccatcttgtggtggccatcttgtgacaatgtcgtggaaaccatcttgtgatgatatcatgtgaggacgttgcacaatgccacctaggcttttattatataggatatatatacacatatatgaaaagccagacaaagtgcctgaaaaaaaattagctttcaatagatttcttctccagttcctacttgtcctcttgatcttatgcttaacacctctctggacttgtcagtacaagccctgaatgtttcaccaaacaaactacacagttggtgccactggactgtttaacatactctaattaccacaatttcattggaagggaccagggttggcaaccaaactaacgctaagggatttgatcatagaattcccacaccaagtaggaggcataaaatgacttggagacctggtcatagttcctgcagctatggttcatggcatgttagcagaacacagtctctgaaagttatttcctaaaagctttccaaagagctggttcttcacacaaagagttttggccaattgcaatgacagggagaaactagaatctcaggaagaacagaatttaagccttaagtttcattttgttttggatcagatgttccttaaacataaaccttcacctgattctaattttcatttcaattccatagaccggtgatttaagtgttggggatagagttcttagaatctgtttatgtggtataaatacagaaatggtctggttctgagtattaggatcatgatccatgttttgatatgaactcaaaacctctctaagtcacgtgttattttttgaagcacagaaactggacctcacaaacaacctaaatccctctatttgagaaactaaataagcactgagttttcttgccataaagttctgcactgcatctagaatgctttcttaaatagtatctctgggagtcctggtcaagggcgtgtacctgggtttgaggtttgctccctgcccccactctgatgcatgctggaagcaaccagttggtgtgtctctctcacattgatgtttctctctctgtgttcagtttcgagttggcaagattgcaaaagtttgcttgtaaatctgtgggttttgttttttgtggggttttttttagacctttgaatgcatttttttttattgttattgatttcagagaggaagggagagggagagataatagaaacatcaatgatgagagagaatcatggattggctgcctcctgcacgccccctactgggaatcgagcccgcaacccgggcatgtgcccctgaccagtatcaaaccaaggacccttcagtctgcaggccagcactctatccaaggagccaaaccggctagggcttttgaatgcttttttttttgttccagagggtggtcagcgtctcaggccaaaccccaaagcctatttcacctattcttgtattagaaccaggggtagattcagttttgtgtttaggaatggtgagggaagtacatgtttgcttctatctttaatcatttcaaatttgttagaaacatagacttattgacctagataccactcagtgagtttagacagttgctcaagttaataatgttttaagttattgatggtggttatggacatggactttgaagtcaaagagatcgtgctttatctatttacttgatcaattatttttgatatctcaccattaagccagacactgatagaaacaaacttaaaatgatgaatataacttctcttactagcttacactacattgcttttctcagtggtattcagttttcttagttataggagtgggagaaagataaaaacatatgcctcatagagttgtcattaatattaaatgaaattaatgcatggaaaatatcctatataataaaaggctaatatgcaaatagactgaacggtgaaacaaccaaacaaccaaacaaccagtcactgtgacatgcgttgaccaccagggggcatgtgcggaacatggtgggtgttggccatggcaggatggtggagcaggtgagcaggggacaccagaccaaggtgaggtgccattcattgtcatcgggtcgagcctcctgtggttactgaaaattctttgctcctgtgcactgtggtcttgcccagagcttccacctgctaccagcaccggagccactgctcgcaccttcagccagtgctggaaccaccgctcacacctactgccgatgctctgcaccagtcccaatcactcaatgccatcaatgggtgcaagtgtggctgctggccctgatcgcccctgagggcttctccacctccccctgctcctgggggtgatcgggacagcagctgctgctcacacccgctgctagcatcagcctcaatcactccgtaccttcagcaggtatgaaaggggctggcatcgtcagtgcatgggagcggcaggagtgggactgctggcagacaggggaccagggggctgtggtgggaggggctgggcgggggcatggaggatgggctgagacctgcccctatgcctatggtagcctcacggcccacagtttctttcaaggtgcatgaatttgtgcactgagcccctagtttagcataaaatgaaatttcagtgtatggtagccactgacatgactgtggaccaacccctcattttaaagctacaaaagagctcacctggcatggctccgtggttgaacgctgatctagaaggtcacggttcaattcccagtagggggtgtgcaggaggcagccagtcaatgattctctctcatcattgatgtttctacttctctctctctcccttcctctctgaaatcaataaaaaatatatttttaaaaaggagaaagtttatgtgcatagaatgattttatgactatttgagtacttacaaaaatagtaaaaaaaaaaaaaaaaaaaaaaatttaagctgaaaaagactatatcacccaggcattctcactctccttttagcccatgccctcttacctgagcattggctttattgtcagacagccttgccaagatgggcactagtagctttatgcttcctgtccccattgctaacgattctgagaagactgtccacttggaccacatggaataaatttatgagaaagaggatttctatttctagaaagcagggattaaggaaagacatgataggcagacaagaaccatagctaccatacttcactttagtgagacaaagcgggggagggaaaggatataaatgtgtggtggtggtgggtgcacccctatgtgtttgtgaattgggattatgatgtaaagtcagaagcagtggactagaaactaaaaaatgatcagttcctaaagaaaaaaaaataaatcgtgagcactccccaaagtaaattattatatttgcattcaaatttcttcattttacactatttttcacaatcccttcttatttaacacagagaagtcaactacaacttcaggtattccagcaatcaaagtaggactcctttgggagcaattttcaaaaacctattaccatttaatgaagataaaggtgacagttaaagatactcctgggcctacctggagatcacaaggccaaaccagatttctatacaggtaaatgcaagtgatcaacctgcttttatttgcaaagaccaaatcagagaagtacactcatgaaatttagatccagagatataaaagaacaagaaaggtagatcttcccgaataagtttccaaaacaatctgaagctaatattaccaaaatacatgggctatagcggaaccttctaaacaagtatgagtaaccactgagtgaatttatgttatgagcggggtaatcaagaattttcactgctttaccatgatgatcataatttgcatcggcccaacatccttgatctcagaatgatgctcaaaaatgttaagaaacagtcttgccgctatggatcagtggttgagtgtcaacccatgaaccaagaggtctcagattcaggttcaattcccagtcagggcacatgccaggttgctggctcagtccccagtcgggggcatgcaggaaactgccaattgatgatgtttctctctcactgatgttttatcctgttctaagtttacaacctcacttctcaaaaaaaaaaaaaaaaaaatgatgctactcagtagctggtacattgtgttcattcctctgaaagtgggctttaaaattgcagctattacctctgcccagctcactggtgtctgcagggccagaggcttgcctcgttcccaaggccatcagacatacaccctggaagggctccagaagcacagccacagcaggagcacctgaccgatcctgcaccaaaaggtgcatgatttgaccagatttttggaggagcatcctggtggggcagaagtcctgagggaacaagctggaggtgatgctaccaaaaactttgaggacacctggaactctacagatgccagagaactgtccaaaacatatagcactcgggagcttcgtccagaggacagacaaatgataaccaagtcttcagaagctcctagtattactgttgattctaataccagctggtggaccaattgggtgatcccagccatctcagcacttgtcatagccctaatgaacttccatattatggggaaaaatatgaacatatgcaaaatgaccccaagaaatcacagatcaatggtaaaatatttaaaacaaagtgtgaagaacctttgctttgcaggaatatttttttaaaaatacacaaaacatgcacataacataaactacctctgctaagacaaattaacaaagcaagataaaaaaatttaaatcttatagccaccaagatagtaaagaattatgaaataaagaagaaaaggaaaccagagaggtgaatcctaggattggttgatggttctggaagagatgattaaggtagaatttcggcagaggttttggcagacccatagggctagatggttaaagtaaataaaatatatatatatatataatttagcaaaaaatcaaagttctgttcaggagtgaactctggtaaatgttcacacatctagatgggatcccaaagggctccacctatgagtaaggagtaactggaaaaaacttggagctgcactaaagtttaaatcatcacagtccctataattaaaaaatattttttattgatttcagagaggaaatgagagagatagaaacattagtgacgagactcattgatcagctgcctcctgcaagacccctactgcaggtgaagcctgaaacccggtgtgtgccctaatggggaatttaactgtgacctcctggctcataggttgatgctcaaccactgagccatgccagctgggttcagtccctataattagatgaaggtgatctaagatagctggggccctaaccacctaccaaaggcaaatgtaagtcttccttggaggaagataattctaggcctcaaataattgtaacttttttttcacatacaatgtcctgtacctcaagaaaatgaaccagaaatggaaagggatatctcactacagagcctacagatattttttttattattataaaatcatttaatgatatttattattaacaactttatgttcctaaatttgaaaatttaggtaaattggacaagtttctaggaaaaaatacaacttactcaaactgacataaaaaggaatagaaaatctgaatcattcaataaacttaaaggaattaaaaactataagaaaggcacattgtgatttcccccagttactccattctcttttcctattaaaactttattttcctatttttgagaatgattcatttatttggctattggaatatttcagaaattaaaaaaatatgattacctacttcctctccatctttactcaatgttatcaaacttttctatgttttacttttgagaattacatatgttgtcttctcaattatatttcaaaccataggagggcagaccttgtggcatagaagaaaaaagagaggaagaagggaggtctgggaaaaagaaggaaaaagagaattgtgtttctggtttttacagttctacatccatatcctctgcagcctatttatagcctgctgatgtagaataatcataaaaggtccacgacagtatcctatataataaaaggctaatatgcaaatagaccaaatggtagaacagccgaacaaccgaacaaccagttgctatgacatgcactgaccaccagggggcgtgcgcaaaacatggcaggcatccgctgcgacgggatggtggagcaggtgagtgggggcaccagaccaaggcagggcgccggttgctgtcattgaggcaagcatctggtggttactgaaaattctttgctcttgtgtaccatggtcctgcctggtgcttgcacctgctgccagtgctggccccattcgcacccactgctggcacccggcgccagccccaatctctcggtgccatcagtgggtgcaagctgtggctgccaatcccaattgcccctgagggcttctccacctccacctgctcctgaggggcgaacagggcagcaactgctgcttgcacccgatgatggcgccagccctactcatacccattgctggtgccaggacccaattgctccgcaccttcagtgggtacgagcagggccagcactgtcagtgcgctggagtggtggtggcgggagcagggctgctggcagacaagggactgggggtgcagtgggagagtccaggcaagggcatggaggatgagccgagacccgcccctgtgcccactgcagcctcgcagcccacagttcctttcaaggtgcatgaattcgtgcactgggcccctagttaaacatactaagcaatatgacagagtggttaatatatatgagtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtcttggagttcttgagcaagatcattggataaattacttaacctctctaattttcaatttcttcagtgataagaaaagatcattgtggaacacctcacacatcagtgtaaatattaagtgataaaatacatggaacagtattattgcaaagcctaggaatagaagaattccataaatagtagctatcatttatgattatgatggagagaaaataaattaaaatatatataatttaattttagtttttaatatgtgttgacgaggaaaatcccaaatcagtcaaagtctctttatgcttacttcactcaaataaaaattttctatactaagtagcagaaataagtaaggcccttctggtaaactgttctaagttgggctacctggtggtctagagttgatcagaattttgaaacctatcctaagaaaagatggtgaaataataacttggcttttcttaaaaaatatagaccatctttgccagttgtccgttgtatggtttgttgagattgtctggaatttccaagccttaggactattaaaataattcaccagaactgcctccaaataattttatattgtgtaatttcttaacgctgcactttgggatggatgttctcggaatcacatgggtttagtgagagtcctgcataaagcatgttttgtcataaaaagcatccccttgatgcttagggaatgatgttgataggttttagacacagaacctacaatgactggaacatatttatcgaagcacagagggcagttgaaaatggctgttacaaactctgaacaaacaaggtcattgacatttcagtgtgaaccctcaagagcccttgaggggaaaaaaaattactttcacagggaactttcaattgccttctctctgattctaaaaagaatagaccagttcttttcttttcccccttgtccgttggctaagagaccacaaaaaaaaaataatcctttgcttaatttccagaaaaaaattatattgcttttagtaaaattcatttttcacctcaaggatagagctccaaattattatgtcaaactacattttctttaaacatctgaaatttttcctccgatttttattaaaagtacattctactttatgatatagaatgaaatattttggtattgatgcttttcttaataaattaagacatataggaacgctatttaaaatcatgctggggtggtaaccggtttggctcagtggatagagtgttggtctgtggactgaagggtcccaggttcaattccagtcaagggcatgtaccttagttgtgagcacatccccaatagggggtgtgcaggatgtttctaactctctattcctcttccttccactctgtaaaaaatcaataaaatatatttttttaaaaatcaaatcatgctggaagataaatgtcagttatttttaccaatgatttttcattttgggaaagcagcttttctatatccaatggcattactcaaaaagaagacttttctaaaaagtgttcctaatcattttgaatggagctaactgaaaatggcctcctggactctgctgcatttgggtttcccatccacttagattaattcattcaaaatggaaaatcatgtgactgaatttttactgttttttgttgttgttcttaatcctcacccaaggatatttttcccattgatttttagagatagtgggagggagggggtgagagagggacagagagaaatatggatgtaagagagacaatcgattggttccctccgtgtgcaccctaaccgaggccaaggatcaaatttgcaacccaagggaacttacgtgccctagattggaaatataacctgagactcttcaacatgcaggccgacgctccaaccattgagcaacatcagccagggctctatttttttttttttttaaattgagaggattgttgggagatgtgtaagtgatttattatctgttttgttttaatgaagacatttggaaataggaaatgatttcagtagacaacttgcatggaacaaatttgcctttttggtcacggcatttttcctttgtgacatgaaggcttttcagggggtacacaccatgtttccttttcatataaaacgagaggcccagtgcacaaaattcgtgcattcagggaggggttcccaaagcttggcctgcaccctcttgcagtctgggagccctcaggggatgtcagacatgccaatgagcccggcttctgtggctgagcggcgcttcccctgtgggagcacactgaccaccaggggggcagctcctgcattgagtgtctgccccctggtggccagtgcgcatcatagcaaccagtccttctaccattgggttgattgcatattagccttttattatataggactagaggcctggtgcacaaaatttgtgcactctgggggggtgggttccctcagcctagcctgtccctctccacagtccgggagccctcagtggatgtcctactgatggccacagtctggagcagaggctcggagcaggcgtcccgtgtgtgtgtgtgtgtgtgtgtgttgtgtgtgtgttgtgtgtctgctaggggcctgcccccatccgcgcactgccggggtccaaccccagcggggtcaggggtcccccaaaggtgtggacggagtcagcgaagaaggaatgacacgggagagggcattcagatgatcatcatagcaaggttctctagccacgttctggtcaggatctccagtgaggtcttggttaggatctccagccaggttctgtgtccatggttctcttgctaggttctccaggttctcagccagttctgtagccatgttccctcactaggttcttccagccaggttgtgtccaggttctccagccaggctcagtcaccaggttctagtcaggttctcttgccaatttctgtagtcaggttcagtccaggatctttgccatgttctctccagcaaagttcttctgtctctaggttctgtgtaggttctgtgttcttagttctgtcttcttagttctgtgtctaagttctgtctcctaagttctgtgttgttacatctgtatttataccagttgattccaatcctatcaatctctattccaaaggttagggcgtttctatctccattccagggagtaaagattatgtagcttagcatgttgttcattagtatagttgattaattacccgcctggcacttagttaagaggttttattccctccctaacttcagtgaaaatcccctacctggggaaacaacctttctcagagaccttggttaaaacacatagtccaagaaggtgacaaacatattaaggaacagtatgccatatattccaggtcccttgaaacagcaagcatggaccggctcccggcacgccacgtaggctcaaagcaatattgccgtgtgttgtgtgtgtgtgttgtgtgtgtgtgtgtgtgtgtcccccactgggggtctgtccccagccacattggaggctcagagcaggcactgtgtgtgtgtgtgtgtgtgtgtgtgttgtgtgtgtgttctgctgggggcctgcccccagcacaccatagaggcttggagcaggagcccctctgtgttgatctcttaggctccgggcccccaccttgcatgttgtacctcccttgagctatggccaggatgggggtgcttgcaagccagactttcctgctcctggatcgccatggcgtccggggagcaggcccagctgggagctgcccacaggctgggctttcctgctcccaggtctccgggcaaccagggagcagcccagcttggagctgcccgcaggccgggcttgttctcctgttcccggatccccatggtgactgggaagcaggcctatcgggggctgcccgccaggccaggctttcctgctcccgtttcaggcccagcttggggctgcccacaggccaggctgctttccctcccagatcaggtgcagctgggggctgcctacaggctgcacttttctacttacgatggccagattcaacacagtgacccagggcccagcggcactttcctgctctccaatagtcataggtcccgggcgtggggcggggcttagcagtgcaggcttaggcagcacgggatccccagctggggatcaggtggcacacagggatccggcgggggcaggacttatgcccactacccagggctgcacatgggcccggatggcagctcgagctgtcccgccctgccagtagtataggatagaggcctggtgcacgggtgggggctggctggtttgccctgaaagtgtcccagatcagggtggggggtcccgcttgggtggcctggccagctgcatgaggggatgatggctgtttgcatctggtcacacccccttcaggtgggggtccccactggggtgcctggccagtctgggtgagggcctgagagccattttcaggctggagggcaacttaagctcccacgctctccttttttttcttttttttttttattctgggattttatttaccttgtatagctgtcactggagctgagagcaggctccagctctgggggctgaaagcaggttctgggcttttttggcttctataattgaaactctgttgcccatcactgcagctctaagctctgagggcctaagctggctgaaagcaagtctcgtggaggcttgtttagcttctataattgcaacatagttgcttagagtgtagctcagaggctggtcatggcaggccgggaatgttggcttcctccatcactggagcaagcaagcctcctgttcgcttcagctgcgtggctgctggccgccatcttggttggcagttaatttgcatatcctgctgatatcctgcatatcctgggaagggtgttggggttatggtcaatttgcatgtttctcttttattagataggattatatcaccatgaaactttagacctgtcagtgacagccaaagtatatagggaacaggaagctctgactctgacttgatgagtgaaacaaacaggaaagctctggtctctttctttagatgtgagtatccaggtgtgcgctggatcatacaagcctgtccacactcttgcaactggagacagggagaggaaagctgacctgccagtgacatagacagcacatgcgtccctccaccctgccactctgctctgggcatcagatcagcatctagttgtgtccctacagtcctatagaggaactcaaatattggaagcaaagtaggtatagactctcactgagcacattcaaaaactcttatttttaattttcaattcagaaaaataaatacattttatgggcaattctttctacattactccttattgtttaattggataccagacacagtaataatactaagaaaggttgtccctaaagttgaagtaacaatgaaatgttgtaagtcatttcatggcgagggagaggagggaatgagttttgagccattttaaggtgactcattgtgtcctctagaggaaatgacttcagaaaatatctgttagaaaacactactcttggagagtctcataatgtggagagctgagtgtttgtagcttcataaaaagtgattgttaactgttatcagaaagcatcttgatgttatttttaaaattatttgcagcagagacaatatgtattagcttgtgcctccttccttccccgcaagaaagactctttgttagccattgtcttgtatgtttcatggtacaacatcagggaaatgggtagataagataacatctgaaaaggaggcagaaataaccagtaagcctagatcactttataataaagaaagaatgaatttgtgtgtgtgtgtgtgtgtgtgtgtgtgtgtacatatatatattgagagaaagagagagagagagagagagagagagagagagagagagagagagagagagattagttgtttggttttattataaggaactagctcacacaataatggaggctgagaagtttaagatctacagtcaaaaggtagggacacaaggagagctgagggtatagttccagcctgagcctaaaggcctgagaacgaagagagccactagtgtaaattctagttcaagtctaagtccaaaggcgggaaaagatggatgtcccagcttaaagataggcagtcagagagaaagaatgctttcttggcctgccttttattctatccagtcctccagtggattcgatggggcttgcctacattaaagaaggttatttgctttactcagtctatcgattcaaatgctgaactcatgcagagaataaacaccattacagacacatccagaattatgcttaaccaaatttctgagcactcatggcccagtcaagttgacaaatgaaattaaccttcatacttttgtatacaaatatacttttgcaccggaacagaacagagaacccagaaattgacccaagccattatgctcaattaatatttgacaaaaggaagcaagagcatacaatggagtaagacagtctcttcaataatggtactgggaaatttggtcaaatccataccaaaaaaaaaaatgaaactagatcaccaacctataccatacacaaaaacaaactcaaaatggttaaaggacttaaatttaagatgggaaaccaaaaaatcttaaaagactacataggcagcaaaaaatatcaggcatatgtcacagcaatatctttactgatacagcttctagggacgggtgggcaaacgttttgaatcgagggccacaatgggttcttaaactggacgtgaaggccggaacaaaagcatggatggagtgtttgtgtgaactaatataaattcaaagtaaacatcatacataaaagggtatggtctgtttttgggttttttttagttttatcatttcaaatgggccggatcgcccggcgggccgtagtttgcccaggctgttctaagggcaatgggaaaataagggagaacatagaccaaataggactacatcaaaataaaaagctttctgcacagcaaaagaaatcatcaacaaaacaacaagaagcccactggcatgggagaacatttgccaatgctattcagataagggtttatcctccaaaattttacagagaacttcatacaactacaaaaggaagataaacactcaatcaaaaaatgggcaatggacctaaattagacacttttaaaagaggacatagaaaggccaagagacaatgaaacatgctcaaagtcactaatcctctgaggagatgcaaatcaaaaacaacaatgagataccactcacactgtaagaatggctatcatcaacaaataacaaaggacaagtactggcaaggatggtggagaaaaaggaaccttcatgcactgctggtgggaatgcagactgggtgcagccaactgttggaagacgtatggaatttcctccaaaaattaaaaatggaattcccatttgacccagttaattcccacttctagagactaccatcccaagaactagaacaccaatcagaaaggatatatgccaccctatgttcatagcagcacaatttacaatagctagatttgaaaacagcctaagtgccccatcagcagatgagtggattaaaaaactgtggtacacctaacacaatggaatactattcctaatatataaaaagccaggggctgtaaaccaaaacaaacgggctggatgaccgaacagcaggctgcatggggcgacaaggccggcagggggattagtgagggacaatcaacgactgaacagcagctgcataaggcgaccaggcaggcagggattttagtgaaggatgaccaaatgactgaacagcagctgcgtggggtgacccgcctgcaggggggttagagagggacgaccaaaccactgaacagcaggctgcatggggcaaccaggccagcggggggcagttgggggcaacacggccagcaggagggcagttgggggtgattcaggcagcaggcaggtgagcagttagaaggccagcagtctcagattgtgagaggggatgtccaactgccggtttaggccttattctggggatcgggcctaaaccggcagtcgacacccccaaggggccctgattggagagggtgcaagctggctgggggatggatgaaacagcatggtggaactggagagcattatgctaagggaaataagccagttggagaaagataatatatcacatgatactcactagatgtgggatataatgatcaacaaaactgtgaacaaaatagatccagggacaagtggcatgggcggtgggttagaagatcaacctaaagacttgtatgcatatatatgcatattagcattagccaatggacacagacactggggggcggtggggacttttcctcaggggttgggagtggttgggggcgggtcaatcagggaaaagggagacatatgtaatactttaaaccataaaaaataccaattaagaatgtttacatatatatacattatctatagtgctaaaatgtaaaagattgatataccaggtgtacctacttgatacatttctccaacatggatggagtctcaataacattatgtttgaacaaatgaagccagacagaaaagggtagctaatgtataatcacatttacgtgaggttaataaacaagcaagttggtgatgatagaagtcagaatagtggtgttatccttctgagaaaggtgggcttattggattgaaaggggcacaggggaactttctccagctgacggtgacaattttacatttttccaggtcttagtcacatggatatttgagtaacatataacatatggtgtaagtatcctaatattataaaaacccagggatccataacgacctaccgaaggctcaactgaacaccagcagtcctgcagtcagtgctggggctgccgtggcgaccagcactgactaccgccggtgcgggcacagccaacccagcactgactgccgagggagctgcggatcaggcccagagagagaggcacgtctgatctgcaggcctccatggcagctgttgatcaactgttgcctcttctctctctctccccgggcttcaccagcagcccacgctctttctctcaaggcctccgtgagggggctgcttatcagtcccaccttctctgatcaggcctgggagataggcccagagatgttgactggcatagaaactaaccaatcagaccaaatctgggtgacagtaggagccaatggctgcctaggaggcagagcttttgatgctgactggcatagaaacttgaccaatcagaaccaaattggccagcaggggagggcagttgggggcaaagatcaggcctgcaggggagagcagttgggggcagatcaggacagcagaggagggcagttagggattatcaggcaggtagaggcagtagggcaattcagctggcaaggagagggcagtttgggggcgagatcaggctgcaggggatggctggtagggggcgagatcaggccggcaggggagggcaggttaggggtgatcaggcaggcagtcagaggggttaggggcaatcaggcaggcagaggagttagggaccgatatgcaggaaggcaggtgagtggttaggagccagcagtctccgattgcaaggagggatgtctgactgccggtttagtccaaatccctgtgggacatccccaagggtccccaaatggagagggtgcagactgggctaagggaacccccgctccatgcggattttgtgcaccgggccactagtacttatatattgtatacatttaggatgtgtgcatttcactggatgtaaattatactttaaacttgtgaggaggggaggttcaatacaaaaattattcccaacgaatgaaataaatgaaatttatttttttttaatcaagagacctaaaccaagcattacaaataaaaatttctctcagatgggcccaagccagacagtttccataagcaaacccatgaaagccattgcctgggttcctaagactcagatgacaaaatttaaaatcctatggaattgtccccaaaaagatgactcacaggtgaccttgaaatctgagtggtgtgtatactatttcagctacagtgtcacacattccattgtagttatcacctggtatttcataattatttgtaaataattcagtagcaacattgactaaactccactctaccaccaaaattgaaaacataacattgatagattttaaaattatttcatacttacaagtattagtttatttgttaacaggtccaaacattttgactgcatggatccttctgggtctacgtcctgattttaattcctgttctgtctgctctctcacaactcaccacaaagtattttgaaaattgcataagatgctctggccagtgtgtctcagcgggttagagatcagcctgtgcactggaagggtctcaggtttgattcccagtcaagggaacatacctgggcaggcaaccaatcgatgtgtcttctctcacatcgatattttttctctccccctccaccctcccttccactctttctaaaaaatatcaatgaaaaaaatccagtgaggataaacaacaacaaattgcaccaataataaaaccaaattatcagtaataatttattcctaataaagataaaaccatggggccaaatggtacatgtcacatttaatttatcaatattgtcatttattataatacacaactttatgatgtcaaagtatctggatttctatggtcacattgggtagttcagtgttatacacctttgggcacatctcaaaagaatccagttgattcgcttgacacaggctatttaatgcaaaaggaggcccttaaaatgcaggtgacaatattaaacagataaacttgatgcagaaatcacaaatacctctcagcctgattgcccaaagaccaaaactgtgacacacaaatgggaccacacacgcttagctcagccatgccaggggagggctgaccgggcttctcaactcagaaaaattcagtcatgctccaccctcatacttcacatttctttatcaagcaggggctgcagtgggaagcaggcacattaatgacaacccacattccacatgtgcgcacacacaccacaatggagctatgatttcagaaaccttctaataaacaggatgttggagtgctgattgatttacacgttgataaaagttccgttgaaatgagcctactatctatcatgcaagtggctgtgaaaaacttaattgcacaaactgttcttgttggaacagggagctatctgctccttttccttgcatggaccagagttcctccctcacagctactgcaggacacaaaaagacagcacttaaaactggatcctgttctgcaaaccgtgcaagtcaatccggcgccgttcacagacatttttatttctgatcatgtctccaagtgtagacatctgagaaatataatcttgcctcagcaaggctcaaaggagacatttgatctctaagtcatagtctcactgatcatgcttagttagtaagtcacagaaaaaataaattaaattgcatcatgggcttaaaatattttattctcccttataatttccatatcccaaagccaacttgtttaactattaaatgtacgtactccacttaggaatgaaaactgtaaactgttttttcctgatgagcacatgtgatgtctttatcattaatagtgaataggtggtaatattccttcttggttttgtttttgggttttttatgttaacactattacacatgtcccccattccttccccccttttgcccacttccacccagtccctacccacccctctctggccttcaccaccactgttttctgggttcgtgggcttgcatatattgttctttggctaatcctctacacttcctt");
1809     auto exactAlignment = getPaddedAlignment(ac, begin, end, aSequence, bSequence);
1810 
1811     cast(void) exactAlignment[47139 .. 73889];
1812 }
1813 
1814 /**
1815     Get the FASTA sequences of the designated records.
1816 
1817     Throws: DazzlerCommandException if recordNumber is not in dbFile
1818 */
1819 auto getFastaSequences(Range)(in string dbFile, Range recordNumbers, in string workdir)
1820         if (isForwardRange!Range && is(ElementType!Range : size_t))
1821 {
1822     string[] dbdumpOptions = [DBdumpOptions.sequenceString];
1823     auto numRecords = recordNumbers.save.walkLength;
1824     auto sequences = readSequences(dbdump(dbFile, recordNumbers, dbdumpOptions, workdir));
1825     size_t numFoundSequences;
1826 
1827     string countedSequences()
1828     {
1829         enforce!DazzlerCommandException(
1830             !sequences.empty || numFoundSequences >= numRecords,
1831             "cannot read sequence: dump too short"
1832         );
1833 
1834         if (sequences.empty)
1835         {
1836             assert(numFoundSequences == numRecords, "unexpected excessive sequence in dump");
1837             return null;
1838         }
1839 
1840         ++numFoundSequences;
1841         auto currentSequence = sequences.front;
1842         sequences.popFront();
1843 
1844         return currentSequence;
1845     }
1846 
1847     return generate!countedSequences.takeExactly(numRecords);
1848 }
1849 
1850 /**
1851     Get the FASTA sequence of the designated record with prefetching to reduce `fork`s.
1852 
1853     Throws: DazzlerCommandException if recordNumber is not in dbFile
1854 */
1855 auto getFastaSequence(in string dbFile, id_t recordNumber, in string workdir, in id_t cacheSize = 1024)
1856 {
1857     // FIXME the cache size should limit the number of `char`s retrieved, ie. control the memory
1858     // requirements of this function
1859     static uint _dbIdx;
1860     static id_t[2] _firstRecord;
1861     static string[2] _dbFile;
1862     static id_t[2] _numRecords;
1863     static string[][2] _cache;
1864 
1865     if (!dbFile.among(_dbFile[0], _dbFile[1]))
1866     {
1867         // Select least recently used DB cache
1868         _dbIdx = 1 - _dbIdx;
1869         _firstRecord[_dbIdx] = 0;
1870         _dbFile[_dbIdx] = dbFile;
1871         _numRecords[_dbIdx] = cast(id_t) getNumContigs(dbFile, workdir);
1872         _cache[_dbIdx].length = 0;
1873     }
1874 
1875     _dbIdx = _dbFile[0] == dbFile ? 0 : 1;
1876     assert(_dbFile[_dbIdx] == dbFile);
1877 
1878     if (
1879         recordNumber >= _firstRecord[_dbIdx] + _cache[_dbIdx].length ||
1880         recordNumber < _firstRecord[_dbIdx]
1881     )
1882     {
1883         enum string[] dbdumpOptions = [DBdumpOptions.sequenceString];
1884         _cache[_dbIdx].length = cacheSize;
1885         auto bufferRest = readSequences(dbdump(
1886             dbFile,
1887             recordNumber,
1888             min(recordNumber + cacheSize - 1, _numRecords[_dbIdx]),
1889             dbdumpOptions,
1890             workdir,
1891         )).copy(_cache[_dbIdx]);
1892         _cache[_dbIdx] = _cache[_dbIdx][0 .. $ - bufferRest.length];
1893         _firstRecord[_dbIdx] = recordNumber;
1894         enforce!DazzlerCommandException(_cache[_dbIdx].length > 0, "cannot read sequence: empty dump");
1895     }
1896 
1897 
1898     return _cache[_dbIdx][recordNumber - _firstRecord[_dbIdx]];
1899 }
1900 
1901 private auto readSequences(R)(R dbdump)
1902 {
1903     enum baseLetters = AliasSeq!('A', 'C', 'G', 'N', 'T', 'a', 'c', 'g', 'n', 't');
1904 
1905     return dbdump
1906         .filter!(dumpLine => dumpLine[0] == 'S')
1907         .map!(dumpLine => dumpLine.find!(among!baseLetters));
1908 }
1909 
1910 unittest
1911 {
1912     import std.algorithm : equal;
1913 
1914     enum testDbDump = q"EOF
1915         + R 2
1916         + M 0
1917         + S 100
1918         @ S 100
1919         S 58 tgtgatatcggtacagtaaaccacagttgggtttaaggagggacgatcaacgaacacc
1920         S 42 atgccaactactttgaacgcgccgcaaggcacaggtgcgcct
1921 EOF".outdent;
1922 
1923     size_t[] recordIds = [];
1924     auto fastaSequences = readSequences(testDbDump.lineSplitter);
1925     assert(fastaSequences.equal([
1926         "tgtgatatcggtacagtaaaccacagttgggtttaaggagggacgatcaacgaacacc",
1927         "atgccaactactttgaacgcgccgcaaggcacaggtgcgcct",
1928     ]));
1929 }
1930 
1931 /**
1932     Get the designated set of records in FASTA format. If recordNumbers is
1933     empty the whole DB will be converted.
1934 */
1935 auto getFastaEntries(Options, Range)(in string dbFile, Range recordNumbers, in Options options)
1936         if (isIntegral!(typeof(options.fastaLineWidth)) &&
1937             isSomeString!(typeof(options.workdir)) &&
1938             isInputRange!Range && is(ElementType!Range : size_t))
1939 {
1940     string[] dbdumpOptions = [
1941         DBdumpOptions.readNumber,
1942         DBdumpOptions.originalHeader,
1943         DBdumpOptions.sequenceString,
1944     ];
1945 
1946     return readDbDump(dbdump(dbFile, recordNumbers, dbdumpOptions,
1947             options.workdir), recordNumbers, options.fastaLineWidth);
1948 }
1949 
1950 private auto readDbDump(S, Range)(S dbDump, Range recordNumbers, in size_t lineLength)
1951         if (isInputRange!S && isSomeString!(ElementType!S)
1952             && isInputRange!Range && is(ElementType!Range : size_t))
1953 {
1954     import std.algorithm : count, filter, sort;
1955     import std.array : appender;
1956     import std.range : chunks, drop;
1957 
1958     enum lineSeparator = '\n';
1959     enum subrecordSeparator = ';';
1960     enum recordFormat = "R %d;H %d %s;L %d %d %d;S %d %s";
1961     enum numRecordLines = recordFormat.count(subrecordSeparator) + 1;
1962 
1963     /// Build chunks of numRecordLines lines.
1964     alias byRecordSplitter = dbDump => dbDump.drop(6).arrayChunks(numRecordLines);
1965     /// Parse chunks of numRecordLines lines into FASTA format.
1966     alias parseRecord = recordLines => {
1967         size_t recordNumber;
1968         size_t headerLineLength;
1969         string headerLine;
1970         size_t locationWell;
1971         size_t locationPulseStart;
1972         size_t locationPulseEnd;
1973         size_t sequenceLength;
1974         string sequence;
1975 
1976         auto joinedLines = recordLines.joiner(only(subrecordSeparator)).array;
1977 
1978         int numMatches = joinedLines
1979             .formattedRead!recordFormat(
1980                 recordNumber,
1981                 headerLineLength,
1982                 headerLine,
1983                 locationWell,
1984                 locationPulseStart,
1985                 locationPulseEnd,
1986                 sequenceLength,
1987                 sequence,
1988             );
1989         assert(numMatches == 8, format!"%d matches in chunk: `%s`"(numMatches, joinedLines.array));
1990 
1991         bool isSkipping = recordNumbers.length > 0 && !recordNumbers.canFind(recordNumber);
1992 
1993         debug logJsonDebug(
1994             "isSkipping", isSkipping,
1995             "wantedRecordNumbers", recordNumbers.toJson,
1996             "recordNumber", recordNumber,
1997             "headerLine", headerLine,
1998         );
1999 
2000         // skip unwanted records
2001         if (isSkipping)
2002             return null;
2003 
2004         auto fastaData = appender!string;
2005         fastaData.reserve(headerLine.length + sequence.length + sequence.length / lineLength + 1);
2006 
2007         fastaData ~= headerLine ~ lineSeparator;
2008         fastaData ~= sequence.chunks(lineLength).joiner(only(lineSeparator));
2009 
2010         return fastaData.data;
2011     };
2012 
2013     return byRecordSplitter(dbDump).map!parseRecord
2014         .map!"a()"
2015         .cache
2016         .filter!"a !is null";
2017 }
2018 
2019 unittest
2020 {
2021     enum testDbDump = q"EOF
2022         + R 4
2023         + M 0
2024         + H 104539
2025         @ H 26
2026         + S 38
2027         @ S 19574
2028         R 1
2029         H 22 >Sim/1/0_14 RQ=0.975
2030         L 0 0 14
2031         S 14 ggcccaggcagccc
2032         R 2
2033         H 22 >Sim/2/0_9 RQ=0.975
2034         L 0 0 9
2035         S 9 cacattgtg
2036         R 3
2037         H 23 >Sim/3/0_11 RQ=0.975
2038         L 0 0 11
2039         S 11 gagtgcagtgg
2040         R 4
2041         H 23 >Sim/4/0_4 RQ=0.975
2042         L 0 0 4
2043         S 4 gagc
2044         R 5
2045         H 24 >Sim/5/0_60 RQ=0.975
2046         L 0 0 60
2047         S 60 gagcgagcgagcgagcgagcgagcgagcgagcgagcgagcgagcgagcgagcgagcgagc
2048 EOF".outdent;
2049 
2050     {
2051         size_t[] recordIds = [];
2052         auto fastaEntries = readDbDump(testDbDump.lineSplitter, recordIds, 50).array;
2053         assert(fastaEntries == [
2054             ">Sim/1/0_14 RQ=0.975\nggcccaggcagccc",
2055             ">Sim/2/0_9 RQ=0.975\ncacattgtg",
2056             ">Sim/3/0_11 RQ=0.975\ngagtgcagtgg",
2057             ">Sim/4/0_4 RQ=0.975\ngagc",
2058             ">Sim/5/0_60 RQ=0.975\ngagcgagcgagcgagcgagcgagcgagcgagcgagcgagcgagcgagcga\ngcgagcgagc",
2059         ], fastaEntries.to!string);
2060     }
2061     {
2062         size_t[] recordIds = [1, 3];
2063         auto fastaEntries = readDbDump(testDbDump.lineSplitter, recordIds, 50).array;
2064         assert(fastaEntries == [
2065             ">Sim/1/0_14 RQ=0.975\nggcccaggcagccc",
2066             ">Sim/3/0_11 RQ=0.975\ngagtgcagtgg",
2067         ], fastaEntries.to!string);
2068     }
2069 }
2070 
2071 /// Build a .dam file with the given set of FASTA records.
2072 string buildDamFile(Range)(Range fastaRecords, in string workdir, in string[] dbsplitOptions = [])
2073         if (isInputRange!Range && isSomeString!(ElementType!Range))
2074 {
2075     enum tempDbNameTemplate = "auxiliary-XXXXXX";
2076 
2077     auto tempDbTemplate = buildPath(workdir, tempDbNameTemplate);
2078     auto tempDb = mkstemp(tempDbTemplate, damFileExtension);
2079 
2080     tempDb.file.close();
2081     remove(tempDb.name);
2082     fasta2dam(tempDb.name, fastaRecords, workdir);
2083     dbsplit(tempDb.name, dbsplitOptions, workdir);
2084 
2085     return tempDb.name;
2086 }
2087 
2088 unittest
2089 {
2090     import dentist.util.tempfile : mkdtemp;
2091     import std.file : rmdirRecurse, isFile;
2092 
2093     auto fastaRecords = [
2094         ">Sim/1/0_14 RQ=0.975\nggcccacccaggcagccc",
2095         ">Sim/3/0_11 RQ=0.975\ngagtgcgtgcagtgg",
2096     ];
2097 
2098     auto tmpDir = mkdtemp("./.unittest-XXXXXX");
2099     scope (exit)
2100         rmdirRecurse(tmpDir);
2101 
2102     string dbName = buildDamFile(fastaRecords[], tmpDir);
2103 
2104     assert(dbName.isFile);
2105     foreach (hiddenDbFile; getHiddenDbFiles(dbName))
2106     {
2107         assert(hiddenDbFile.isFile);
2108     }
2109 }
2110 
2111 /**
2112     Self-dalign dbFile and build consensus using daccord.
2113 
2114     Returns: list of consensus DBs.
2115 */
2116 string getConsensus(Options)(in string dbFile, in size_t readId, in Options options)
2117         if (isOptionsList!(typeof(options.daccordOptions)) &&
2118             isOptionsList!(typeof(options.lasFilterAlignmentsOptions)) &&
2119             isOptionsList!(typeof(options.dalignerOptions)) &&
2120             isOptionsList!(typeof(options.dbsplitOptions)) &&
2121             isSomeString!(typeof(options.workdir)))
2122 {
2123     static struct ModifiedOptions
2124     {
2125         string[] daccordOptions;
2126         string[] dalignerOptions;
2127         string[] dbsplitOptions;
2128         string[] lasFilterAlignmentsOptions;
2129         string workdir;
2130     }
2131 
2132     auto readIdx = readId - 1;
2133     auto consensusDb = getConsensus(dbFile, const(ModifiedOptions)(
2134         options.daccordOptions ~ format!"%s%d,%d"(cast(string) DaccordOptions.readInterval, readIdx, readIdx),
2135         options.dalignerOptions,
2136         options.dbsplitOptions,
2137         options.lasFilterAlignmentsOptions,
2138         options.workdir,
2139     ));
2140 
2141     if (consensusDb is null)
2142     {
2143         throw new Exception("empty consensus");
2144     }
2145 
2146     return consensusDb;
2147 }
2148 
2149 /// ditto
2150 string getConsensus(Options)(in string dbFile, in Options options)
2151         if (isOptionsList!(typeof(options.daccordOptions)) &&
2152             isOptionsList!(typeof(options.lasFilterAlignmentsOptions)) &&
2153             isOptionsList!(typeof(options.dalignerOptions)) &&
2154             isOptionsList!(typeof(options.dbsplitOptions)) &&
2155             isSomeString!(typeof(options.workdir)))
2156 {
2157     dalign(dbFile, options.dalignerOptions, options.workdir);
2158     auto lasFile = getLasFile(dbFile, options.workdir);
2159     enforce!DazzlerCommandException(
2160         !lasEmpty(
2161             lasFile,
2162             dbFile,
2163             null,
2164             options.workdir,
2165         ),
2166         "empty pre-consensus alignment",
2167     );
2168 
2169     computeIntrinsticQualityValuesForConsensus(dbFile, options);
2170     // FIXME remove if bug in lasfilteralignments is fixed
2171     version(unittest)
2172         auto filteredLasFile = lasFile;
2173     else
2174         auto filteredLasFile = filterAlignmentsForConsensus(dbFile, options);
2175     enforce!DazzlerCommandException(
2176         !lasEmpty(
2177             filteredLasFile,
2178             dbFile,
2179             null,
2180             options.workdir,
2181         ),
2182         "empty pre-consensus alignment",
2183     );
2184 
2185     computeErrorProfile(dbFile, filteredLasFile, options);
2186 
2187     auto consensusDb = daccord(dbFile, filteredLasFile, options.daccordOptions, options.workdir);
2188     dbsplit(consensusDb, options.dbsplitOptions, options.workdir);
2189 
2190     return consensusDb;
2191 }
2192 
2193 private void computeIntrinsticQualityValuesForConsensus(Options)(in string dbFile, in Options options)
2194         if (isSomeString!(typeof(options.workdir)))
2195 {
2196     auto readDepth = getNumContigs(dbFile, options.workdir);
2197     auto lasFile = getLasFile(dbFile, options.workdir);
2198 
2199     computeIntrinsicQV(dbFile, lasFile, readDepth, options.workdir);
2200 }
2201 
2202 private string filterAlignmentsForConsensus(Options)(in string dbFile, in Options options)
2203         if (isOptionsList!(typeof(options.lasFilterAlignmentsOptions)) &&
2204             isSomeString!(typeof(options.workdir)))
2205 {
2206     auto lasFile = getLasFile(dbFile, options.workdir);
2207     auto filteredLasFile = lasFilterAlignments(dbFile, lasFile, options.lasFilterAlignmentsOptions, options.workdir);
2208 
2209     return filteredLasFile;
2210 }
2211 
2212 private void computeErrorProfile(Options)(in string dbFile, in string lasFile, in Options options)
2213         if (isOptionsList!(typeof(options.daccordOptions)) &&
2214             isSomeString!(typeof(options.workdir)))
2215 {
2216     auto eProfOptions = options
2217         .daccordOptions
2218         .filter!(option => !option.startsWith(
2219             cast(string) DaccordOptions.produceFullSequences,
2220             cast(string) DaccordOptions.readsPart,
2221             cast(string) DaccordOptions.errorProfileFileName,
2222         ))
2223         .chain(only(DaccordOptions.computeErrorProfileOnly))
2224         .array;
2225 
2226     // Produce error profile
2227     silentDaccord(dbFile, lasFile, eProfOptions, options.workdir);
2228 }
2229 
2230 
2231 unittest
2232 {
2233     import dentist.util.tempfile : mkdtemp;
2234     import std.file : rmdirRecurse, isFile;
2235 
2236     auto fastaRecords = [
2237         ">Sim/1/0_1050 RQ=0.975\nattTgaggcatcagccactgcacccagccttgtgccctttctgagagccgggaagatgctcccaggagccctcg\nggaggcttccctccggtcgtcgtggccagaattgtctcctgctcgtgtggagtcggtggcgggccaggcgaatg\nggagctaccggggctgccgctttggactgctcggcatttgccccatggggctgcacaggggcccaggctggctg\nagaatgtccctgggtccaggaggcagacggaggtacagcccagcagccaggaggtgttcaggatgttccccagt\ncagcacccgtggaggggagggaggaggcagggtgggcgaggaaggtccaacagtggacggcctgcccacaagag\nagctctgagctgggagctggcagagttgctgcaagtgggtgtgggccaggactgactgggcctgtgcacctgcc\ntggatgcatcagtggtcgtggtgctgcccgggaagggcgtgaagctccctgcagccaaggatcctggaggtgca\ngacatcacccagcccaccggacaacagcctgccctacttcgaggagctctgggcagcccagccccatgtccccc\ntcacgccccaccccacactgacaaaaagaccacaggattccaacagtccaaccagggggaggccgttgaattcg\nggggacaaccagaaacgcctgaaacagagataaagagactgatatggaaaagactgggctggcatggtggctcc\ncaactgggatcccagtgcttgtgagaggccgaggcgggaggatcacttgagcccagaagttcaagaccagcgtg\nggcaacatagtgagaccccgtctcttttaaaaatccttttttaattaggcaggcataggtagttgcgtgcctgc\nttttcccagctgctagggaggtagaggcaggagaatcacgggagtttcgaagtccaaggtcacagtgagctgtg\nattgcaccactgcactccagcctgggcaacatggcaagaccccatctctaaaagaaagaaacaagaagacatgg\nagagaaatatccaa",
2238         ">Sim/2/0_1050 RQ=0.975\nattagagCcatcagccactgcacccagccttgtgccctttctgagagccgggaagatgctcccaggagccctcg\nggaggcttccctccggtcgtcgtggccagaattgtctcctgctcgtgtggagtcggtggcgggccaggcgaatg\nggagctaccggggctgccgctttggactgctcggcatttgccccatggggctgcacaggggcccaggctggctg\nagaatgtccctgggtccaggaggcagacggaggtacagcccagcagccaggaggtgttcaggatgttccccagt\ncagcacccgtggaggggagggaggaggcagggtgggcgaggaaggtccaacagtggacggcctgcccacaagag\nagctctgagctgggagctggcagagttgctgcaagtgggtgtgggccaggactgactgggcctgtgcacctgcc\ntggatgcatcagtggtcgtggtgctgcccgggaagggcgtgaagctccctgcagccaaggatcctggaggtgca\ngacatcacccagcccaccggacaacagcctgccctacttcgaggagctctgggcagcccagccccatgtccccc\ntcacgccccaccccacactgacaaaaagaccacaggattccaacagtccaaccagggggaggccgttgaattcg\nggggacaaccagaaacgcctgaaacagagataaagagactgatatggaaaagactgggctggcatggtggctcc\ncaactgggatcccagtgcttgtgagaggccgaggcgggaggatcacttgagcccagaagttcaagaccagcgtg\nggcaacatagtgagaccccgtctcttttaaaaatccttttttaattaggcaggcataggtagttgcgtgcctgc\nttttcccagctgctagggaggtagaggcaggagaatcacgggagtttcgaagtccaaggtcacagtgagctgtg\nattgcaccactgcactccagcctgggcaacatggcaagaccccatctctaaaagaaagaaacaagaagacatgg\nagagaaatatccaa",
2239         ">Sim/3/0_1050 RQ=0.975\nattagaggcatcagccactgcacccagccttgtgccctttctgagagccgggaagatgctcccaggagccctcg\nggaggcttccctccggtcgtcgtggccagaattgtctcctgctcgtgtggagtcggtggcgggccaggcgaatg\nggagctaccggggctgccgctttggactgctcggcatttgccccatggggctgcacaggggcccaggctggctg\nagaatgtccctgggtccaggaggcagacggaggtacagcccagcagccaggaggtgttcaggatgttccccagt\ncagcacccgtggaggggagggaggaggcagggtgggcgaggaaggtccaacagtggacggcctgcccacaagag\nagctctgagctgggagctggcagagttgctgcaagtgggtgtgggccaggactgactgggcctgtgcacctgcc\ntggatgcatcagtggtcgtggtgctgcccgggaagggcgtgaagctccctgcagccaaggatcctggaggtgca\ngacatcacccagcccaccggacaacagcctgccctacttcgaggagctctgggcagcccagccccatgtccccc\ntcacgccccaccccacactgacaaaaagaccacaggattccaacagtccaaccagggggaggccgttgaattcg\nggggacaaccagaaacgcctgaaacagagataaagagactgatatggaaaagactgggctggcatggtggctcc\ncaactgggatcccagtgcttgtgagaggccgaggcgggaggatcacttgagcccagaagttcaagaccagcgtg\nggcaacatagtgagaccccgtctcttttaaaaatccttttttaattaggcaggcataggtagttgcgtgcctgc\nttttcccagctgctagggaggtagaggcaggagaatcacgggagtttcgaagtccaaggtcacagtgagctgtg\nattgcaccactgcactccagcctgggcaacatggcaagaccccatctctaaaagaaagaaacaagaagacatgg\nagagaaatatccaa",
2240     ];
2241 
2242     struct Options
2243     {
2244         string[] dbsplitOptions;
2245         string[] dalignerOptions;
2246         string[] daccordOptions;
2247         string[] lasFilterAlignmentsOptions;
2248         size_t fastaLineWidth;
2249         string workdir;
2250     }
2251 
2252     auto tmpDir = mkdtemp("./.unittest-XXXXXX");
2253     auto options = Options(
2254         [],
2255         [DalignerOptions.minAlignmentLength ~ "15"],
2256         [],
2257         [
2258             LasFilterAlignmentsOptions.errorThresold ~ (0.05).to!string,
2259         ],
2260         74,
2261         tmpDir,
2262     );
2263     scope (exit)
2264         rmdirRecurse(tmpDir);
2265 
2266     string dbName = buildDamFile(fastaRecords[], tmpDir);
2267     string consensusDb = getConsensus(dbName, options);
2268     assert(consensusDb !is null);
2269     auto consensusFasta = getFastaEntries(consensusDb, cast(size_t[])[], options);
2270     auto expectedSequence = fastaRecords[$ - 1].lineSplitter.drop(1).joiner.array;
2271     auto consensusSequence = consensusFasta.front.lineSplitter.drop(1).joiner.array;
2272 
2273     assert(expectedSequence == consensusSequence,
2274             format!"expected %s but got %s"(expectedSequence, consensusSequence));
2275 }
2276 
2277 string getLasFile(in string dbA, in string baseDirectory)
2278 {
2279     return getLasFile(dbA, null, baseDirectory);
2280 }
2281 
2282 string getLasFile(in string dbA, in string dbB, in string baseDirectory)
2283 {
2284     alias dbName = dbFile => dbFile.baseName.stripExtension;
2285 
2286     enum fileTemplate = "%s/%s.%s.las";
2287     auto dbAName = dbName(dbA);
2288     auto dbBName = dbB is null ? dbAName : dbName(dbB);
2289 
2290     return format!fileTemplate(baseDirectory, dbAName, dbBName);
2291 }
2292 
2293 bool lasFileGenerated(in string dbA, in string baseDirectory)
2294 {
2295     return lasFileGenerated(dbA, null, baseDirectory);
2296 }
2297 
2298 bool lasFileGenerated(in string dbA, in string dbB, in string baseDirectory)
2299 {
2300     return getLasFile(dbA, dbB, baseDirectory).exists;
2301 }
2302 
2303 id_t getNumBlocks(in string damFile)
2304 {
2305     // see also in dazzler's DB.h:394
2306     //     #define DB_NBLOCK "blocks = %9d\n"  //  number of blocks
2307     enum blockNumFormat = "blocks = %d";
2308     enum blockNumFormatStart = blockNumFormat[0 .. 6];
2309     id_t numBlocks;
2310     auto matchingLine = File(damFile.stripBlock).byLine.filter!(
2311             line => line.startsWith(blockNumFormatStart)).front;
2312 
2313     if (!matchingLine)
2314     {
2315         auto errorMessage = format!"could not read the block count in `%s`"(damFile.stripBlock);
2316         throw new DazzlerCommandException(errorMessage);
2317     }
2318 
2319     if (formattedRead!blockNumFormat(matchingLine, numBlocks) != 1)
2320     {
2321         auto errorMessage = format!"could not read the block count in `%s`"(damFile.stripBlock);
2322         throw new DazzlerCommandException(errorMessage);
2323     }
2324 
2325     return numBlocks;
2326 }
2327 
2328 id_t getNumContigs(in string damFile, in string workdir)
2329 {
2330     enum contigNumFormat = "+ R %d";
2331     enum contigNumFormatStart = contigNumFormat[0 .. 4];
2332     id_t numContigs;
2333     id_t[] empty;
2334     auto matchingLine = dbdump(damFile, empty, [], workdir)
2335         .filter!(line => line.startsWith(contigNumFormatStart))
2336         .front;
2337 
2338     if (!matchingLine)
2339     {
2340         auto errorMessage = format!"could not read the contig count in `%s`"(damFile);
2341         throw new DazzlerCommandException(errorMessage);
2342     }
2343 
2344     if (formattedRead!contigNumFormat(matchingLine, numContigs) != 1)
2345     {
2346         auto errorMessage = format!"could not read the contig count in `%s`"(damFile);
2347         throw new DazzlerCommandException(errorMessage);
2348     }
2349 
2350     return numContigs;
2351 }
2352 
2353 auto getScaffoldStructure(in string damFile)
2354 {
2355     enum string[] dbshowOptions = [DBshowOptions.noSequence];
2356 
2357     auto rawScaffoldInfo = dbshow(damFile, dbshowOptions);
2358 
2359     return ScaffoldStructureReader(rawScaffoldInfo);
2360 }
2361 
2362 alias ScaffoldSegment = Algebraic!(ContigSegment, GapSegment);
2363 
2364 struct ContigSegment
2365 {
2366     size_t globalContigId;
2367     size_t scaffoldId;
2368     size_t contigId;
2369     size_t begin;
2370     size_t end;
2371     string header;
2372 
2373     invariant
2374     {
2375         assert(begin < end);
2376     }
2377 
2378     @property size_t length() const pure nothrow
2379     {
2380         return end - begin;
2381     }
2382 }
2383 
2384 struct GapSegment
2385 {
2386     size_t beginGlobalContigId;
2387     size_t endGlobalContigId;
2388     size_t scaffoldId;
2389     size_t beginContigId;
2390     size_t endContigId;
2391     size_t begin;
2392     size_t end;
2393 
2394     invariant
2395     {
2396         assert(begin < end);
2397     }
2398 
2399     @property size_t length() const pure nothrow
2400     {
2401         return end - begin;
2402     }
2403 }
2404 
2405 private struct ScaffoldStructureReader
2406 {
2407     static enum scaffoldInfoLineFormat = "%s:: Contig %d[%d,%d]";
2408     alias RawScaffoldInfo = typeof("".lineSplitter);
2409 
2410     private RawScaffoldInfo rawScaffoldInfo;
2411     private ContigSegment lastContigPart;
2412     private ScaffoldSegment currentPart;
2413     private bool _empty;
2414 
2415     this(string rawScaffoldInfo)
2416     {
2417         this.rawScaffoldInfo = rawScaffoldInfo.lineSplitter;
2418         // Force the first element to be a contigPart.
2419         this.currentPart = GapSegment();
2420         // Make `scaffoldId`s start at 0.
2421         this.lastContigPart.scaffoldId = -1UL;
2422 
2423         if (!empty)
2424         {
2425             popFront();
2426         }
2427     }
2428 
2429     void popFront()
2430     {
2431         assert(!empty, "Attempting to popFront an empty ScaffoldStructureReader");
2432 
2433         if (rawScaffoldInfo.empty)
2434         {
2435             _empty = true;
2436 
2437             return;
2438         }
2439 
2440         auto nextContigPart = ContigSegment(
2441             lastContigPart.globalContigId + 1,
2442             lastContigPart.scaffoldId,
2443         );
2444 
2445         rawScaffoldInfo.front.formattedRead!scaffoldInfoLineFormat(
2446             nextContigPart.header,
2447             nextContigPart.contigId,
2448             nextContigPart.begin,
2449             nextContigPart.end,
2450         );
2451         auto hasHeaderChanged = lastContigPart.header != nextContigPart.header[0 .. $ - 1];
2452 
2453         if (hasHeaderChanged)
2454             ++nextContigPart.scaffoldId;
2455 
2456         if (currentPart.peek!GapSegment !is null
2457                 || lastContigPart.scaffoldId != nextContigPart.scaffoldId)
2458         {
2459             assert(nextContigPart.header[$ - 1] == ' ');
2460             // Remove the trailing space
2461             nextContigPart.header = nextContigPart.header[0 .. $ - 1];
2462             lastContigPart = nextContigPart;
2463             currentPart = nextContigPart;
2464             rawScaffoldInfo.popFront();
2465         }
2466         else
2467         {
2468             currentPart = GapSegment(
2469                 lastContigPart.globalContigId,
2470                 nextContigPart.globalContigId,
2471                 lastContigPart.scaffoldId,
2472                 lastContigPart.contigId,
2473                 nextContigPart.contigId,
2474                 lastContigPart.end,
2475                 nextContigPart.begin,
2476             );
2477         }
2478     }
2479 
2480     @property ScaffoldSegment front() const
2481     {
2482         assert(!empty, "Attempting to fetch the front of an empty ScaffoldStructureReader");
2483         return currentPart;
2484     }
2485 
2486     @property bool empty() const
2487     {
2488         return _empty;
2489     }
2490 
2491     ScaffoldStructureReader save()
2492     {
2493         ScaffoldStructureReader copy;
2494 
2495         copy.rawScaffoldInfo = this.rawScaffoldInfo.save;
2496         copy.lastContigPart = this.lastContigPart;
2497         copy.currentPart = this.currentPart;
2498         copy._empty = this._empty;
2499 
2500         return copy;
2501     }
2502 }
2503 
2504 unittest
2505 {
2506     auto exampleDump = q"EOS
2507 >reference_mod/1/0_837550 RQ=0.850 :: Contig 0[0,8300]
2508 >reference_mod/1/0_837550 RQ=0.850 :: Contig 1[12400,20750]
2509 >reference_mod/1/0_837550 RQ=0.850 :: Contig 2[29200,154900]
2510 >reference_mod/1/0_837550 RQ=0.850 :: Contig 3[159900,169900]
2511 >reference_mod/1/0_837550 RQ=0.850 :: Contig 4[174900,200650]
2512 >reference_mod/1/0_837550 RQ=0.850 :: Contig 5[203650,216400]
2513 >reference_mod/1/0_837550 RQ=0.850 :: Contig 6[218900,235150]
2514 >reference_mod/1/0_837550 RQ=0.850 :: Contig 7[238750,260150]
2515 >reference_mod/1/0_837550 RQ=0.850 :: Contig 8[263650,837500]
2516 >reference_mod/2/0_1450 RQ=0.850 :: Contig 0[0,1450]
2517 EOS";
2518 
2519     auto reader = ScaffoldStructureReader(exampleDump);
2520     auto scaffoldStructure = reader.array;
2521 
2522     assert(scaffoldStructure == [
2523         ScaffoldSegment(ContigSegment(
2524             1, 0, 0, 0, 8300,
2525             ">reference_mod/1/0_837550 RQ=0.850",
2526         )),
2527         ScaffoldSegment(GapSegment(1, 2, 0, 0, 1, 8300, 12400)),
2528         ScaffoldSegment(ContigSegment(
2529             2, 0, 1, 12400, 20750,
2530             ">reference_mod/1/0_837550 RQ=0.850",
2531         )),
2532         ScaffoldSegment(GapSegment(2, 3, 0, 1, 2, 20750, 29200)),
2533         ScaffoldSegment(ContigSegment(
2534             3, 0, 2, 29200, 154900,
2535             ">reference_mod/1/0_837550 RQ=0.850",
2536         )),
2537         ScaffoldSegment(GapSegment(3, 4, 0, 2, 3, 154900, 159900)),
2538         ScaffoldSegment(ContigSegment(
2539             4, 0, 3, 159900, 169900,
2540             ">reference_mod/1/0_837550 RQ=0.850",
2541         )),
2542         ScaffoldSegment(GapSegment(4, 5, 0, 3, 4, 169900, 174900)),
2543         ScaffoldSegment(ContigSegment(
2544             5, 0, 4, 174900, 200650,
2545             ">reference_mod/1/0_837550 RQ=0.850",
2546         )),
2547         ScaffoldSegment(GapSegment(5, 6, 0, 4, 5, 200650, 203650)),
2548         ScaffoldSegment(ContigSegment(
2549             6, 0, 5, 203650, 216400,
2550             ">reference_mod/1/0_837550 RQ=0.850",
2551         )),
2552         ScaffoldSegment(GapSegment(6, 7, 0, 5, 6, 216400, 218900)),
2553         ScaffoldSegment(ContigSegment(
2554             7, 0, 6, 218900, 235150,
2555             ">reference_mod/1/0_837550 RQ=0.850",
2556         )),
2557         ScaffoldSegment(GapSegment(7, 8, 0, 6, 7, 235150, 238750)),
2558         ScaffoldSegment(ContigSegment(
2559             8, 0, 7, 238750, 260150,
2560             ">reference_mod/1/0_837550 RQ=0.850",
2561         )),
2562         ScaffoldSegment(GapSegment(8, 9, 0, 7, 8, 260150, 263650)),
2563         ScaffoldSegment(ContigSegment(
2564             9, 0, 8, 263650, 837500,
2565             ">reference_mod/1/0_837550 RQ=0.850",
2566         )),
2567         ScaffoldSegment(ContigSegment(
2568             10, 1, 0, 0, 1450,
2569             ">reference_mod/2/0_1450 RQ=0.850",
2570         )),
2571     ]);
2572 }
2573 
2574 /**
2575     Get the hidden files comprising the designated mask.
2576 */
2577 auto getMaskFiles(in string dbFile, in string maskDestination)
2578 {
2579     auto destinationDir = maskDestination.dirName;
2580     auto maskName = maskDestination.baseName;
2581     auto dbName = dbFile.baseName.stripExtension;
2582     auto maskHeader = format!"%s/.%s.%s.anno"(destinationDir, dbName, maskName);
2583     auto maskData = format!"%s/.%s.%s.data"(destinationDir, dbName, maskName);
2584 
2585     return tuple!("header", "data")(maskHeader, maskData);
2586 }
2587 
2588 /// Thrown on failure while reading a Dazzler mask.
2589 ///
2590 /// See_Also: `readMask`
2591 class MaskReaderException : Exception
2592 {
2593     pure nothrow @nogc @safe this(string msg, string file = __FILE__,
2594             size_t line = __LINE__, Throwable nextInChain = null)
2595     {
2596         super(msg, file, line, nextInChain);
2597     }
2598 }
2599 
2600 private
2601 {
2602     alias MaskHeaderEntry = int;
2603     alias MaskDataPointer = long;
2604     alias MaskDataEntry = int;
2605 }
2606 
2607 /**
2608     Read the `Region`s of a Dazzler mask for `dbFile`.
2609 
2610     Throws: MaskReaderException
2611     See_Also: `writeMask`, `getMaskFiles`
2612 */
2613 Region[] readMask(Region)(in string dbFile, in string maskDestination, in string workdir)
2614 {
2615     alias _enforce = enforce!MaskReaderException;
2616 
2617     auto maskFileNames = getMaskFiles(dbFile, maskDestination);
2618     auto maskHeader = readMaskHeader(maskFileNames.header);
2619     auto maskData = getBinaryFile!MaskDataEntry(maskFileNames.data);
2620 
2621     auto maskRegions = appender!(Region[]);
2622     alias RegionContigId = typeof(maskRegions.data[0].tag);
2623     alias RegionBegin = typeof(maskRegions.data[0].begin);
2624     alias RegionEnd = typeof(maskRegions.data[0].end);
2625     auto numReads = getNumContigs(dbFile, workdir).to!int;
2626 
2627     size_t currentContig = 1;
2628 
2629     if (maskHeader.numReads != numReads)
2630         logJsonWarn(
2631             "info",
2632             "mask does not match DB: number of reads does not match",
2633         );
2634     _enforce(maskHeader.size == 0, "corrupted mask: expected 0");
2635     _enforce(maskHeader.dataPointers.length == maskHeader.numReads + 1,
2636              "corrupted mask: unexpected number of data pointers");
2637 
2638     foreach (dataPtrRange; maskHeader.dataPointers[].slide!(No.withPartial)(2))
2639     {
2640         auto dataPtrs = dataPtrRange.map!(ptr => ptr / MaskDataEntry.sizeof)
2641             .takeExactly!2;
2642         _enforce(0 <= dataPtrs[0] && dataPtrs[0] <= dataPtrs[1]
2643                 && dataPtrs[1] <= maskData.length, "corrupted mask: data pointer out of bounds");
2644         _enforce(dataPtrs[0] % 2 == 0 && dataPtrs[1] % 2 == 0,
2645                 "corrupted mask: non-sense data pointers");
2646 
2647         foreach (interval; maskData[dataPtrs[0] .. dataPtrs[1]].chunks(2))
2648         {
2649             enforce!MaskReaderException(interval.length == 2 && 0 <= interval[0]
2650                     && interval[0] <= interval[1], "corrupted mask: invalid interval");
2651 
2652             Region newRegion;
2653             newRegion.tag = currentContig.to!RegionContigId;
2654             newRegion.begin = interval[0].to!RegionBegin;
2655             newRegion.end = interval[1].to!RegionEnd;
2656 
2657             maskRegions ~= newRegion;
2658         }
2659 
2660         ++currentContig;
2661     }
2662 
2663     return maskRegions.data;
2664 }
2665 
2666 private auto readMaskHeader(in string fileName)
2667 {
2668     auto headerFile = File(fileName, "rb");
2669     MaskHeaderEntry[2] headerBuffer;
2670     auto numPointers = (headerFile.size - headerBuffer.sizeof) / MaskDataPointer.sizeof;
2671     auto pointerBuffer = uninitializedArray!(MaskDataPointer[])(numPointers);
2672 
2673     enforce!MaskReaderException(headerFile.rawRead(headerBuffer).length == headerBuffer.length,
2674             format!"error while reading mask header `%s`: file too short"(fileName));
2675     enforce!MaskReaderException(headerFile.rawRead(pointerBuffer).length == numPointers,
2676             format!"error while reading mask header `%s`: file too short"(fileName));
2677 
2678     return tuple!("numReads", "size", "dataPointers")(headerBuffer[0],
2679             headerBuffer[1], pointerBuffer);
2680 }
2681 
2682 private T[] getBinaryFile(T)(in string fileName)
2683 {
2684     auto file = File(fileName, "rb");
2685     auto bufferLength = file.size / T.sizeof;
2686     auto dataBuffer = file.rawRead(uninitializedArray!(T[])(bufferLength));
2687 
2688     enforce!MaskReaderException(dataBuffer.length == bufferLength,
2689             format!"error while reading binary file `%s`: expected %d bytes of data but read only %d"(
2690                 fileName, bufferLength * T.sizeof, dataBuffer.length * T.sizeof));
2691 
2692     return dataBuffer;
2693 }
2694 
2695 /**
2696     Write the list of regions to a Dazzler mask for `dbFile`.
2697 
2698     See_Also: `readMask`, `getMaskFiles`
2699 */
2700 void writeMask(Region)(
2701     in string dbFile,
2702     in string maskDestination,
2703     in Region[] regions,
2704     in string workdir,
2705 )
2706 {
2707     alias MaskRegion = Tuple!(
2708         MaskHeaderEntry, "tag",
2709         MaskDataEntry, "begin",
2710         MaskDataEntry, "end",
2711     );
2712 
2713     if (regions.length == 0)
2714     {
2715         logJsonDiagnostic(
2716             "notice", "skipping empty mask",
2717             "dbFile", dbFile,
2718             "maskDestination", maskDestination,
2719         );
2720 
2721         return;
2722     }
2723 
2724     auto maskFileNames = getMaskFiles(dbFile, maskDestination);
2725     auto maskHeader = File(maskFileNames.header, "wb");
2726     auto maskData = File(maskFileNames.data, "wb");
2727 
2728     auto maskRegions = regions
2729         .map!(region => MaskRegion(
2730             region.tag.to!MaskHeaderEntry,
2731             region.begin.to!MaskDataEntry,
2732             region.end.to!MaskDataEntry,
2733         ))
2734         .array;
2735     maskRegions.sort();
2736 
2737     auto numReads = getNumContigs(dbFile, workdir).to!MaskHeaderEntry;
2738     MaskHeaderEntry size = 0; // Mark the DAZZ_TRACK as a mask (see DAZZ_DB/DB.c:1183)
2739     MaskHeaderEntry currentContig = 1;
2740     MaskDataPointer dataPointer = 0;
2741 
2742     maskHeader.rawWrite([numReads, size]);
2743     maskHeader.rawWrite([dataPointer]);
2744     foreach (maskRegion; maskRegions)
2745     {
2746         assert(maskRegion.tag >= currentContig);
2747 
2748         while (maskRegion.tag > currentContig)
2749         {
2750             maskHeader.rawWrite([dataPointer]);
2751             ++currentContig;
2752         }
2753 
2754         if (maskRegion.tag == currentContig)
2755         {
2756             maskData.rawWrite([maskRegion.begin, maskRegion.end]);
2757             dataPointer += typeof(maskRegion.begin).sizeof + typeof(maskRegion.end).sizeof;
2758         }
2759     }
2760 
2761     foreach (emptyContig; currentContig .. numReads + 1)
2762     {
2763         maskHeader.rawWrite([dataPointer]);
2764     }
2765 }
2766 
2767 /// Options for `daccord`.
2768 enum DaccordOptions : string
2769 {
2770     /// number of threads (default 4)
2771     numberOfThreads = "-t",
2772     /// window size (default 40)
2773     windowSize = "-w",
2774     /// advance size (default 10)
2775     advanceSize = "-a",
2776     /// max depth (default 18446744073709551615)
2777     maxDepth = "-d",
2778     /// produce full sequences (default 0)
2779     produceFullSequences = "-f",
2780     /// verbosity (default 18446744073709551615)
2781     verbosity = "-V",
2782     /// read interval (default 0,18446744073709551615)
2783     readInterval = "-I",
2784     /// reads part (default 0,1)
2785     readsPart = "-J",
2786     /// error profile file name (default input.las.eprof)
2787     errorProfileFileName = "-E",
2788     /// minimum window coverage (default 3)
2789     minWindowCoverage = "-m",
2790     /// maximum window error (default 18446744073709551615)
2791     maxWindowError = "-e",
2792     /// minimum length of output (default 0)
2793     minLengthOfOutput = "-l",
2794     /// minimum k-mer filter frequency (default 0)
2795     minKMerFilterFrequency = "--minfilterfreq",
2796     /// maximum k-mer filter frequency (default 2)
2797     maxKMerFilterFrequency = "--maxfilterfreq",
2798     /// temporary file prefix (default daccord_ozelot_4500_1529654843)
2799     temporaryFilePrefix = "-T",
2800     /// maximum number of alignments considered per read (default 5000)
2801     maxAlignmentsPerRead = "-D",
2802     /// maximum number of alignments considered per read (default 0)
2803     maxAlignmentsPerReadVard = "--vard",
2804     /// compute error profile only (default disable)
2805     computeErrorProfileOnly = "--eprofonly",
2806     /// compute error distribution estimate (default disable)
2807     computeErrorDistributionEstimate = "--deepprofileonly",
2808     /// kmer size (default 8)
2809     kmerSize = "-k",
2810 }
2811 
2812 /// Options for `daligner`.
2813 enum DalignerOptions : string
2814 {
2815     verbose = "-v",
2816     /// If the -b option is set, then the daligner assumes the data has a
2817     /// strong compositional bias (e.g. >65% AT rich).
2818     strongCompositionalBias = "-b",
2819     /// If the -A option is set (“A” for “asymmetric”) then just overlaps
2820     /// where the a-read is in block X and the b-read is in block Y are
2821     /// reported, and if X = Y then it further reports only those overlaps
2822     /// where the a-read index is less than the b-read index.
2823     asymmetric = "-A",
2824     /// If the -I option is set (“I” for “identity”) then when X = Y, overlaps
2825     /// between different portions of the same read will also be found and
2826     /// reported.
2827     identity = "-I",
2828     /// Search code looks for a pair of diagonal bands of width 2^^w
2829     /// (default 26 = 64) that contain a collection of exact matching k-mers
2830     /// (default 14) between the two reads, such that the total number of
2831     /// bases covered by the k-mer hits is h (default 35).
2832     kMerSize = "-k",
2833     /// ditto
2834     bandWidth = "-w",
2835     /// ditto
2836     hitBaseCoverage = "-h",
2837     /// Suppresses the use of any k-mer that occurs more than t times in
2838     /// either the subject or target block.
2839     maxKmerOccurence = "-t",
2840     /// Let the program automatically select a value of t that meets a given
2841     /// memory usage limit specified (in Gb) by the -M parameter.
2842     maxKmerMemory = "-M",
2843     tempDir = "-P",
2844     /// Searching for local alignments involving at least -l base pairs
2845     /// (default 1000) or more, that have an average correlation rate of
2846     /// -e (default 70%).
2847     minAlignmentLength = "-l",
2848     /// ditto
2849     averageCorrelationRate = "-e",
2850     /// The local alignments found will be output in a sparse encoding where
2851     /// a trace point on the alignment is recorded every -s base pairs of
2852     /// the a-read (default 100bp).
2853     tracePointDistance = "-s",
2854     /// By setting the -H parameter to say N, one alters daligner so that it
2855     /// only reports overlaps where the a-read is over N base-pairs long.
2856     minAReadLength = "-H",
2857     /// The program runs with 4 threads by default, but this may be set to
2858     /// any power of 2 with the -T option.
2859     numThreads = "-T",
2860     /// If there are one or more interval tracks specified with the -m option
2861     /// (m for mask), then the reads of the DB or DB’s to which the track
2862     /// applies are soft masked with the union of the intervals of all the
2863     /// interval tracks that apply, that is any k-mers that contain any bases
2864     /// in any of the masked intervals are ignored for the purposes of seeding
2865     /// a match.
2866     masks = "-m",
2867 }
2868 
2869 /// Options for `damapper`.
2870 enum DamapperOptions : string
2871 {
2872     verbose = "-v",
2873     /// If the -b option is set, then the daligner assumes the data has a
2874     /// strong compositional bias (e.g. >65% AT rich).
2875     strongCompositionalBias = "-b",
2876     /// Search code looks for a pair of diagonal bands of width 2^^w
2877     /// (default 26 = 64) that contain a collection of exact matching k-mers
2878     /// (default 14) between the two reads, such that the total number of
2879     /// bases covered by the k-mer hits is h (default 35).
2880     kMerSize = "-k",
2881     /// Suppresses the use of any k-mer that occurs more than t times in
2882     /// either the subject or target block.
2883     maxKmerOccurence = "-t",
2884     /// Let the program automatically select a value of t that meets a given
2885     /// memory usage limit specified (in Gb) by the -M parameter.
2886     maxKmerMemory = "-M",
2887     /// ditto
2888     averageCorrelationRate = "-e",
2889     /// The local alignments found will be output in a sparse encoding where
2890     /// a trace point on the alignment is recorded every -s base pairs of
2891     /// the a-read (default 100bp).
2892     tracePointDistance = "-s",
2893     /// The program runs with 4 threads by default, but this may be set to
2894     /// any power of 2 with the -T option.
2895     numThreads = "-T",
2896     /// If there are one or more interval tracks specified with the -m option
2897     /// (m for mask), then the reads of the DB or DB’s to which the track
2898     /// applies are soft masked with the union of the intervals of all the
2899     /// interval tracks that apply, that is any k-mers that contain any bases
2900     /// in any of the masked intervals are ignored for the purposes of seeding
2901     /// a match.
2902     masks = "-m",
2903     /// If the -n option is given then all chains that are within the given
2904     /// fraction of the best are also reported, e.g. -n.95 reports all
2905     /// matches within 95% of the top match.
2906     bestMatches = "-n",
2907     /// The -p option requests that damapper produce a repeat profile track
2908     /// for each read.
2909     repeatProfileTrack = "-p",
2910     /// The parameter -z asks that LAs are sorted in pile order as opposed to
2911     /// map order (see the -a option of daligner for which this is the
2912     /// negation).
2913     sortPileOrder = "-z",
2914     /// If the -C option is set, then damapper also outputs a file Y.X.las
2915     /// for a given block pair that contains all the same matches as in
2916     /// X.Y.las but where the A-read is a contig of the reference and the
2917     /// B-read is a mapped read. And if the -N options is set, then the file
2918     /// Y.X.las is not produced.
2919     symmetric = "-C",
2920     /// ditto
2921     oneDirection = "-N",
2922 }
2923 
2924 /// Options for `DBdump`.
2925 enum DBdumpOptions
2926 {
2927     readNumber = "-r",
2928     originalHeader = "-h",
2929     sequenceString = "-s",
2930     sNROfACGTChannels = "-a",
2931     intrinsicQualityVector = "-i",
2932     quivaValues = "-q",
2933     repeatProfileVector = "-p",
2934     masks = "-m",
2935     untrimmedDatabase = "-u",
2936     upperCase = "-U",
2937 }
2938 
2939 /// Options for `DBshow`.
2940 enum DBshowOptions
2941 {
2942     untrimmedDatabase = "-u",
2943     showQuiva = "-q",
2944     showArrowPulseSequence = "-a",
2945     noSequence = "-n",
2946     masks = "-m",
2947     produceQuivaFile = "-Q",
2948     produceArrowFile = "-A",
2949     upperCase = "-U",
2950     fastaLineWidth = "-w",
2951 }
2952 
2953 /// Options for `fasta2DAM` and `fasta2DB`.
2954 enum Fasta2DazzlerOptions
2955 {
2956     verbose = "-v",
2957     /// Import files listed 1/line in given file.
2958     fromFile = "-f",
2959     /// Import data from stdin, use optional name as data source.
2960     fromStdin = "-i",
2961 }
2962 
2963 /// Options for `LAdump`.
2964 enum LAdumpOptions
2965 {
2966     coordinates = "-c",
2967     numDiffs = "-d",
2968     tracePoints = "-t",
2969     lengths = "-l",
2970     properOverlapsOnly = "-o",
2971 }
2972 
2973 /// Options for `computeintrinsicqv`.
2974 enum ComputeIntrinsicQVOptions
2975 {
2976     /// Read depth aka. read coverage. (mandatory)
2977     readDepth = "-d",
2978 }
2979 
2980 /// Options for `lasfilteralignments`.
2981 enum LasFilterAlignmentsOptions
2982 {
2983     /// Error threshold for proper alignment termination (default: 0.35)
2984     errorThresold = "-e",
2985 }
2986 
2987 private
2988 {
2989 
2990     void dalign(in string refDam, in string[] dalignerOpts, in string workdir)
2991     {
2992         dalign([refDam], dalignerOpts, workdir);
2993     }
2994 
2995     void dalign(in string refDam, in string readsDam, in string[] dalignerOpts, in string workdir)
2996     {
2997         dalign([refDam, readsDam], dalignerOpts, workdir);
2998     }
2999 
3000     void dalign(in string[] dbList, in string[] dalignerOpts, in string workdir)
3001     {
3002         assert(dbList.length >= 1);
3003         auto isSelfAlignment = dbList.length == 1;
3004         auto additionalOptions = only(isSelfAlignment ? DalignerOptions.identity : null);
3005         auto inputFiles = isSelfAlignment ? [dbList[0], dbList[0]] : dbList;
3006         const(string[]) inputFilesRelativeToWorkDir = inputFiles.map!(
3007                 f => f.relativeToWorkdir(workdir)).array;
3008 
3009         executeCommand(chain(only("daligner"), additionalOptions, dalignerOpts,
3010                 inputFilesRelativeToWorkDir), workdir);
3011     }
3012 
3013     void damapper(in string refDam, in string readsDam, in string[] damapperOpts, in string workdir)
3014     {
3015         damapper([refDam, readsDam], damapperOpts, workdir);
3016     }
3017 
3018     void damapper(in string[] dbList, in string[] damapperOpts, in string workdir)
3019     {
3020         const(string[]) dbListRelativeToWorkDir = dbList.map!(
3021                 f => f.relativeToWorkdir(workdir)).array;
3022 
3023         executeCommand(chain(only("damapper", DamapperOptions.symmetric),
3024                 damapperOpts, dbListRelativeToWorkDir), workdir);
3025     }
3026 
3027     void computeIntrinsicQV(in string dbFile, in string lasFile, in size_t readDepth,
3028                              in string workdir)
3029     {
3030         executeCommand(chain(
3031             only("computeintrinsicqv"),
3032             only(
3033                 ComputeIntrinsicQVOptions.readDepth ~ readDepth.to!string,
3034                 dbFile.stripBlock.relativeToWorkdir(workdir),
3035                 lasFile.relativeToWorkdir(workdir),
3036             ),
3037         ), workdir);
3038     }
3039 
3040     string lasFilterAlignments(in string dbFile, in string lasFile, in string[] options,
3041                              in string workdir)
3042     {
3043         string filteredLasFile = lasFile.stripExtension.to!string ~ "-filtered.las";
3044 
3045         executeCommand(chain(
3046             only("lasfilteralignments"),
3047             options,
3048             only(
3049                 filteredLasFile.relativeToWorkdir(workdir),
3050                 dbFile.stripBlock.relativeToWorkdir(workdir),
3051                 lasFile.relativeToWorkdir(workdir),
3052             ),
3053         ), workdir);
3054 
3055         return filteredLasFile;
3056     }
3057 
3058     string daccord(in string dbFile, in string lasFile, in string[] daccordOpts, in string workdir)
3059     {
3060         alias esc = escapeShellCommand;
3061         string daccordedDb = dbFile.stripExtension.to!string ~ "-daccord.dam";
3062 
3063         executeShell(chain(
3064             only("daccord"),
3065             only(esc(daccordOpts)),
3066             only(esc(lasFile.relativeToWorkdir(workdir))),
3067             only(esc(dbFile.stripBlock.relativeToWorkdir(workdir))),
3068             only("|"),
3069             only("fasta2DAM", Fasta2DazzlerOptions.fromStdin),
3070             only(esc(daccordedDb.relativeToWorkdir(workdir))),
3071         ), workdir);
3072 
3073         return daccordedDb;
3074     }
3075 
3076     void silentDaccord(in string dbFile, in string lasFile, in string[] daccordOpts,
3077             in string workdir)
3078     {
3079         executeCommand(chain(
3080             only("daccord"),
3081             daccordOpts,
3082             only(lasFile.relativeToWorkdir(workdir)),
3083             only(dbFile.stripBlock.relativeToWorkdir(workdir)),
3084         ), workdir);
3085     }
3086 
3087     void buildSubsetDb(R)(in string inDbFile, in string outDbFile, R readIds, in string workdir)
3088     {
3089         alias esc = escapeShellCommand;
3090         auto escapedReadIds = readIds
3091             .map!(to!size_t)
3092             .map!(to!string)
3093             .map!esc;
3094 
3095         executeShell(chain(
3096             only("DBshow"),
3097             only(esc(inDbFile.relativeToWorkdir(workdir))),
3098             escapedReadIds,
3099             only("|"),
3100             only("fasta2DAM", Fasta2DazzlerOptions.fromStdin),
3101             only(esc(outDbFile.relativeToWorkdir(workdir))),
3102         ), workdir);
3103     }
3104 
3105     void fasta2dam(Range)(in string outFile, Range fastaRecords, in string workdir)
3106             if (isInputRange!(Unqual!Range) && isSomeString!(ElementType!(Unqual!Range)))
3107     {
3108         import std.algorithm : each, joiner;
3109         import std.process : Config, pipeProcess, Redirect, wait;
3110         import std.range : chunks;
3111 
3112         enum writeChunkSize = 1024 * 1024;
3113         auto outFileArg = outFile.relativeToWorkdir(workdir);
3114         auto command = ["fasta2DAM", Fasta2DazzlerOptions.fromStdin, outFileArg];
3115 
3116         if (shouldLog(LogLevel.diagnostic))
3117         {
3118             static if (isForwardRange!Range)
3119                 auto input = fastaRecords
3120                     .save
3121                     .map!(record => record[0 .. min(1024, $)].toJson)
3122                     .array[0 .. min(1024, $)]
3123                     .toJson;
3124             else
3125                 auto input = toJson(null);
3126 
3127             logJsonDiagnostic(
3128                 "action", "execute",
3129                 "type", "pipe",
3130                 "command", command.map!Json.array,
3131                 "input", input,
3132                 "state", "pre",
3133             );
3134         }
3135 
3136         auto process = pipeProcess(
3137             ["fasta2DAM", Fasta2DazzlerOptions.fromStdin, outFileArg],
3138             Redirect.stdin,
3139             null, // env
3140             Config.none,
3141             workdir
3142         );
3143         fastaRecords
3144             .filter!(fastaRecord => parseFastaRecord(fastaRecord).length >= minSequenceLength)
3145             .joiner(only('\n'))
3146             .chain("\n")
3147             .chunks(writeChunkSize)
3148             .each!(chunk => process.stdin.write(chunk.array));
3149         process.stdin.close();
3150         auto exitStatus = wait(process.pid);
3151         if (exitStatus != 0)
3152         {
3153             throw new DazzlerCommandException(
3154                     format!"command `fasta2dam` failed with exit code %d"(exitStatus));
3155         }
3156 
3157         return;
3158     }
3159 
3160     void fasta2dam(in string inFile, in string outFile, in string workdir)
3161     {
3162         executeCommand(only("fasta2DAM", outFile.relativeToWorkdir(workdir), inFile), workdir);
3163     }
3164 
3165     void dbsplit(in string dbFile, in string[] dbsplitOptions, in string workdir)
3166     {
3167         executeCommand(chain(only("DBsplit"), dbsplitOptions,
3168                 only(dbFile.stripBlock.relativeToWorkdir(workdir))), workdir);
3169     }
3170 
3171     auto ladump(in string lasFile, in string dbA, in string dbB,
3172             in string[] ladumpOpts, in string workdir)
3173     {
3174         return ladump(lasFile, dbA, dbB, [], ladumpOpts, workdir);
3175     }
3176 
3177     auto ladump(in string lasFile, in string dbA, in string dbB, in id_t[] readIds,
3178             in string[] ladumpOpts, in string workdir)
3179     {
3180         return executePipe(chain(
3181             only("LAdump"),
3182             ladumpOpts,
3183             only(
3184                 dbA.stripBlock.relativeToWorkdir(workdir),
3185                 dbB.stripBlock.relativeToWorkdir(workdir),
3186                 lasFile.relativeToWorkdir(workdir)
3187             ),
3188             readIds.map!(to!string),
3189         ), workdir);
3190     }
3191 
3192     auto dbdump(Range)(in string dbFile, Range recordNumbers,
3193             in string[] dbdumpOptions, in string workdir)
3194             if (isForwardRange!Range && is(ElementType!Range : size_t))
3195     {
3196         return executePipe(chain(
3197             only("DBdump"),
3198             dbdumpOptions,
3199             only(dbFile.relativeToWorkdir(workdir)),
3200             recordNumbers.map!(to!string)
3201         ), workdir);
3202     }
3203 
3204     auto dbdump(
3205         in string dbFile,
3206         id_t firstRecord,
3207         id_t lastRecord,
3208         in string[] dbdumpOptions,
3209         in string workdir,
3210     )
3211     {
3212         return executePipe(chain(
3213             only("DBdump"),
3214             dbdumpOptions,
3215             only(
3216                 dbFile.relativeToWorkdir(workdir),
3217                 format!"%d-%d"(firstRecord, lastRecord),
3218             ),
3219         ), workdir);
3220     }
3221 
3222     string dbshow(in string dbFile, in string contigId)
3223     {
3224         return executeCommand(only("DBshow", dbFile, contigId));
3225     }
3226 
3227     string dbshow(in string dbFile, in string[] dbshowOptions)
3228     {
3229         return executeCommand(chain(only("DBshow"), dbshowOptions, only(dbFile)));
3230     }
3231 
3232     auto executePipe(Range)(Range command, in string workdir = null)
3233             if (isInputRange!Range && isSomeString!(ElementType!Range))
3234     {
3235         static final class LinesPipe
3236         {
3237             static enum lineTerminator = "\n";
3238 
3239             private const string[] command;
3240             private const string workdir;
3241             private ProcessPipes process;
3242             private string currentLine;
3243 
3244             this(in string[] command, in string workdir)
3245             {
3246                 this.command = command;
3247                 this.workdir = workdir;
3248             }
3249 
3250             ~this()
3251             {
3252                 if (!(process.pid is null))
3253                     releaseProcess();
3254             }
3255 
3256             void releaseProcess()
3257             {
3258                 if (!process.stdout.isOpen)
3259                     return;
3260 
3261                 process.stdout.close();
3262 
3263                 version (Posix)
3264                 {
3265                     import core.sys.posix.signal : SIGKILL;
3266 
3267                     process.pid.kill(SIGKILL);
3268                 }
3269                 else
3270                 {
3271                     static assert(0, "Only intended for use on POSIX compliant OS.");
3272                 }
3273                 process.pid.wait();
3274             }
3275 
3276             private void ensureInitialized()
3277             {
3278                 if (!(process.pid is null))
3279                     return;
3280 
3281                 logJsonDiagnostic(
3282                     "action", "execute",
3283                     "type", "pipe",
3284                     "command", command.toJson,
3285                     "state", "pre",
3286                 );
3287                 process = pipeProcess(command, Redirect.stdout, null, Config.none, workdir);
3288 
3289                 if (!empty)
3290                     popFront();
3291             }
3292 
3293             void popFront()
3294             {
3295                 ensureInitialized();
3296                 assert(!empty, "Attempting to popFront an empty LinesPipe");
3297                 currentLine = process.stdout.readln();
3298 
3299                 if (currentLine.empty)
3300                 {
3301                     currentLine = null;
3302                     releaseProcess();
3303                 }
3304 
3305                 if (currentLine.endsWith(lineTerminator))
3306                     currentLine = currentLine[0 .. $ - lineTerminator.length];
3307             }
3308 
3309             @property string front()
3310             {
3311                 ensureInitialized();
3312                 assert(!empty, "Attempting to fetch the front of an empty LinesPipe");
3313 
3314                 return currentLine;
3315             }
3316 
3317             @property bool empty()
3318             {
3319                 ensureInitialized();
3320 
3321                 if (!process.stdout.isOpen || process.stdout.eof)
3322                 {
3323                     releaseProcess();
3324 
3325                     return true;
3326                 }
3327                 else
3328                 {
3329                     return false;
3330                 }
3331             }
3332         }
3333 
3334         auto sanitizedCommand = command.filter!"a != null".array;
3335 
3336         return new LinesPipe(sanitizedCommand, workdir);
3337     }
3338 
3339     unittest
3340     {
3341         import std.algorithm : equal;
3342         import std.range : take;
3343 
3344         auto cheers = executePipe(only("yes", "Cheers!"), ".");
3345         assert(cheers.take(5).equal([
3346             "Cheers!",
3347             "Cheers!",
3348             "Cheers!",
3349             "Cheers!",
3350             "Cheers!",
3351         ]));
3352 
3353         auto helloWorld = executePipe(only("echo", "Hello World!"), ".");
3354         assert(helloWorld.equal(["Hello World!"]));
3355     }
3356 
3357     string executeCommand(Range)(in Range command, in string workdir = null)
3358             if (isInputRange!(Unqual!Range) && isSomeString!(ElementType!(Unqual!Range)))
3359     {
3360         import std.process : Config, execute;
3361 
3362         string output = command.executeWrapper!("command",
3363                 sCmd => execute(sCmd, null, // env
3364                     Config.none, size_t.max, workdir));
3365         return output;
3366     }
3367 
3368     void executeShell(Range)(in Range command, in string workdir = null)
3369             if (isInputRange!(Unqual!Range) && isSomeString!(ElementType!(Unqual!Range)))
3370     {
3371         import std.algorithm : joiner;
3372         import std.process : Config, executeShell;
3373 
3374         string output = command.executeWrapper!("shell",
3375                 sCmd => executeShell(sCmd.joiner(" ").array.to!string, null, // env
3376                     Config.none, size_t.max, workdir));
3377     }
3378 
3379     void executeScript(Range)(in Range command, in string workdir = null)
3380             if (isInputRange!(Unqual!Range) && isSomeString!(ElementType!(Unqual!Range)))
3381     {
3382         import std.process : Config, executeShell;
3383 
3384         string output = command.executeWrapper!("script",
3385                 sCmd => executeShell(sCmd.buildScriptLine, null, // env
3386                     Config.none, size_t.max, workdir));
3387     }
3388 
3389     string executeWrapper(string type, alias execCall, Range)(in Range command)
3390             if (isInputRange!(Unqual!Range) && isSomeString!(ElementType!(Unqual!Range)))
3391     {
3392         import std.array : array;
3393         import std.algorithm : filter;
3394         import std..string : lineSplitter;
3395 
3396         auto sanitizedCommand = command.filter!"a != null".array;
3397 
3398         logJsonDiagnostic(
3399             "action", "execute",
3400             "type", type,
3401             "command", sanitizedCommand.map!Json.array,
3402             "state", "pre",
3403         );
3404         auto result = execCall(sanitizedCommand);
3405         logJsonDiagnostic(
3406             "action", "execute",
3407             "type", type,
3408             "command", sanitizedCommand.map!Json.array,
3409             "output", result
3410                 .output[0 .. min(1024, $)]
3411                 .lineSplitter
3412                 .map!Json
3413                 .array,
3414             "exitStatus", result.status,
3415             "state", "post",
3416         );
3417         if (result.status > 0)
3418         {
3419             throw new DazzlerCommandException(
3420                     format("process %s returned with non-zero exit code %d: %s",
3421                     sanitizedCommand[0], result.status, result.output));
3422         }
3423 
3424         return result.output;
3425     }
3426 
3427     string buildScriptLine(in string[] command)
3428     {
3429         return escapeShellCommand(command) ~ " | sh -sve";
3430     }
3431 
3432     string stripBlock(in string fileName)
3433     {
3434         import std.regex : ctRegex, replaceFirst;
3435 
3436         enum blockNumRegex = ctRegex!(`\.[1-9][0-9]*\.(dam|db)$`);
3437 
3438         return fileName.replaceFirst(blockNumRegex, `.$1`);
3439     }
3440 
3441     unittest
3442     {
3443         assert("foo_bar.1.dam".stripBlock == "foo_bar.dam");
3444         assert("foo_bar.1024.db".stripBlock == "foo_bar.db");
3445         assert("foo_bar.dam".stripBlock == "foo_bar.dam");
3446         assert("foo_bar.db".stripBlock == "foo_bar.db");
3447     }
3448 
3449     string relativeToWorkdir(in string fileName, in string workdir)
3450     {
3451         return relativePath(absolutePath(fileName), absolutePath(workdir));
3452     }
3453 }