1 /**
2     This package contains methods to handle the proprietary binary data
3     container for `PileUp`s.
4 
5     Copyright: © 2018 Arne Ludwig <arne.ludwig@posteo.de>
6     License: Subject to the terms of the MIT license, as written in the
7              included LICENSE file.
8     Authors: Arne Ludwig <arne.ludwig@posteo.de>
9 */
10 module dentist.common.binio.pileupdb;
11 
12 import core.exception : AssertError;
13 import dentist.common.alignments :
14     AlignmentChain,
15     AlignmentLocationSeed,
16     coord_t,
17     diff_t,
18     id_t,
19     PileUp,
20     ReadAlignment,
21     SeededAlignment,
22     trace_point_t;
23 import dentist.common.binio._base :
24     ArrayStorage,
25     DbIndex,
26     lockIfPossible,
27     readRecord,
28     readRecordAt,
29     readRecords;
30 import std.array : minimallyInitializedArray;
31 import std.conv : to;
32 import std.exception : assertThrown, enforce, ErrnoException;
33 import std.format : format;
34 import std.typecons : tuple, Tuple;
35 import std.stdio : File;
36 
37 version (unittest) import dentist.common.binio._testdata :
38     getPileUpsTestData,
39     numLocalAlignments,
40     numPileUps,
41     numReadAlignments,
42     numSeededAlignments,
43     numTracePoints;
44 
45 class PileUpDbException : Exception
46 {
47     pure nothrow @nogc @safe this(string msg, string file = __FILE__,
48             size_t line = __LINE__, Throwable nextInChain = null)
49     {
50         super(msg, file, line, nextInChain);
51     }
52 }
53 
54 struct PileUpDb
55 {
56     private alias LocalAlignment = AlignmentChain.LocalAlignment;
57     private alias TracePoint = LocalAlignment.TracePoint;
58     private alias DbSlices = Tuple!(
59         ArrayStorage!(StorageType!PileUp), "pileUps",
60         ArrayStorage!(StorageType!ReadAlignment), "readAlignments",
61         ArrayStorage!(StorageType!SeededAlignment), "seededAlignments",
62         ArrayStorage!(StorageType!LocalAlignment), "localAlignments",
63         ArrayStorage!(StorageType!TracePoint), "tracePoints",
64     );
65 
66     private File pileUpDb;
67     private PileUpDbIndex dbIndex;
68     private DbSlices dbSlices;
69 
70     @property auto pileUps() const pure nothrow
71     {
72         return dbIndex.pileUps;
73     }
74     @property auto readAlignments() const pure nothrow
75     {
76         return dbIndex.readAlignments;
77     }
78     @property auto seededAlignments() const pure nothrow
79     {
80         return dbIndex.seededAlignments;
81     }
82     @property auto localAlignments() const pure nothrow
83     {
84         return dbIndex.localAlignments;
85     }
86     @property auto tracePoints() const pure nothrow
87     {
88         return dbIndex.tracePoints;
89     }
90 
91     static PileUpDb parse(in string dbFile)
92     {
93         auto pileUpDb = File(dbFile, "rb");
94         lockIfPossible(pileUpDb);
95 
96         auto db = PileUpDb(pileUpDb);
97         db.ensureDbIndex();
98 
99         return db;
100     }
101 
102     void releaseDb()
103     {
104         pileUpDb.close();
105     }
106 
107     PileUp[] opIndex()
108     {
109         ensureDbIndex();
110 
111         return readSlice(0, length);
112     }
113 
114     PileUp opIndex(size_t i)
115     {
116         ensureDbIndex();
117         enforce!PileUpDbException(
118             i < length,
119             format!"cannot read block %d in `%s`: out of bounds [0, %d)"(
120                     i, pileUpDb.name, length)
121         );
122 
123         return readSlice(i, i + 1)[0];
124     }
125 
126     PileUp[] opIndex(size_t[2] slice)
127     {
128         auto from = slice[0];
129         auto to = slice[1];
130         ensureDbIndex();
131         enforce!PileUpDbException(
132             from <= to && to <= length,
133             format!"cannot read blocks %d-%d in `%s`: out of bounds [0, %d]"(
134                     from, to, pileUpDb.name, length)
135         );
136 
137         return readSlice(from, to);
138     }
139 
140     size_t[2] opSlice(size_t dim)(size_t from, size_t to)
141             if (dim == 0)
142     {
143         assert(from <= to, "invalid slice");
144 
145         return [from, to];
146     }
147 
148     @property size_t length()
149     {
150         ensureDbIndex();
151 
152         return dbIndex.pileUps.length;
153     }
154 
155     alias opDollar = length;
156 
157     private void ensureDbIndex()
158     {
159         if (dbIndex != dbIndex.init)
160             return;
161 
162         dbIndex = pileUpDb.readRecord!PileUpDbIndex();
163     }
164 
165     private PileUp[] readSlice(size_t from, size_t to)
166     {
167         assert(from <= to && to <= length);
168 
169         if (from == to)
170             return [];
171 
172         // Step 1: determine memory requirements and DB slices
173         dbSlices = getDbSlices(from, to);
174 
175         // Step 2: allocate minimally initialized memory for all blocks
176         auto pileUps = minimallyInitializedArray!(PileUp[])(dbSlices.pileUps.length);
177         auto readAlignments = minimallyInitializedArray!(ReadAlignment[])(dbSlices.readAlignments.length);
178         auto seededAlignments = minimallyInitializedArray!(SeededAlignment[])(dbSlices.seededAlignments.length);
179         auto localAlignments = minimallyInitializedArray!(LocalAlignment[])(dbSlices.localAlignments.length);
180         auto tracePoints = minimallyInitializedArray!(TracePoint[])(dbSlices.tracePoints.length);
181 
182         // Step 3: parse each record for each block assigning already
183         //         allocated array slices to the array fields
184         parse(pileUps, readAlignments);
185         parse(seededAlignments, localAlignments);
186         parse(readAlignments, seededAlignments);
187         parse(localAlignments, tracePoints);
188         parse(tracePoints);
189 
190         return pileUps;
191     }
192 
193     private DbSlices getDbSlices(size_t from, size_t to)
194     {
195         auto pileUps = dbIndex.pileUps[from .. to];
196         auto firstPileUp = pileUpDb.readRecordAt!(StorageType!PileUp)(pileUps[0]);
197         auto lastPileUp = pileUpDb.readRecordAt!(StorageType!PileUp)(pileUps[$ - 1]);
198 
199         auto readAlignments = ArrayStorage!(StorageType!ReadAlignment).fromPtrs(
200             firstPileUp[0],
201             lastPileUp[$],
202         );
203         auto firstReadAlignment = pileUpDb.readRecordAt!(StorageType!ReadAlignment)(readAlignments[0]);
204         auto lastReadAlignment = pileUpDb.readRecordAt!(StorageType!ReadAlignment)(readAlignments[$ - 1]);
205 
206         auto seededAlignments = ArrayStorage!(StorageType!SeededAlignment).fromPtrs(
207             firstReadAlignment[0],
208             lastReadAlignment[$],
209         );
210         auto firstSeededAlignment = pileUpDb.readRecordAt!(StorageType!SeededAlignment)(seededAlignments[0]);
211         auto lastSeededAlignment = pileUpDb.readRecordAt!(StorageType!SeededAlignment)(seededAlignments[$ - 1]);
212 
213         auto localAlignments = ArrayStorage!(StorageType!LocalAlignment).fromPtrs(
214             firstSeededAlignment.localAlignments[0],
215             lastSeededAlignment.localAlignments[$],
216         );
217         auto firstLocalAlignment = pileUpDb.readRecordAt!(StorageType!LocalAlignment)(localAlignments[0]);
218         auto lastLocalAlignment = pileUpDb.readRecordAt!(StorageType!LocalAlignment)(localAlignments[$ - 1]);
219 
220         auto tracePoints = ArrayStorage!(StorageType!TracePoint).fromPtrs(
221             firstLocalAlignment.tracePoints[0],
222             lastLocalAlignment.tracePoints[$],
223         );
224 
225         return DbSlices(
226             pileUps,
227             readAlignments,
228             seededAlignments,
229             localAlignments,
230             tracePoints,
231         );
232     }
233 
234     private void parse(ref PileUp[] pileUps, ReadAlignment[] readAlignments)
235     {
236         static assert(PileUp.sizeof == StorageType!PileUp.sizeof);
237         pileUpDb.seek(dbSlices.pileUps.ptr);
238         pileUps = pileUpDb.readRecords(pileUps);
239 
240         size_t[2] pileUpSlice;
241         foreach (ref pileUp; pileUps)
242         {
243             auto pileUpStorage = *cast(StorageType!PileUp*) &pileUp;
244             pileUpSlice[0] = pileUpSlice[1];
245             pileUpSlice[1] += pileUpStorage.length;
246 
247             pileUp = readAlignments[pileUpSlice[0] .. pileUpSlice[1]];
248         }
249     }
250 
251     private void parse(
252         ref SeededAlignment[] seededAlignments,
253         AlignmentChain.LocalAlignment[] localAlignments
254     )
255     {
256         // Parse `SeededAlignment`s
257         alias Contig = AlignmentChain.Contig;
258         pileUpDb.seek(dbSlices.seededAlignments.ptr);
259 
260         size_t[2] seededAlignmentsSlice;
261         foreach (ref seededAlignment; seededAlignments)
262         {
263             auto seededAlignmentStorage = pileUpDb.readRecord!(StorageType!SeededAlignment);
264 
265             seededAlignmentsSlice[0] = seededAlignmentsSlice[1];
266             seededAlignmentsSlice[1] += seededAlignmentStorage.localAlignments.length;
267 
268             seededAlignment = SeededAlignment(
269                 AlignmentChain(
270                     seededAlignmentStorage.id,
271                     Contig(
272                         seededAlignmentStorage.contigAId,
273                         seededAlignmentStorage.contigALength,
274                     ),
275                     Contig(
276                         seededAlignmentStorage.contigBId,
277                         seededAlignmentStorage.contigBLength,
278                     ),
279                     seededAlignmentStorage.flags,
280                     localAlignments[seededAlignmentsSlice[0] .. seededAlignmentsSlice[1]],
281                     seededAlignmentStorage.tracePointDistance,
282                 ),
283                 seededAlignmentStorage.seed,
284             );
285         }
286     }
287 
288     private void parse(ref ReadAlignment[] readAlignments, SeededAlignment[] seededAlignments)
289     {
290         pileUpDb.seek(dbSlices.readAlignments.ptr);
291 
292         size_t[2] readAlignmentsSlice;
293         foreach (ref readAlignment; readAlignments)
294         {
295             auto readAlignmentStorage = pileUpDb.readRecord!(StorageType!ReadAlignment);
296 
297             readAlignmentsSlice[0] = readAlignmentsSlice[1];
298             readAlignmentsSlice[1] += readAlignmentStorage.length;
299 
300             readAlignment = ReadAlignment(
301                 seededAlignments[readAlignmentsSlice[0] .. readAlignmentsSlice[1]]
302             );
303         }
304     }
305 
306     private void parse(
307         ref AlignmentChain.LocalAlignment[] localAlignments,
308         AlignmentChain.LocalAlignment.TracePoint[] tracePoints
309     )
310     {
311         alias LocalAlignment = AlignmentChain.LocalAlignment;
312         alias Locus = LocalAlignment.Locus;
313         pileUpDb.seek(dbSlices.localAlignments.ptr);
314 
315         size_t[2] localAlignmentsSlice;
316         foreach (ref localAlignment; localAlignments)
317         {
318             auto localAlignmentStorage = pileUpDb.readRecord!(StorageType!LocalAlignment);
319 
320             localAlignmentsSlice[0] = localAlignmentsSlice[1];
321             localAlignmentsSlice[1] += localAlignmentStorage.tracePoints.length;
322 
323             localAlignment = LocalAlignment(
324                 Locus(
325                     localAlignmentStorage.contigABegin,
326                     localAlignmentStorage.contigAEnd,
327                 ),
328                 Locus(
329                     localAlignmentStorage.contigBBegin,
330                     localAlignmentStorage.contigBEnd,
331                 ),
332                 localAlignmentStorage.numDiffs,
333                 tracePoints[localAlignmentsSlice[0] .. localAlignmentsSlice[1]],
334             );
335         }
336     }
337 
338     private void parse(ref AlignmentChain.LocalAlignment.TracePoint[] tracePoints)
339     {
340         alias LocalAlignment = AlignmentChain.LocalAlignment;
341         alias TracePoint = LocalAlignment.TracePoint;
342 
343         static assert(TracePoint.sizeof == StorageType!TracePoint.sizeof);
344         pileUpDb.seek(dbSlices.tracePoints.ptr);
345         tracePoints = pileUpDb.readRecords(tracePoints);
346     }
347 }
348 
349 void writePileUpsDb(in PileUp[] pileUps, in string dbFile)
350 {
351     auto pileUpDb = File(dbFile, "wb");
352     lockIfPossible(pileUpDb);
353 
354     writePileUpsDb(pileUps, pileUpDb);
355 }
356 
357 void writePileUpsDb(in PileUp[] pileUps, File pileUpDb)
358 {
359     alias LocalAlignment = AlignmentChain.LocalAlignment;
360     alias TracePoint = LocalAlignment.TracePoint;
361 
362     PileUpDbIndex dbIndex = buildPileUpDbIndex(pileUps);
363 
364     pileUpDb.rawWrite([dbIndex]);
365 
366     writePileUpsDbBlock!PileUp(pileUpDb, pileUps, dbIndex);
367     writePileUpsDbBlock!ReadAlignment(pileUpDb, pileUps, dbIndex);
368     writePileUpsDbBlock!SeededAlignment(pileUpDb, pileUps, dbIndex);
369     writePileUpsDbBlock!LocalAlignment(pileUpDb, pileUps, dbIndex);
370     writePileUpsDbBlock!TracePoint(pileUpDb, pileUps, dbIndex);
371 }
372 
373 unittest
374 {
375     import dentist.util.tempfile : mkstemp;
376     import std.file : remove;
377 
378     alias LocalAlignment = AlignmentChain.LocalAlignment;
379     alias TracePoint = LocalAlignment.TracePoint;
380 
381     auto pileUps = getPileUpsTestData();
382 
383     enum totalDbSize =
384         PileUpDbIndex.sizeof +
385         StorageType!PileUp.sizeof * numPileUps +
386         StorageType!ReadAlignment.sizeof * numReadAlignments +
387         StorageType!SeededAlignment.sizeof * numSeededAlignments +
388         StorageType!LocalAlignment.sizeof * numLocalAlignments +
389         StorageType!TracePoint.sizeof * numTracePoints;
390 
391     auto tmpDb = mkstemp("./.unittest-XXXXXX");
392     scope (exit)
393     {
394         tmpDb.file.close();
395         remove(tmpDb.name);
396     }
397 
398     writePileUpsDb(pileUps, tmpDb.file);
399     tmpDb.file.sync();
400 
401     assert(tmpDb.file.size == totalDbSize);
402 
403     tmpDb.file.rewind();
404     auto pileUpDb = PileUpDb(tmpDb.file);
405 
406     assert(pileUpDb[] == pileUps);
407 }
408 
409 private PileUpDbIndex buildPileUpDbIndex(in PileUp[] pileUps) nothrow pure
410 {
411     alias LocalAlignment = AlignmentChain.LocalAlignment;
412     alias TracePoint = AlignmentChain.LocalAlignment.TracePoint;
413 
414     PileUpDbIndex dbIndex;
415 
416     dbIndex.beginPtr!PileUp = PileUpDbIndex.sizeof;
417     dbIndex.endPtr!PileUp = StorageType!PileUp.sizeof * pileUps.length;
418     foreach (ref pileUp; pileUps)
419     {
420         dbIndex.endPtr!ReadAlignment += StorageType!ReadAlignment.sizeof * pileUp.length;
421         foreach (ref readAlignment; pileUp)
422         {
423             dbIndex.endPtr!SeededAlignment += StorageType!SeededAlignment.sizeof * readAlignment.length;
424             foreach (ref seededAlignment; readAlignment[])
425             {
426                 dbIndex.endPtr!LocalAlignment += StorageType!LocalAlignment.sizeof * seededAlignment.localAlignments.length;
427                 foreach (ref localAlignment; seededAlignment.localAlignments)
428                 {
429                     dbIndex.endPtr!TracePoint += StorageType!TracePoint.sizeof * localAlignment.tracePoints.length;
430                 }
431             }
432         }
433     }
434 
435     dbIndex.readAlignmentsPtr += dbIndex.pileUpsPtr;
436     dbIndex.seededAlignmentsPtr += dbIndex.readAlignmentsPtr;
437     dbIndex.localAlignmentsPtr += dbIndex.seededAlignmentsPtr;
438     dbIndex.tracePointsPtr += dbIndex.localAlignmentsPtr;
439     dbIndex.eofPtr += dbIndex.tracePointsPtr;
440 
441     return dbIndex;
442 }
443 
444 unittest
445 {
446     alias LocalAlignment = AlignmentChain.LocalAlignment;
447     alias TracePoint = AlignmentChain.LocalAlignment.TracePoint;
448 
449     auto pileUps = getPileUpsTestData();
450     auto dbIndex = buildPileUpDbIndex(pileUps);
451 
452     assert(dbIndex.pileUpsPtr == PileUpDbIndex.sizeof);
453     assert(dbIndex.readAlignmentsPtr ==
454             dbIndex.pileUpsPtr +
455             StorageType!PileUp.sizeof * numPileUps);
456     assert(dbIndex.seededAlignmentsPtr ==
457             dbIndex.readAlignmentsPtr +
458             StorageType!ReadAlignment.sizeof * numReadAlignments);
459     assert(dbIndex.localAlignmentsPtr ==
460             dbIndex.seededAlignmentsPtr +
461             StorageType!SeededAlignment.sizeof * numSeededAlignments);
462     assert(dbIndex.tracePointsPtr ==
463             dbIndex.localAlignmentsPtr +
464             StorageType!LocalAlignment.sizeof * numLocalAlignments);
465     assert(dbIndex.eofPtr ==
466             dbIndex.tracePointsPtr +
467             StorageType!TracePoint.sizeof * numTracePoints);
468 }
469 
470 void writePileUpsDbBlock(T)(ref File pileUpDb, in PileUp[] pileUps, in PileUpDbIndex dbIndex)
471     if (is(T == PileUp))
472 {
473     auto readAlignments = ArrayStorage!(StorageType!ReadAlignment)(dbIndex.beginPtr!ReadAlignment);
474 
475     version (assert)
476     {
477         auto storedPileUps = ArrayStorage!(StorageType!PileUp)(dbIndex.beginPtr!PileUp);
478         assert(storedPileUps.ptr == pileUpDb.tell());
479     }
480     foreach (ref pileUp; pileUps)
481     {
482         readAlignments.length = pileUp.length;
483 
484         pileUpDb.rawWrite([readAlignments]);
485 
486         readAlignments.ptr = readAlignments[$];
487 
488         version (assert)
489         {
490             ++storedPileUps.length;
491             assert(storedPileUps[$] == pileUpDb.tell());
492         }
493     }
494 }
495 
496 void writePileUpsDbBlock(T)(ref File pileUpDb, in PileUp[] pileUps, in PileUpDbIndex dbIndex)
497     if (is(T == ReadAlignment))
498 {
499     auto seededAlignments = ArrayStorage!(StorageType!SeededAlignment)(dbIndex.beginPtr!SeededAlignment);
500 
501     version (assert)
502     {
503         auto readAlignments = ArrayStorage!(StorageType!ReadAlignment)(dbIndex.beginPtr!ReadAlignment);
504         assert(readAlignments.ptr == pileUpDb.tell());
505     }
506     foreach (ref pileUp; pileUps)
507     {
508         foreach (ref readAlignment; pileUp)
509         {
510             seededAlignments.length = readAlignment.length;
511 
512             pileUpDb.rawWrite([seededAlignments]);
513 
514             seededAlignments.ptr = seededAlignments[$];
515 
516             version (assert)
517             {
518                 ++readAlignments.length;
519                 assert(readAlignments[$] == pileUpDb.tell());
520             }
521         }
522     }
523 }
524 
525 void writePileUpsDbBlock(T)(ref File pileUpDb, in PileUp[] pileUps, in PileUpDbIndex dbIndex)
526     if (is(T == SeededAlignment))
527 {
528     alias LocalAlignment = AlignmentChain.LocalAlignment;
529 
530     auto localAlignments = ArrayStorage!(StorageType!LocalAlignment)(dbIndex.beginPtr!LocalAlignment);
531 
532     version (assert)
533     {
534         auto seededAlignments = ArrayStorage!(StorageType!SeededAlignment)(dbIndex.beginPtr!SeededAlignment);
535         assert(seededAlignments.ptr == pileUpDb.tell());
536     }
537     foreach (ref pileUp; pileUps)
538     {
539         foreach (ref readAlignment; pileUp)
540         {
541             foreach (ref seededAlignment; readAlignment[])
542             {
543                 localAlignments.length = seededAlignment.localAlignments.length;
544 
545                 pileUpDb.rawWrite([StorageType!SeededAlignment(
546                     seededAlignment.id,
547                     seededAlignment.contigA.id,
548                     seededAlignment.contigA.length,
549                     seededAlignment.contigB.id,
550                     seededAlignment.contigB.length,
551                     seededAlignment.flags,
552                     localAlignments,
553                     seededAlignment.tracePointDistance,
554                     seededAlignment.seed,
555                 )]);
556 
557                 localAlignments.ptr = localAlignments[$];
558 
559                 version (assert)
560                 {
561                     ++seededAlignments.length;
562                   assert(seededAlignments[$] == pileUpDb.tell());
563               }
564             }
565         }
566     }
567 }
568 
569 void writePileUpsDbBlock(T)(ref File pileUpDb, in PileUp[] pileUps, in PileUpDbIndex dbIndex)
570     if (is(T == AlignmentChain.LocalAlignment))
571 {
572     alias LocalAlignment = AlignmentChain.LocalAlignment;
573     alias TracePoint = LocalAlignment.TracePoint;
574 
575     auto tracePoints = ArrayStorage!(StorageType!TracePoint)(dbIndex.beginPtr!TracePoint);
576 
577     version (assert)
578     {
579         auto localAlignments = ArrayStorage!(StorageType!LocalAlignment)(dbIndex.beginPtr!LocalAlignment);
580         assert(localAlignments.ptr == pileUpDb.tell());
581     }
582     foreach (ref pileUp; pileUps)
583     {
584         foreach (ref readAlignment; pileUp)
585         {
586             foreach (ref seededAlignment; readAlignment[])
587             {
588                 foreach (ref localAlignment; seededAlignment.localAlignments)
589                 {
590                     tracePoints.length = localAlignment.tracePoints.length;
591 
592                     pileUpDb.rawWrite([StorageType!LocalAlignment(
593                         localAlignment.contigA.begin,
594                         localAlignment.contigA.end,
595                         localAlignment.contigB.begin,
596                         localAlignment.contigB.end,
597                         localAlignment.numDiffs,
598                         tracePoints,
599                     )]);
600 
601                     tracePoints.ptr = tracePoints[$];
602 
603                     version (assert)
604                     {
605                         ++localAlignments.length;
606                         assert(localAlignments[$] == pileUpDb.tell());
607                     }
608                 }
609             }
610         }
611     }
612 }
613 
614 void writePileUpsDbBlock(T)(ref File pileUpDb, in PileUp[] pileUps, in PileUpDbIndex dbIndex)
615     if (is(T == AlignmentChain.LocalAlignment.TracePoint))
616 {
617     alias LocalAlignment = AlignmentChain.LocalAlignment;
618     alias TracePoint = LocalAlignment.TracePoint;
619 
620     version (assert)
621     {
622         auto tracePoints = ArrayStorage!(StorageType!TracePoint)(dbIndex.beginPtr!TracePoint);
623         assert(tracePoints.ptr == pileUpDb.tell());
624     }
625     foreach (ref pileUp; pileUps)
626     {
627         foreach (ref readAlignment; pileUp)
628         {
629             foreach (ref seededAlignment; readAlignment[])
630             {
631                 foreach (ref localAlignment; seededAlignment.localAlignments)
632                 {
633                     pileUpDb.rawWrite(cast(const(StorageType!TracePoint[])) localAlignment.tracePoints);
634 
635                     version (assert)
636                     {
637                         tracePoints.length += localAlignment.tracePoints.length;
638                         assert(tracePoints[$] == pileUpDb.tell());
639                     }
640                 }
641             }
642         }
643     }
644 }
645 
646 private struct PileUpDbIndex
647 {
648     mixin DbIndex;
649 
650     private static template NextType(T)
651     {
652         static if (is(T == PileUp))
653             alias NextType = ReadAlignment;
654         else static if (is(T == ReadAlignment))
655             alias NextType = SeededAlignment;
656         else static if (is(T == SeededAlignment))
657             alias NextType = AlignmentChain.LocalAlignment;
658         else static if (is(T == AlignmentChain.LocalAlignment))
659             alias NextType = AlignmentChain.LocalAlignment.TracePoint;
660         else static if (is(T == AlignmentChain.LocalAlignment.TracePoint))
661             alias NextType = EOF;
662     }
663 
664     private static template fieldPtr(T)
665     {
666         static if (is(T == PileUp))
667             alias fieldPtr = pileUpsPtr;
668         else static if (is(T == ReadAlignment))
669             alias fieldPtr = readAlignmentsPtr;
670         else static if (is(T == SeededAlignment))
671             alias fieldPtr = seededAlignmentsPtr;
672         else static if (is(T == AlignmentChain.LocalAlignment))
673             alias fieldPtr = localAlignmentsPtr;
674         else static if (is(T == AlignmentChain.LocalAlignment.TracePoint))
675             alias fieldPtr = tracePointsPtr;
676         else static if (is(T == EOF))
677             alias fieldPtr = eofPtr;
678     }
679 
680     size_t pileUpsPtr;
681     size_t readAlignmentsPtr;
682     size_t seededAlignmentsPtr;
683     size_t localAlignmentsPtr;
684     size_t tracePointsPtr;
685     size_t eofPtr;
686 
687     @property alias pileUps = arrayStorage!PileUp;
688     @property alias readAlignments = arrayStorage!ReadAlignment;
689     @property alias seededAlignments = arrayStorage!SeededAlignment;
690     @property alias localAlignments = arrayStorage!(AlignmentChain.LocalAlignment);
691     @property alias tracePoints = arrayStorage!(AlignmentChain.LocalAlignment.TracePoint);
692 }
693 
694 unittest
695 {
696     alias LocalAlignment = AlignmentChain.LocalAlignment;
697     alias TracePoint = AlignmentChain.LocalAlignment.TracePoint;
698     enum begin = 1;
699     enum end = 2;
700     enum modified = 3;
701 
702 
703     {
704         PileUpDbIndex dbIndex;
705 
706         dbIndex.pileUpsPtr = begin;
707         dbIndex.readAlignmentsPtr = end;
708 
709         assert(dbIndex.beginPtr!PileUp == begin);
710         assert(dbIndex.endPtr!PileUp == end);
711 
712         dbIndex.beginPtr!PileUp = modified;
713         dbIndex.endPtr!PileUp = modified;
714 
715         assert(dbIndex.pileUpsPtr == modified);
716         assert(dbIndex.readAlignmentsPtr == modified);
717     }
718     {
719         PileUpDbIndex dbIndex;
720 
721         dbIndex.readAlignmentsPtr = begin;
722         dbIndex.seededAlignmentsPtr = end;
723 
724         assert(dbIndex.beginPtr!ReadAlignment == begin);
725         assert(dbIndex.endPtr!ReadAlignment == end);
726 
727         dbIndex.beginPtr!ReadAlignment = modified;
728         dbIndex.endPtr!ReadAlignment = modified;
729 
730         assert(dbIndex.readAlignmentsPtr == modified);
731         assert(dbIndex.seededAlignmentsPtr == modified);
732     }
733     {
734         PileUpDbIndex dbIndex;
735 
736         dbIndex.seededAlignmentsPtr = begin;
737         dbIndex.localAlignmentsPtr = end;
738 
739         assert(dbIndex.beginPtr!SeededAlignment == begin);
740         assert(dbIndex.endPtr!SeededAlignment == end);
741 
742         dbIndex.beginPtr!SeededAlignment = modified;
743         dbIndex.endPtr!SeededAlignment = modified;
744 
745         assert(dbIndex.seededAlignmentsPtr == modified);
746         assert(dbIndex.localAlignmentsPtr == modified);
747     }
748     {
749         PileUpDbIndex dbIndex;
750 
751         dbIndex.localAlignmentsPtr = begin;
752         dbIndex.tracePointsPtr = end;
753 
754         assert(dbIndex.beginPtr!LocalAlignment == begin);
755         assert(dbIndex.endPtr!LocalAlignment == end);
756 
757         dbIndex.beginPtr!LocalAlignment = modified;
758         dbIndex.endPtr!LocalAlignment = modified;
759 
760         assert(dbIndex.localAlignmentsPtr == modified);
761         assert(dbIndex.tracePointsPtr == modified);
762     }
763     {
764         PileUpDbIndex dbIndex;
765 
766         dbIndex.tracePointsPtr = begin;
767         dbIndex.eofPtr = end;
768 
769         assert(dbIndex.beginPtr!TracePoint == begin);
770         assert(dbIndex.endPtr!TracePoint == end);
771 
772         dbIndex.beginPtr!TracePoint = modified;
773         dbIndex.endPtr!TracePoint = modified;
774 
775         assert(dbIndex.tracePointsPtr == modified);
776         assert(dbIndex.eofPtr == modified);
777     }
778 }
779 
780 private template StorageType(T)
781 {
782     static if (is(T == PileUp))
783         alias StorageType = ArrayStorage!(StorageType!ReadAlignment);
784     else static if (is(T == ReadAlignment))
785         alias StorageType = ArrayStorage!(StorageType!SeededAlignment);
786     else static if (is(T == SeededAlignment))
787         alias StorageType = SeededAlignmentStorage;
788     else static if (is(T == AlignmentChain.LocalAlignment[]))
789         alias StorageType = ArrayStorage!(StorageType!(AlignmentChain.LocalAlignment));
790     else static if (is(T == AlignmentChain.LocalAlignment))
791         alias StorageType = LocalAlignmentStorage;
792     else static if (is(T == AlignmentChain.LocalAlignment.TracePoint[]))
793         alias StorageType = ArrayStorage!(StorageType!(AlignmentChain.LocalAlignment.TracePoint));
794     else static if (is(T == AlignmentChain.LocalAlignment.TracePoint))
795         alias StorageType = TracePointStorage;
796 }
797 
798 private struct SeededAlignmentStorage
799 {
800     alias LocalAlignment = AlignmentChain.LocalAlignment;
801 
802     id_t id;
803     id_t contigAId;
804     coord_t contigALength;
805     id_t contigBId;
806     coord_t contigBLength;
807     AlignmentChain.Flags flags;
808     StorageType!(LocalAlignment[]) localAlignments;
809     trace_point_t tracePointDistance;
810     AlignmentLocationSeed seed;
811 }
812 
813 private struct LocalAlignmentStorage
814 {
815     alias TracePoint = AlignmentChain.LocalAlignment.TracePoint;
816 
817     coord_t contigABegin;
818     coord_t contigAEnd;
819     coord_t contigBBegin;
820     coord_t contigBEnd;
821     diff_t numDiffs;
822     StorageType!(TracePoint[]) tracePoints;
823 }
824 
825 private struct TracePointStorage
826 {
827     trace_point_t numDiffs;
828     trace_point_t numBasePairs;
829 }