[med-svn] [pbdagcon] 01/02: Imported Upstream version 0~20151114+git1d12e13+ds

Afif Elghraoui afif-guest at moszumanska.debian.org
Sat Nov 28 20:44:12 UTC 2015


This is an automated email from the git hooks/post-receive script.

afif-guest pushed a commit to branch master
in repository pbdagcon.

commit d20cc2250d8fc5cfe75b33d8d3221d21648c2aea
Author: Afif Elghraoui <afif at ghraoui.name>
Date:   Fri Nov 27 15:40:18 2015 -0800

    Imported Upstream version 0~20151114+git1d12e13+ds
---
 .gitignore                          |   10 +
 .gitmodules                         |    3 +
 .travis.yml                         |   24 +
 Makefile                            |   48 +
 README.md                           |  127 +
 src/cpp/Alignment.cpp               |  263 ++
 src/cpp/Alignment.hpp               |  112 +
 src/cpp/AlnGraphBoost.cpp           |  521 +++
 src/cpp/AlnGraphBoost.hpp           |  182 +
 src/cpp/AlnProvider.hpp             |   85 +
 src/cpp/BlasrM5AlnProvider.cpp      |  133 +
 src/cpp/BlasrM5AlnProvider.hpp      |  107 +
 src/cpp/BoundedBuffer.hpp           |   89 +
 src/cpp/DB.c                        | 1106 ++++++
 src/cpp/DB.h                        |  357 ++
 src/cpp/DazAlnProvider.cpp          |  452 +++
 src/cpp/DazAlnProvider.hpp          |  222 ++
 src/cpp/Makefile                    |   38 +
 src/cpp/ProgramOpts.hpp             |   39 +
 src/cpp/SimpleAligner.cpp           |  103 +
 src/cpp/SimpleAligner.hpp           |   73 +
 src/cpp/align.c                     | 3805 ++++++++++++++++++++
 src/cpp/align.h                     |  331 ++
 src/cpp/boost.mk                    |   18 +
 src/cpp/dazcon.cpp                  |  257 ++
 src/cpp/main.cpp                    |  314 ++
 src/cpp/pbdagcon_wf.sh              |   74 +
 src/cpp/pbi.mk                      |   22 +
 src/cpp/third-party/easylogging++.h | 6663 +++++++++++++++++++++++++++++++++++
 src/filterm4.py                     |   64 +
 src/m4topre.py                      |  208 ++
 src/q-sense.py                      |  170 +
 src/tests/test_aligngraph.py        |  204 ++
 test/cpp/AlignmentTest.cpp          |   78 +
 test/cpp/AlnGraphBoostTest.cpp      |   56 +
 test/cpp/Makefile                   |   77 +
 test/cpp/SimpleAlignerTest.cpp      |   21 +
 test/cpp/TargetHitTest.cpp          |   77 +
 test/cpp/TargetTest.cpp             |   22 +
 test/cpp/basic.m5                   |    2 +
 test/cpp/gtest.mk                   |   10 +
 test/cpp/parsequery.m5              |    1 +
 42 files changed, 16568 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b69e8a4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+*.d
+*.o
+*.log
+*.swp
+*.swo
+pbdagcon
+dazcon
+test_*
+boost_*
+gtest-*
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..ce6830a
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "blasr_libcpp"]
+	path = blasr_libcpp
+	url = https://github.com/PacificBiosciences/blasr_libcpp.git
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..8ce6492
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,24 @@
+language: cpp
+script: 
+  - make init-submodule 
+  - make
+  - make check
+compiler:
+  - gcc
+  - clang
+install:
+- if [ "$CXX" = "g++" ]; then export CXX="g++-4.8" CC="gcc-4.8"; fi
+addons:
+  apt:
+    sources:
+    - ubuntu-toolchain-r-test
+    packages:
+    - libyajl-dev
+    - libxml2-dev
+    - gcc-4.8
+    - g++-4.8
+    - clang
+#   - libxqilla-dev # missing, but not needed?
+notifications:
+  email: false
+sudo: false
diff --git a/Makefile b/Makefile
new file mode 100755
index 0000000..6d11406
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,48 @@
+.PHONY: all clean test init-submodule cpp-github cpp cpp-check cpp-clean
+SHELL = /bin/bash -e
+
+all: cpp-github
+
+clean: cpp-clean
+
+check: cpp-github-check
+
+project: init-submodule cpp-github
+
+init-submodule:
+	$(MAKE) update-submodule
+	$(MAKE) build-submodule
+
+update-submodule:
+	git submodule update --init
+
+build-submodule:
+	$(MAKE) -C blasr_libcpp/pbdata nopbbam=1 mklibconfig
+	#$(MAKE) -C blasr_libcpp/pbdata mklibconfig
+	$(MAKE) -C blasr_libcpp/alignment -f simple.mk nohdf=1
+	$(MAKE) -C blasr_libcpp/pbdata nopbbam=1
+
+submodule-clean:
+	$(RM) -r blasr_libcpp
+	
+# C++ project build directives
+cpp-github:
+	$(MAKE) -C src/cpp BLASR=$(PWD)/blasr_libcpp/alignment PBDATA=$(PWD)/blasr_libcpp/pbdata
+
+cpp-github-check:
+	$(MAKE) -C test/cpp BLASR=$(PWD)/blasr_libcpp/alignment PBDATA=$(PWD)/blasr_libcpp/pbdata
+
+cpp:
+	$(MAKE) -C src/cpp
+
+cpp-check: cpp
+	$(MAKE) -C test/cpp
+
+cpp-clean:
+	$(MAKE) -C src/cpp clean
+	$(MAKE) -C test/cpp clean
+
+clean-all: cpp-clean submodule-clean
+	$(RM)r src/cpp/third-party/boost_1_58_0-headersonly
+	$(RM)r test/cpp/gtest-1.7.0
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5b858e6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,127 @@
+[![Build Status](https://travis-ci.org/pbjd/pbdagcon.svg?branch=master)](https://travis-ci.org/pbjd/pbdagcon)
+What is pbdagcon?
+=================
+
+pbdagcon is a tool that implements DAGCon (Directed Acyclic Graph Consensus)
+which is a sequence consensus algorithm based on using directed acyclic graphs
+to encode multiple sequence alignment.
+
+It uses the alignment information from blasr to align sequence reads to a
+"backbone" sequence. Based on the underlying alignment directed acyclic graph
+(DAG), it will be able to use the new information from the reads to find the
+discrepancies between the reads and the "backbone" sequences.  A dynamic
+programming process is then applied to the DAG to find the optimum sequence of
+bases as the consensus.  The new consensus can be used as a new backbone
+sequence to iteratively improve the consensus quality.
+
+While the code is developed for processing PacBio(TM) raw sequence data, the
+algorithm can be used for general consensus purpose. Currently, it only takes
+FASTA input. For shorter read sequences, one might need to adjust the blasr
+alignment parameters to get the alignment string properly.
+
+The code and the underlying graphical data structure have been used for some
+algorithm development prototyping including phasing reads, pre-assembly and a
+work around to generate consensus from intermediate Celera Assembler outputs.
+
+The initial graphical algorithm was a pure python implementation. Cython was
+then use to speed it up.
+
+Check out the example/ directory to see how to use it. 
+
+This code is released under the assumption it will help the community to adopt
+the PacBio data and make interesting science project possible and more
+feasible.  It is not an official software release from the PacBio(TM) software
+developing organization.
+
+Building
+========
+The following are instructions on how to build the C++ pbdagcon executable. 
+The code now depends on C++11 features, in particular std::thread, std::move. 
+GCC 4.8.1 or higher is known to work.
+
+This project requires that you have boost headers available.  You can either 
+supply them yourself or the Makefile will obtain them for you from the 
+internet.
+
+### Compile/Check (pbdagcon)
+```sh
+    # First fetch and build the relevant portions of the blasr_libcpp
+    # submodule
+    make init-submodule
+
+    # build pbdagcon executable (Makefile fetches boost headers)
+    make
+    # or, if you already have boost headers
+    make boost=<path to headers>
+
+    # build and run unit tests
+    # THIS IS CURRENTLY BROKEN. -cdunn
+    ###make check
+
+    # usage 
+    cd src/cpp
+    ./pbdagcon --help
+```
+
+Running
+=======
+
+### Use Case: Generating consensus from BLASR alignments
+The most basic use case where one can generate a consensus from a set of 
+alignments using the pbdagcon executable directly.
+
+At the most basic level, pbdagcon takes information from BLASR alignments 
+sorted by target and generates fasta-formatted corrected target sequences.
+The alignments from BLASR can be formatted with either *-m 4* or *-m 5*. 
+For *-m 4* format, the alignments must be run through a format adapter, 
+*[m4topre.py][]*, in order to generate suitable input to *pbdagcon*.
+
+The following example shows the simplest way to generate a consensus for one 
+target using BLASR *-m 5* alignments as input.
+
+    blasr queries.fasta target.fasta -bestn 1 -m 5 -out mapped.m5
+    pbdagcon mapped.m5 > consensus.fasta
+
+### Use Case: Generating consensus from daligner alignments
+Can parse LAS/DB files generated from the following commits:
+* [DALIGNER](https://github.com/thegenemyers/DALIGNER/commit/8edd180ba7b5302c6f1fc859eef5c646db99fd87)
+* [DAZ_DB](https://github.com/thegenemyers/DAZZ_DB/commit/84fa98fde94ba0ab56dd715aa7f8fe7e150290f8)
+
+
+### Use Case: HGAP correction of PacBio reads
+Walks through how one could use pbdagcon to correct PacBio reads.  This 
+example demonstrates how correction is performed in PacBio's "Hierarchichal  
+Genome Assembly Process" (HGAP) workflow.  HGAP uses BLASR *-m 4* output.
+
+This example makes use of the *[filterm4.py][]* and *[m4topre.py][]* scripts:
+```sh
+    # First filter the m4 file to help remove chimeras
+    filterm4.py mapped.m4 > mapped.m4.filt
+
+    # Next run the m4 adapter script, generating 'pre-alignments'
+    m4topre.py mapped.m4.filt mapped.m4.filt reads.fasta 24 > mapped.pre
+
+    # Finally, correct using pbdagcon, typically using multiple consensus  
+    # threads.
+    pbdagcon -j 4 -a mapped.pre > corrected.fasta
+```
+
+The *[pbdagcon_wf.sh][]* script automates this workflow.
+
+-----------------------------------------------------------------------------
+
+<script>
+(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+ga('create', 'UA-13166584-17', 'github.com');
+ga('send', 'pageview');
+</script>
+
+[m4topre.py]:
+  src/m4topre.py 'code'
+[filterm4.py]:
+  src/filterm4.py 'code'
+[pbdagcon_wf.sh]:
+  src/cpp/pbdagcon_wf.sh 'code'
diff --git a/src/cpp/Alignment.cpp b/src/cpp/Alignment.cpp
new file mode 100644
index 0000000..c039254
--- /dev/null
+++ b/src/cpp/Alignment.cpp
@@ -0,0 +1,263 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <list>
+#include <vector>
+#include <cassert>
+#include "Alignment.hpp"
+
+using namespace dagcon;
+
+///
+/// Simple method to reverse complement a sequence.
+///
+std::string revComp(std::string& seq) {
+    const std::string bases = "ACTG";
+    std::string::iterator curr = seq.begin();
+    for (; curr != seq.end(); ++curr) {
+        char& c = *curr;
+        c = c == 'T' ? bases[0] : 
+            c == 'G' ? bases[1] :
+            c == 'A' ? bases[2] : 
+            c == 'C' ? bases[3] : c;
+    }
+    return std::string(seq.rbegin(), seq.rend());
+}
+
+// Set this to false if the alignments are grouped by query.  The parse
+// routine will be adjusted to build the alignment graph based on the 
+// queries. 
+bool Alignment::groupByTarget = true;
+
+Alignment::Alignment() : 
+    tlen(0), 
+    start(0), 
+    end(0), 
+    id(""), 
+    sid(""), 
+    strand('+'), 
+    qstr(""), 
+    tstr("") { }
+
+// Parses blasr m5 output grouped either by target or query.
+void parseM5(std::istream& stream, Alignment* aln) {
+    std::string line;
+    std::getline(stream, line);
+    std::stringstream row(line);
+    std::string col;
+    std::vector<std::string> fields;
+    while(std::getline(row, col, ' ')) {
+        if (col == "") continue;
+        fields.push_back(col);
+    }
+
+    // avoids *some* empty lines
+    if (fields.size() == 0) return;
+
+    // base query id (without the last '/<coordinates>'), allows us to 
+    // group properly by query when asked.
+    std::string baseQid = fields[0].substr(0,fields[0].find_last_of("/"));
+    aln->sid = fields[0];
+    aln->id = Alignment::groupByTarget ? fields[5] : baseQid; 
+
+    std::istringstream ssLen(Alignment::groupByTarget ? fields[6] : fields[1]);
+    ssLen >> aln->tlen;
+    std::istringstream ssStart(Alignment::groupByTarget ? fields[7] : fields[2]);
+    ssStart >> aln->start;
+    aln->start++;
+
+    // the target is always reversed.
+    aln->strand = fields[9][0];
+    if (aln->strand == '-' && Alignment::groupByTarget) {
+        // only need to reverse complement when correcting targets
+        aln->qstr = revComp(fields[16]);
+        aln->tstr = revComp(fields[18]);
+    } else {
+        aln->qstr = Alignment::groupByTarget ? fields[16] : fields[18];
+        aln->tstr = Alignment::groupByTarget ? fields[18] : fields[16];
+    }
+}
+
+void parsePre(std::istream& stream, Alignment* aln) {
+    std::string line;
+    std::getline(stream, line);
+    std::stringstream row(line);
+    std::string col;
+    std::vector<std::string> fields;
+    while(std::getline(row, col, ' ')) {
+        if (col == "") continue;
+        fields.push_back(col);
+    }
+
+    // avoids *some* empty lines
+    if (fields.size() == 0) return;
+
+    // qid, tid, strand, tlen, tstart, tend, qstr, tstr
+    aln->sid = fields[0];
+    aln->id = fields[1];
+    aln->strand = fields[2][0];
+
+    std::istringstream ssLen(fields[3]);
+    ssLen >> aln->tlen;
+
+    std::istringstream ssStart(fields[4]);
+    ssStart >> aln->start;
+
+    std::istringstream ssEnd(fields[5]);
+    ssEnd >> aln->end;
+
+    aln->qstr = fields[6];
+    aln->tstr = fields[7];
+}
+
+// default to parsing m5
+Alignment::ParseFunc Alignment::parse = parseM5;
+
+std::istream& operator>>(std::istream& instrm, Alignment& data) {
+    Alignment::parse(instrm, &data);
+    return instrm;
+}
+
+std::ostream& operator<<(std::ostream& ostrm, Alignment& data) {
+    ostrm << "target: " << data.id << ", query: " << data.sid;
+    ostrm << ", start: " << data.start << ", end: " << data.end;
+    ostrm << ", length: " << data.tlen <<  std::endl;
+    ostrm << "tstr(50): " << data.tstr.substr(0,50) << std::endl;
+    ostrm << "qstr(50): " << data.qstr.substr(0,50) << std::endl;
+    return ostrm;
+}
+
+Alignment normalizeGaps(Alignment& aln, bool push) {
+    // XXX: optimize this
+    size_t qlen = aln.qstr.length(), tlen = aln.tstr.length();
+    assert(qlen == tlen);
+    std::string qNorm, tNorm;
+    qNorm.reserve(qlen+100);
+    tNorm.reserve(tlen+100);
+
+    // convert mismatches to indels
+    for (size_t i=0; i < qlen; i++) {
+        char qb = aln.qstr[i], tb = aln.tstr[i];
+        if (qb != tb && qb != '-' && tb != '-') {
+            qNorm += '-';
+            qNorm += qb;
+            tNorm += tb;
+            tNorm += '-';
+        } else {
+            qNorm += qb;
+            tNorm += tb;
+        }
+    }
+
+    // update lengths
+    qlen = qNorm.length();
+    tlen = tNorm.length();
+
+    if (push) {
+        // push gaps to the right, but not past the end
+        for (size_t i=0; i < qlen-1; i++) {
+            // pushing target gaps
+            if (tNorm[i] == '-') {
+                size_t j = i;
+                while (true) {
+                    char c = tNorm[++j];
+                    if (c != '-' || j > qlen - 1) {
+                        if (c == qNorm[i]) {
+                            tNorm[i] = c;
+                            tNorm[j] = '-';
+                        }
+                        break;
+                    }
+                }
+            }
+
+            // pushing query gaps
+            if (qNorm[i] == '-') {
+                size_t j = i;
+                while (true) {
+                    char c = qNorm[++j];
+                    if (c != '-' || j > tlen - 1) {
+                        if (c == tNorm[i]) {
+                            qNorm[i] = c;
+                            qNorm[j] = '-';
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    // generate the final, normalized alignment strings
+    Alignment finalNorm;
+    finalNorm.id = aln.id;
+    finalNorm.sid = aln.sid;
+    finalNorm.start = aln.start;
+    finalNorm.tlen = aln.tlen;
+    finalNorm.strand = aln.strand;
+    for (size_t i=0; i < qlen; i++) {
+        if (qNorm[i] != '-' || tNorm[i] != '-') {
+            finalNorm.qstr += qNorm[i];
+            finalNorm.tstr += tNorm[i];
+        }
+    }
+
+    return finalNorm;
+}
+
+void trimAln(Alignment& aln, int trimLen) {
+    int lbases = 0, loffs = 0;
+    while(lbases < trimLen) {
+        if (aln.tstr[loffs++] != '-') {
+            lbases++;
+        }
+    }
+
+    int rbases = 0, roffs = aln.tstr.length();
+    while (rbases < trimLen) {
+        if (aln.tstr[roffs--] != '-') {
+            rbases++;
+        }
+    }
+
+    aln.start += lbases;
+    aln.qstr = aln.qstr.substr(loffs, roffs - loffs);
+    aln.tstr = aln.tstr.substr(loffs, roffs - loffs);
+}
diff --git a/src/cpp/Alignment.hpp b/src/cpp/Alignment.hpp
new file mode 100644
index 0000000..ff1ae71
--- /dev/null
+++ b/src/cpp/Alignment.hpp
@@ -0,0 +1,112 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#ifndef __GCON_ALIGNMENT_HPP__
+#define __GCON_ALIGNMENT_HPP__
+
+#include <stdint.h>
+
+/// 
+/// Super-simple alignment representation.  Represents an alignment between two
+/// PacBio reads, one of which we're trying to correct.  The read to correct
+/// may be either the target or the query, depending on how the alignment was
+/// done.
+///
+namespace dagcon {
+class Alignment {
+public:
+    typedef void (*ParseFunc)(std::istream&, Alignment* aln);
+
+    // May correct the target or the query, default is target
+    static bool groupByTarget;
+
+    // length of the sequence we are trying to correct
+    uint32_t tlen;
+    
+    // conforming offsets are 1-based
+    uint32_t start;
+
+    uint32_t end;
+
+    // ID of the read we're trying to correct (target)
+    std::string id;
+
+    // ID of the supporting read (query)
+    std::string sid; 
+
+    char strand;
+
+    // query and target strings must be equal length
+    std::string qstr;
+    std::string tstr;
+
+    Alignment();
+
+    static ParseFunc parse;
+};
+}
+
+std::istream& operator>>(std::istream& instrm, dagcon::Alignment& data);
+std::ostream& operator<<(std::ostream& ostrm, dagcon::Alignment& data);
+
+void parseM5(std::istream& stream, dagcon::Alignment* aln);
+
+void parsePre(std::istream& stream, dagcon::Alignment* aln);
+
+/// Simplifies the alignment by normalizing gaps.  Converts mismatches into
+/// indels ... 
+///      query: CAC        query:  C-AC
+///             | |  --->          |  |
+///     target: CGC       target:  CG-C
+///
+/// Shifts equivalent gaps to the right in the reference ...
+///      query: CAACAT        query: CAACAT
+///             | | ||  --->         |||  |
+///     target: C-A-AT       target: CAA--T
+///
+/// Shifts equivalent gaps to the right in the read ...
+///      query: -C--CGT       query: CCG--T
+///              |  | |  --->        |||  |
+///     target: CCGAC-T      target: CCGACT  
+/// Allow optional gap pushing, some aligners may not need it and I'd like 
+/// to get rid of it anyway.
+dagcon::Alignment normalizeGaps(dagcon::Alignment& aln, bool push=true);
+
+void trimAln(dagcon::Alignment& aln, int trimLen=50);
+
+std::string revComp(std::string& seq); 
+
+#endif // __GCON_ALIGNMENT_HPP__
diff --git a/src/cpp/AlnGraphBoost.cpp b/src/cpp/AlnGraphBoost.cpp
new file mode 100644
index 0000000..79ded57
--- /dev/null
+++ b/src/cpp/AlnGraphBoost.cpp
@@ -0,0 +1,521 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#include <cstdint>
+#include <cfloat>
+#include <cassert>
+#include <string>
+#include <queue>
+#include <map>
+#include <vector>
+#include <boost/format.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/graphviz.hpp>
+#include <boost/utility/value_init.hpp>
+#include "Alignment.hpp"
+#include "AlnGraphBoost.hpp"
+
+AlnGraphBoost::AlnGraphBoost(const std::string& backbone) {
+    // initialize the graph structure with the backbone length + enter/exit
+    // vertex
+    size_t blen = backbone.length();
+    _g = G(blen+1);
+    for (size_t i = 0; i < blen+1; i++) 
+        boost::add_edge(i, i+1, _g); 
+
+    VtxIter curr, last;
+    boost::tie(curr, last) = boost::vertices(_g);
+    _enterVtx = *curr++;
+    _g[_enterVtx].base = '^';
+    _g[_enterVtx].backbone = true;
+    for (size_t i = 0; i < blen; i++, ++curr) {
+        VtxDesc v = *curr;
+        _g[v].backbone = true;
+        _g[v].weight = 1;
+        _g[v].base = backbone[i];
+        _bbMap[v] = v;
+    }
+    _exitVtx = *curr;
+    _g[_exitVtx].base = '$';
+    _g[_exitVtx].backbone = true;
+}
+
+AlnGraphBoost::AlnGraphBoost(const size_t blen) {
+    _g = G(blen+1);
+    for (size_t i = 0; i < blen+1; i++) 
+        boost::add_edge(i, i+1, _g); 
+
+    VtxIter curr, last;
+    boost::tie(curr, last) = boost::vertices(_g);
+    _enterVtx = *curr++;
+    _g[_enterVtx].base = '^';
+    _g[_enterVtx].backbone = true;
+    for (size_t i = 0; i < blen; i++, ++curr) {
+        VtxDesc v = *curr;
+        _g[v].backbone = true;
+        _g[v].weight = 1;
+        _g[v].deleted = false;
+        _g[v].base = 'N';
+        _bbMap[v] = v;
+    }
+    _exitVtx = *curr;
+    _g[_exitVtx].base = '$';
+    _g[_exitVtx].backbone = true;
+}
+
+void AlnGraphBoost::addAln(dagcon::Alignment& aln) {
+    IndexMap index = boost::get(boost::vertex_index, _g);
+    // tracks the position on the backbone
+    uint32_t bbPos = aln.start;
+    VtxDesc prevVtx = _enterVtx;
+    for (size_t i = 0; i < aln.qstr.length(); i++) {
+        char queryBase = aln.qstr[i], targetBase = aln.tstr[i];
+        VtxDesc currVtx = index[bbPos];
+        // match
+        if (queryBase == targetBase) {
+            _g[_bbMap[currVtx]].coverage++;
+
+            // NOTE: for empty backbones
+            _g[_bbMap[currVtx]].base = targetBase;
+
+            _g[currVtx].weight++;
+            addEdge(prevVtx, currVtx);
+            bbPos++;
+            prevVtx = currVtx;
+        // query deletion
+        } else if (queryBase == '-' && targetBase != '-') {
+            _g[_bbMap[currVtx]].coverage++;
+
+            // NOTE: for empty backbones
+            _g[_bbMap[currVtx]].base = targetBase;
+
+            bbPos++;
+        // query insertion
+        } else if (queryBase != '-' && targetBase == '-') {
+            // create new node and edge
+            VtxDesc newVtx = boost::add_vertex(_g);
+            _g[newVtx].base = queryBase;
+            _g[newVtx].weight++;
+            _g[newVtx].backbone = false;
+            _g[newVtx].deleted = false;
+            _bbMap[newVtx] = bbPos;
+            addEdge(prevVtx, newVtx);
+            prevVtx = newVtx;
+        }
+    }
+    addEdge(prevVtx, _exitVtx);
+}
+
+void AlnGraphBoost::addEdge(VtxDesc u, VtxDesc v) {
+    // Check if edge exists with prev node.  If it does, increment edge counter,
+    // otherwise add a new edge.
+    InEdgeIter ii, ie;
+    bool edgeExists = false;
+    for (boost::tie(ii, ie) = boost::in_edges(v, _g); ii != ie; ++ii) {
+        EdgeDesc e = *ii;
+        if (boost::source(e , _g) == u) {
+            // increment edge count
+            _g[e].count++;
+            edgeExists = true;
+        }
+    }
+    if (! edgeExists) {
+        // add new edge
+        std::pair<EdgeDesc, bool> p = boost::add_edge(u, v, _g);
+        _g[p.first].count++;
+    }
+}
+
+void AlnGraphBoost::mergeNodes() {
+    std::queue<VtxDesc> seedNodes;
+    seedNodes.push(_enterVtx); 
+
+    while(true) {
+        if (seedNodes.size() == 0)
+            break;
+
+        VtxDesc u = seedNodes.front();
+        seedNodes.pop();
+        mergeInNodes(u);
+        mergeOutNodes(u);
+
+        OutEdgeIter oi, oe;
+        for (boost::tie(oi, oe) = boost::out_edges(u, _g); oi != oe; ++oi) {
+            EdgeDesc e = *oi;
+            _g[e].visited = true;
+            VtxDesc v = boost::target(e, _g);
+            InEdgeIter ii, ie;
+            int notVisited = 0;
+            for (boost::tie(ii, ie) = boost::in_edges(v, _g); ii != ie; ++ii) {
+                if (_g[*ii].visited == false)
+                    notVisited++;
+            }
+            
+            // move onto the boost::target node after we visit all incoming edges for
+            // the boost::target node
+            if (notVisited == 0)
+                seedNodes.push(v);
+        }
+    }
+}
+
+void AlnGraphBoost::mergeInNodes(VtxDesc n) {
+    std::map<char, std::vector<VtxDesc>> nodeGroups;     
+    InEdgeIter ii, ie;
+    // Group neighboring nodes by base
+    for(boost::tie(ii, ie) = boost::in_edges(n, _g); ii != ie; ++ii) {
+        VtxDesc inNode = boost::source(*ii, _g);
+        if (out_degree(inNode, _g) == 1) {
+            nodeGroups[_g[inNode].base].push_back(inNode);
+        }
+    }
+    
+    // iterate over node groups, merge an accumulate information
+    for(auto kvp = nodeGroups.cbegin(); kvp != nodeGroups.end(); ++kvp) {
+        std::vector<VtxDesc> nodes = (*kvp).second;
+        if (nodes.size() <= 1) 
+            continue;
+
+        std::vector<VtxDesc>::const_iterator ni = nodes.cbegin();
+        VtxDesc an = *ni++; 
+        OutEdgeIter anoi, anoe;
+        boost::tie(anoi, anoe) = boost::out_edges(an, _g);
+        
+        // Accumulate out edge information
+        for (; ni != nodes.cend(); ++ni) {
+            OutEdgeIter oi, oe;
+            boost::tie(oi, oe) = boost::out_edges(*ni, _g);
+            _g[*anoi].count += _g[*oi].count;    
+            _g[an].weight += _g[*ni].weight;
+        }
+
+        // Accumulate in edge information, merges nodes
+        ni = nodes.cbegin();
+        ++ni;
+        for (; ni != nodes.cend(); ++ni) {
+            InEdgeIter ii, ie;
+            VtxDesc n = *ni;
+            for (boost::tie(ii, ie) = boost::in_edges(n, _g); ii != ie; ++ii) {
+                VtxDesc n1 = boost::source(*ii, _g);
+                EdgeDesc e;
+                bool exists;
+                boost::tie(e, exists) = edge(n1, an, _g);
+                if (exists) {
+                    _g[e].count += _g[*ii].count;
+                } else {
+                    std::pair<EdgeDesc, bool> p = boost::add_edge(n1, an, _g);
+                    _g[p.first].count = _g[*ii].count;
+                    _g[p.first].visited = _g[*ii].visited;
+                }
+            }
+            markForReaper(n);
+        }
+        mergeInNodes(an);
+    }
+}
+
+void AlnGraphBoost::mergeOutNodes(VtxDesc n) {
+    std::map<char, std::vector<VtxDesc>> nodeGroups;     
+    OutEdgeIter oi, oe;
+    for(boost::tie(oi, oe) = boost::out_edges(n, _g); oi != oe; ++oi) {
+        VtxDesc outNode = boost::target(*oi, _g);
+        if (in_degree(outNode, _g) == 1) {
+            nodeGroups[_g[outNode].base].push_back(outNode);
+        }
+    }
+    
+    for(auto kvp = nodeGroups.cbegin(); kvp != nodeGroups.end(); ++kvp) {
+        std::vector<VtxDesc> nodes = (*kvp).second;
+        if (nodes.size() <= 1) 
+            continue;
+
+        std::vector<VtxDesc>::const_iterator ni = nodes.cbegin();
+        VtxDesc an = *ni++;
+        InEdgeIter anii, anie;
+        boost::tie(anii, anie) = boost::in_edges(an, _g);
+        
+        // Accumulate inner edge information
+        for (; ni != nodes.cend(); ++ni) {
+            InEdgeIter ii, ie;
+            boost::tie(ii, ie) = boost::in_edges(*ni, _g);
+            _g[*anii].count += _g[*ii].count;    
+            _g[an].weight += _g[*ni].weight;
+        }
+
+        // Accumulate and merge outer edge information
+        ni = nodes.cbegin();
+        ++ni;
+        for (; ni != nodes.cend(); ++ni) {
+            OutEdgeIter oi, oe;
+            VtxDesc n = *ni;
+            for (boost::tie(oi, oe) = boost::out_edges(n, _g); oi != oe; ++oi) {
+                VtxDesc n2 = boost::target(*oi, _g);
+                EdgeDesc e;
+                bool exists;
+                boost::tie(e, exists) = edge(an, n2, _g);
+                if (exists) {
+                    _g[e].count += _g[*oi].count;
+                } else {
+                    std::pair<EdgeDesc, bool> p = boost::add_edge(an, n2, _g);
+                    _g[p.first].count = _g[*oi].count;
+                    _g[p.first].visited = _g[*oi].visited;
+                }
+            }
+            markForReaper(n);
+        }
+    }
+}
+
+void AlnGraphBoost::markForReaper(VtxDesc n) {
+    _g[n].deleted = true;
+    clear_vertex(n, _g);
+    _reaperBag.push_back(n);
+}
+
+void AlnGraphBoost::reapNodes() {
+    int reapCount = 0;
+    std::sort(_reaperBag.begin(), _reaperBag.end());
+    std::vector<VtxDesc>::iterator curr = _reaperBag.begin();
+    for (; curr != _reaperBag.end(); ++curr) {
+        assert(_g[*curr].backbone==false);
+        remove_vertex(*curr-reapCount++, _g);
+    }
+}
+
+const std::string AlnGraphBoost::consensus(int minWeight) {
+    // get the best scoring path
+    std::vector<AlnNode> path = bestPath();
+
+    // consensus sequence
+    std::string cns;
+
+    // track the longest consensus path meeting minimum weight
+    int offs = 0, bestOffs = 0, length = 0, idx = 0;
+    bool metWeight = false;
+    std::vector<AlnNode>::iterator curr = path.begin();
+    for (; curr != path.end(); ++curr) {
+        AlnNode n = *curr;
+        if (n.base == _g[_enterVtx].base || n.base == _g[_exitVtx].base) 
+            continue;
+        
+        cns += n.base;
+
+        // initial beginning of minimum weight section
+        if (!metWeight && n.weight >= minWeight) {
+            offs = idx;
+            metWeight = true;
+        } else if (metWeight && n.weight < minWeight) {
+        // concluded minimum weight section, update if longest seen so far
+            if ((idx - offs) > length) { 
+                bestOffs = offs;
+                length = idx - offs;
+            }
+            metWeight = false;
+        }
+        idx++;
+    }
+
+    // include end of sequence
+    if (metWeight && (idx - offs) > length) {
+        bestOffs = offs;
+        length = idx - offs;
+    }
+    
+    return cns.substr(bestOffs, length);
+}
+
+void AlnGraphBoost::consensus(std::vector<CnsResult>& seqs, int minWeight, size_t minLen) {
+    seqs.clear();
+
+    // get the best scoring path
+    std::vector<AlnNode> path = bestPath();
+
+    // consensus sequence
+    std::string cns;
+
+    // track the longest consensus path meeting minimum weight
+    int offs = 0, idx = 0;
+    bool metWeight = false;
+    std::vector<AlnNode>::iterator curr = path.begin();
+    for (; curr != path.end(); ++curr) {
+        AlnNode n = *curr;
+        if (n.base == _g[_enterVtx].base || n.base == _g[_exitVtx].base) 
+            continue;
+        
+        cns += n.base;
+
+        // initial beginning of minimum weight section
+        if (!metWeight && n.weight >= minWeight) {
+            offs = idx;
+            metWeight = true;
+        } else if (metWeight && n.weight < minWeight) {
+        // concluded minimum weight section, add sequence to supplied vector
+            metWeight = false;
+            CnsResult result;
+            result.range[0] = offs;
+            result.range[1] = idx;
+            size_t length = idx - offs;
+            result.seq = cns.substr(offs, length);
+            if (length >= minLen) seqs.push_back(result);
+        }
+        idx++;
+    }
+
+    // include end of sequence
+    if (metWeight) {
+        size_t length = idx - offs;
+        CnsResult result;
+        result.range[0] = offs;
+        result.range[1] = idx;
+        result.seq = cns.substr(offs, length);
+        if (length >= minLen) seqs.push_back(result);
+    }
+}
+
+const std::vector<AlnNode> AlnGraphBoost::bestPath() {
+    EdgeIter ei, ee;
+    for (boost::tie(ei, ee) = edges(_g); ei != ee; ++ei)
+        _g[*ei].visited = false;
+
+    std::map<VtxDesc, EdgeDesc> bestNodeScoreEdge;
+    std::map<VtxDesc, float> nodeScore;
+    std::queue<VtxDesc> seedNodes;
+
+    // start at the end and make our way backwards
+    seedNodes.push(_exitVtx);
+    nodeScore[_exitVtx] = 0.0f;
+
+    while (true) {
+        if (seedNodes.size() == 0)
+            break;
+
+        VtxDesc n = seedNodes.front();
+        seedNodes.pop();
+        
+        bool bestEdgeFound = false;
+        float bestScore = -FLT_MAX;
+        EdgeDesc bestEdgeD = boost::initialized_value; 
+        OutEdgeIter oi, oe;
+        for(boost::tie(oi, oe) = boost::out_edges(n, _g); oi != oe; ++oi) {
+            EdgeDesc outEdgeD = *oi;
+            VtxDesc outNodeD = boost::target(outEdgeD, _g);
+            AlnNode outNode = _g[outNodeD];
+            float newScore, score = nodeScore[outNodeD];
+            if (outNode.backbone && outNode.weight == 1) {
+                newScore = score - 10.0f;
+            } else {
+                AlnNode bbNode = _g[_bbMap[outNodeD]];
+                newScore = _g[outEdgeD].count - bbNode.coverage*0.5f + score;
+            }
+
+            if (newScore > bestScore) {
+                bestScore = newScore;
+                bestEdgeD = outEdgeD;
+                bestEdgeFound = true; 
+            }
+        }
+        
+        if (bestEdgeFound) {
+            nodeScore[n]= bestScore;
+            bestNodeScoreEdge[n] = bestEdgeD;
+        }
+
+        InEdgeIter ii, ie;
+        for (boost::tie(ii, ie) = boost::in_edges(n, _g); ii != ie; ++ii) {
+            EdgeDesc inEdge = *ii;
+            _g[inEdge].visited = true;
+            VtxDesc inNode = boost::source(inEdge, _g);
+            int notVisited = 0;
+            OutEdgeIter oi, oe;
+            for (boost::tie(oi, oe) = boost::out_edges(inNode, _g); oi != oe; ++oi) {
+                if (_g[*oi].visited == false)
+                    notVisited++;
+            }
+            
+            // move onto the target node after we visit all incoming edges for
+            // the target node
+            if (notVisited == 0)
+                seedNodes.push(inNode);
+        }
+    }
+
+    // construct the final best path
+    VtxDesc prev = _enterVtx, next;
+    std::vector<AlnNode> bpath;
+    while (true) {
+        bpath.push_back(_g[prev]);
+        if (bestNodeScoreEdge.count(prev) == 0) {
+            break;
+        } else {
+            EdgeDesc bestOutEdge = bestNodeScoreEdge[prev];
+            _g[prev].bestOutEdge = bestOutEdge;
+            next = boost::target(bestOutEdge, _g);
+            _g[next].bestInEdge = bestOutEdge;
+            prev = next;
+        }
+    }
+
+    return bpath;
+}
+
+void AlnGraphBoost::printGraph() {
+    reapNodes();
+    boost::write_graphviz(std::cout, _g, 
+        make_label_writer(get(&AlnNode::base, _g)),
+        make_label_writer(get(&AlnEdge::count, _g)));
+}
+
+bool AlnGraphBoost::danglingNodes() {
+    VtxIter curr, last;
+    boost::tie(curr, last) = boost::vertices(_g);
+    bool found = false;
+    for (;curr != last; ++curr) {
+        if (_g[*curr].deleted)
+            continue;
+        if (_g[*curr].base == _g[_enterVtx].base || _g[*curr].base == _g[_exitVtx].base) 
+            continue;
+
+        int indeg = out_degree(*curr, _g); 
+        int outdeg = in_degree(*curr, _g); 
+        if (outdeg > 0 && indeg > 0) continue;
+
+        found = true;
+    }
+    return found;
+}
+
+AlnGraphBoost::~AlnGraphBoost(){}
diff --git a/src/cpp/AlnGraphBoost.hpp b/src/cpp/AlnGraphBoost.hpp
new file mode 100644
index 0000000..faf759c
--- /dev/null
+++ b/src/cpp/AlnGraphBoost.hpp
@@ -0,0 +1,182 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#ifndef __GCON_ALNGRAPHBOOST_HPP__
+#define __GCON_ALNGRAPHBOOST_HPP__
+
+/// Alignment graph representation and consensus caller.  Based on the original
+/// Python implementation, pbdagcon.  This class is modelled after its 
+/// aligngraph.py component, which accumulates alignment information into a
+/// partial-order graph and then calls consensus.  Used to error-correct pacbio
+/// on pacbio reads. 
+///
+/// Implemented using the boost graph library.
+
+// forward declaration 
+//struct Alignment;
+
+// this allows me to forward-declare properties with graph descriptors as
+// members types 
+typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS> graphTraits;
+
+/// Graph vertex property. An alignment node, which represents one base position 
+/// in the alignment graph.
+struct AlnNode {
+    char base; ///< DNA base: [ACTG]
+    int coverage; ///< Number of reads align to this position, but not 
+                  ///< necessarily match
+    int weight; ///< Number of reads that align to this node *with the same base*, but not
+                ///< necessarily represented in the target.
+    bool backbone; ///< Is this node based on the reference
+    bool deleted; ///< mark for removed as part of the merging process
+    graphTraits::edge_descriptor bestInEdge; ///< Best scoring in edge
+    graphTraits::edge_descriptor bestOutEdge; ///< Best scoring out edge
+    AlnNode() {
+        base = 'N';
+        coverage = 0;
+        weight = 0;
+        backbone = false;
+        deleted = false; 
+    }
+};
+
+/// Graph edge property. Represents an edge between alignment nodes.
+struct AlnEdge {
+    int count; ///< Number of times this edge was confirmed by an alignment
+    bool visited; ///< Tracks a visit during algorithm processing
+    AlnEdge() {
+        count = 0;
+        visited = false;
+    }
+};
+
+// Boost-related typedefs
+// XXX: listS, listS?
+typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, AlnNode, AlnEdge> G;
+typedef boost::graph_traits<G>::vertex_descriptor VtxDesc;
+typedef boost::graph_traits<G>::vertex_iterator VtxIter;
+typedef boost::graph_traits<G>::edge_descriptor EdgeDesc;
+typedef boost::graph_traits<G>::edge_iterator EdgeIter;
+typedef boost::graph_traits<G>::in_edge_iterator InEdgeIter;
+typedef boost::graph_traits<G>::out_edge_iterator OutEdgeIter;
+typedef boost::property_map<G, boost::vertex_index_t>::type IndexMap;
+
+/// 
+/// Simple consensus interface datastructure
+///
+struct CnsResult {
+    int range[2]; ///< Range on the target
+    std::string seq; ///< Consensus fragment
+};
+
+/// 
+/// Core alignments into consensus algorithm, implemented using the boost graph
+/// library.  Takes a set of alignments to a reference and builds a higher
+/// accuracy (~ 99.9) consensus sequence from it.  Designed for use in the HGAP
+/// pipeline as a long read error correction step.
+///
+class AlnGraphBoost {
+public:
+    /// Constructor.  Initialize graph based on the given sequence. Graph is
+    /// annotated with the bases from the backbone.
+    /// \param backbone the reference sequence.
+    AlnGraphBoost(const std::string& backbone);
+
+    /// Constructor.  Initialize graph to a given backbone length. Base
+    /// information is filled in as alignments are added.
+    /// \param blen length of the reference sequence.
+    AlnGraphBoost(const size_t blen);
+
+    /// Add alignment to the graph.
+    /// \param Alignment an alignment record (see Alignment.hpp)
+    void addAln(dagcon::Alignment& aln);
+
+    /// Adds a new or increments an existing edge between two aligned bases.
+    /// \param u the 'from' vertex descriptor
+    /// \param v the 'to' vertex descriptor
+    void addEdge(VtxDesc u, VtxDesc v);
+
+    /// Collapses degenerate nodes (vertices).  Must be called before 
+    /// consensus(). Calls mergeInNodes() followed by mergeOutNodes().
+    void mergeNodes();
+
+    /// Recursive merge of 'in' nodes.
+    /// \param n the base node to merge around.
+    void mergeInNodes(VtxDesc n);
+
+    /// Non-recursive merge of 'out' nodes.
+    /// \param n the base node to merge around.
+    void mergeOutNodes(VtxDesc n);
+
+    /// Mark a given node for removal from graph. Doesn't not modify graph.
+    /// \param n the node to remove.
+    void markForReaper(VtxDesc n);
+
+    /// Removes the set of nodes that have been marked.  Modifies graph.  
+    /// Prohibitively expensive when using vecS as the vertex container.
+    void reapNodes();
+
+    /// Generates the consensus from the graph.  Must be called after
+    /// mergeNodes(). Returns the longest contiguous consensus sequence where
+    /// each base meets the minimum weight requirement.
+    /// \param minWeight sets the minimum weight for each base in the consensus. 
+    ///        default = 0
+    const std::string consensus(int minWeight=0);
+
+    /// Generates all consensus sequences from a target that meet the minimum
+    /// weight requirement.
+    void consensus(std::vector<CnsResult>& seqs, int minWeight=0, size_t minLength=500);
+
+    /// Locates the optimal path through the graph.  Called by consensus()
+    const std::vector<AlnNode> bestPath();
+
+    /// Emits the current graph, in dot format, to stdout.
+    void printGraph();
+
+    /// Locate nodes that are missing either in or out edges.
+    bool danglingNodes();
+
+    /// Destructor.
+    virtual ~AlnGraphBoost();
+private:
+    G _g;
+    VtxDesc _enterVtx; 
+    VtxDesc _exitVtx;
+    std::map<VtxDesc, VtxDesc> _bbMap;
+    std::vector<VtxDesc> _reaperBag;
+};
+
+#endif // __GCON_ALNGRAPHBOOST_HPP__
diff --git a/src/cpp/AlnProvider.hpp b/src/cpp/AlnProvider.hpp
new file mode 100644
index 0000000..98373d4
--- /dev/null
+++ b/src/cpp/AlnProvider.hpp
@@ -0,0 +1,85 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#ifndef __GCON_ALN_PROVIDER__
+#define __GCON_ALN_PROVIDER__
+
+#include <exception>
+
+///
+/// Exceptions thrown by derived classes
+///
+namespace PacBio {
+namespace DagCon {
+    class IOException : public std::exception {
+    public:
+        IOException(const char* desc);
+        virtual const char* what() const throw() {
+            return desc_;
+        }
+    private:
+        const char* desc_; 
+    };
+
+    class MemoryException : public std::exception {
+    public:
+        virtual const char* what() const throw() {
+            return "Failed to allocate memory";
+        }
+    };
+}}
+
+
+///
+/// Generic alignment provider interface.
+///
+class AlnProvider {
+public:
+    /// Gets the set of alignments for the next target and puts them into the
+    /// given vector.  Note this function will clear the contents of the vector
+    /// prior to adding the next set of alignments.
+    /// \param dest reference to a vector to hold the alignments.
+    /// \return True if there are more targets, otherwise false.
+    virtual bool nextTarget(std::vector<dagcon::Alignment>& dest) = 0;
+
+    /// Same as nextTarget(dest), except it also returns the target sequence we are 
+    /// going to correct.
+    virtual bool nextTarget(std::string& targSeq, std::vector<dagcon::Alignment>& dest) = 0; 
+
+    virtual ~AlnProvider() {};
+};
+
+#endif //__GCON_ALN_PROVIDER__
diff --git a/src/cpp/BlasrM5AlnProvider.cpp b/src/cpp/BlasrM5AlnProvider.cpp
new file mode 100644
index 0000000..6b71b7c
--- /dev/null
+++ b/src/cpp/BlasrM5AlnProvider.cpp
@@ -0,0 +1,133 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#include <vector>
+#include <iostream>
+#include <istream>
+#include <fstream>
+#include <sstream>
+#include <boost/format.hpp>
+#include "Alignment.hpp"
+#include "BlasrM5AlnProvider.hpp"
+
+
+BlasrM5AlnProvider::BlasrM5AlnProvider(const std::string& fpath) :
+    fpath_(fpath),
+    currId_(""),
+    firstAln_(true),
+    fs_() {
+
+    //checkFormat();
+    fs_.open(fpath_);
+    is_ = &fs_;
+}
+
+BlasrM5AlnProvider::BlasrM5AlnProvider(std::istream* stream) :
+    fpath_(""),
+    currId_(""),
+    firstAln_(true),
+    fs_(),
+    is_(stream) {
+}
+
+BlasrM5AlnProvider::~BlasrM5AlnProvider() {
+    delete is_;
+}
+
+bool BlasrM5AlnProvider::nextTarget(std::vector<dagcon::Alignment>& dest) {
+    // first clear any previous alignments
+    dest.clear();
+
+    // process up to EOF or next target
+    // need to maintain state in between calls
+    if (! firstAln_)
+        dest.push_back(prevAln_); 
+
+    dagcon::Alignment aln;
+    while (*is_ >> aln) {
+        if (aln.id != currId_) {
+            firstAln_ = false;
+            prevAln_ = aln;
+            currId_ = aln.id;
+            break;
+        }
+        dest.push_back(aln);
+    }
+
+    return bool(*is_);
+}
+
+bool BlasrM5AlnProvider::nextTarget(std::string& targetSeq, std::vector<dagcon::Alignment>& dest) {
+    // NOOP
+    return false;
+}
+
+void BlasrM5AlnProvider::checkFormat() {
+    std::ifstream ifs(fpath_);
+    if (! ifs.is_open() || ifs.fail()) {
+        throw M5Exception::FileOpenError();
+    }
+    // parse the first line and run some field checks
+    std::string line;
+    std::getline(ifs, line);
+    std::stringstream row(line);
+    std::string col;
+    std::vector<std::string> fields;
+
+    while(std::getline(row, col, ' ')) {
+        if (col == "") continue;
+        fields.push_back(col);
+    }
+
+    if (fields.size() < 19) {
+        boost::format msg("Expected 19 fields, found %d");
+        msg % fields.size();
+        throw M5Exception::FormatError(msg.str());
+    }
+
+    // check how the alignments are grouped
+    dagcon::Alignment aln;
+    std::vector<std::string> raw, sorted;
+    int max = 50, count = 0;
+    while(ifs >> aln && count++ < max) 
+        raw.push_back(aln.id);
+
+    sorted = raw;
+    std::sort(sorted.begin(), sorted.end());
+
+    std::string logl = "dagcon::Alignments appear to be grouped by %s";
+    ifs.close();
+}
diff --git a/src/cpp/BlasrM5AlnProvider.hpp b/src/cpp/BlasrM5AlnProvider.hpp
new file mode 100644
index 0000000..087555a
--- /dev/null
+++ b/src/cpp/BlasrM5AlnProvider.hpp
@@ -0,0 +1,107 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#ifndef __GCON_BLASRM5_ALN_PROVIDER__
+#define __GCON_BLASRM5_ALN_PROVIDER__
+
+#include "AlnProvider.hpp"
+
+///
+/// Exceptions thrown by this class
+///
+namespace M5Exception {
+    struct FormatError {
+        std::string msg;
+        FormatError(std::string m) { msg = m; }
+    };
+
+    struct SortError {};
+
+    struct FileOpenError {};
+}
+
+///
+/// Provides sets of alignments for a given target sequence from a blasr M5 
+/// file.  File may be grouped by target or query.  The grouping determines
+/// which set gets corrected.  Earlier, pre-assembly reads were corrected as
+/// targets.  However, we can avoid the sort step if we can correct the reads 
+/// as queries, since blasr groups alignments by query.
+///
+class BlasrM5AlnProvider : public AlnProvider {
+public:
+    /// Constructs a new alignment provider.  Checks the format of the file and
+    /// throws an exception if it's malformed.
+    /// \param fpath Path to the file containing alignments.
+    BlasrM5AlnProvider(const std::string& fpath);
+
+    /// Constructs a provider based on the given stream.  Note that no checks
+    /// are actually made on the validity of the format, caveat emptor. This
+    /// can be used to take a piped stream of alignments straight from blasr.
+    BlasrM5AlnProvider(std::istream* stream);
+    
+    /// Cleans up some stuff.
+    ~BlasrM5AlnProvider();
+
+    /// Gets the set of alignments for the next target and puts them into the
+    /// given vector.  Note this function will clear the contents of the vector
+    /// prior to adding the next set of alignments.
+    /// \param dest reference to a vector to hold the alignments.
+    /// \return True if there are more targets, otherwise false.
+    bool nextTarget(std::vector<dagcon::Alignment>& dest);
+    
+    /// Same as \fn bool nextTarget(std::vector<dagcon::Alignment>& dest) except it
+    /// also returns the target sequence we are going to correct.
+    bool nextTarget(std::string& targetSeq, std::vector<dagcon::Alignment>& dest);
+    
+    /// Called during constructor, checks that the file is formatted correctly.
+    /// Also determines if the input is grouped by query or target.
+    void checkFormat();
+
+private:
+    /// Path to the input file
+    const std::string fpath_;
+
+    /// State variables 
+    std::string currId_;
+    dagcon::Alignment prevAln_;
+    bool firstAln_;
+    
+    /// Represents an input stream to the alignments.
+    std::ifstream fs_;
+    std::istream* is_;
+};
+
+#endif //__GCON_BLASRM5_ALN_PROVIDER__
diff --git a/src/cpp/BoundedBuffer.hpp b/src/cpp/BoundedBuffer.hpp
new file mode 100644
index 0000000..394b50a
--- /dev/null
+++ b/src/cpp/BoundedBuffer.hpp
@@ -0,0 +1,89 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+#ifndef __GCON_BOUNDEDBUFFER__
+#define __GCON_BOUNDEDBUFFER__
+
+#include <deque>
+#include <condition_variable>
+#include <mutex>
+#include <functional>
+#include <vector>
+#include <string>
+#include "Alignment.hpp"
+
+///
+/// Templated, thread-safe buffer container, uses uses boost::circular buffer 
+/// bounded by a given capacity specified by the caller.  When the buffer is 
+/// full, the push waits for an open spot.  When the buffer is empty, the pop
+/// waits for an item to be present.  Condition variables are used to signal
+/// the state of the buffer.
+///
+template <class T>
+class BoundedBuffer {
+public:
+    typedef std::deque<T> buffer_type;
+
+    BoundedBuffer(int max) : max_(max) { }
+
+    void push(T item) {
+        std::unique_lock<std::mutex> lock(mutex_);
+        not_full_.wait(lock, [this](){return buffer_.size() != max_;}); 
+        buffer_.push_front(item);
+        not_empty_.notify_one();
+    }
+
+    void pop(T* pItem) {
+        std::unique_lock<std::mutex> lock(mutex_);
+        not_empty_.wait(lock, [this](){return buffer_.size() != 0;});
+        *pItem = buffer_.back();
+        buffer_.pop_back();
+        not_full_.notify_one();
+    }
+
+    unsigned int size() {
+        return buffer_.size();
+    }
+
+private:
+    int max_;
+    buffer_type buffer_;
+    std::mutex mutex_;
+    std::condition_variable not_empty_;
+    std::condition_variable not_full_;
+};
+
+#endif // __GCON_BOUNDEDBUFFER__
+
diff --git a/src/cpp/DB.c b/src/cpp/DB.c
new file mode 100644
index 0000000..95f073d
--- /dev/null
+++ b/src/cpp/DB.c
@@ -0,0 +1,1106 @@
+/************************************************************************************\
+*                                                                                    *
+* Copyright (c) 2014, Dr. Eugene W. Myers (EWM). All rights reserved.                *
+*                                                                                    *
+* Redistribution and use in source and binary forms, with or without modification,   *
+* are permitted provided that the following conditions are met:                      *
+*                                                                                    *
+*  · Redistributions of source code must retain the above copyright notice, this     *
+*    list of conditions and the following disclaimer.                                *
+*                                                                                    *
+*  · Redistributions in binary form must reproduce the above copyright notice, this  *
+*    list of conditions and the following disclaimer in the documentation and/or     *
+*    other materials provided with the distribution.                                 *
+*                                                                                    *
+*  · The name of EWM may not be used to endorse or promote products derived from     *
+*    this software without specific prior written permission.                        *
+*                                                                                    *
+* THIS SOFTWARE IS PROVIDED BY EWM ”AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,    *
+* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND       *
+* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EWM BE LIABLE   *
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  *
+* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY      *
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING     *
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN  *
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                      *
+*                                                                                    *
+* For any issues regarding this software and its use, contact EWM at:                *
+*                                                                                    *
+*   Eugene W. Myers Jr.                                                              *
+*   Bautzner Str. 122e                                                               *
+*   01099 Dresden                                                                    *
+*   GERMANY                                                                          *
+*   Email: gene.myers at gmail.com                                                      *
+*                                                                                    *
+\************************************************************************************/
+
+/*******************************************************************************************
+ *
+ *  Compressed data base module.  Auxiliary routines to open and manipulate a data base for
+ *    which the sequence and read information are separated into two separate files, and the
+ *    sequence is compressed into 2-bits for each base.  Support for tracks of additional
+ *    information, and trimming according to the current partition.  Eventually will also
+ *    support compressed quality information.
+ *
+ *  Author :  Gene Myers
+ *  Date   :  July 2013
+ *  Revised:  April 2014
+ *
+ ********************************************************************************************/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "DB.h"
+
+#ifdef HIDE_FILES
+#define PATHSEP "/."
+#else
+#define PATHSEP "/"
+#endif
+
+
+/*******************************************************************************************
+ *
+ *  GENERAL UTILITIES
+ *
+ ********************************************************************************************/
+
+char *Prog_Name;
+
+void *Malloc(int64 size, char *mesg)
+{ void *p;
+
+  if ((p = malloc(size)) == NULL)
+    { if (mesg == NULL)
+        fprintf(stderr,"%s: Out of memory\n",Prog_Name);
+      else
+        fprintf(stderr,"%s: Out of memory (%s)\n",Prog_Name,mesg);
+    }
+  return (p);
+}
+
+void *Realloc(void *p, int64 size, char *mesg)
+{ if ((p = realloc(p,size)) == NULL)
+    { if (mesg == NULL)
+        fprintf(stderr,"%s: Out of memory\n",Prog_Name);
+      else
+        fprintf(stderr,"%s: Out of memory (%s)\n",Prog_Name,mesg);
+    }
+  return (p);
+}
+
+char *Strdup(char *name, char *mesg)
+{ char *s;
+
+  if (name == NULL)
+    return (NULL);
+  if ((s = strdup(name)) == NULL)
+    { if (mesg == NULL)
+        fprintf(stderr,"%s: Out of memory\n",Prog_Name);
+      else
+        fprintf(stderr,"%s: Out of memory (%s)\n",Prog_Name,mesg);
+    }
+  return (s);
+}
+
+FILE *Fopen(char *name, char *mode)
+{ FILE *f;
+
+  if (name == NULL || mode == NULL)
+    return (NULL);
+  if ((f = fopen(name,mode)) == NULL)
+    fprintf(stderr,"%s: Cannot open %s for '%s'\n",Prog_Name,name,mode);
+  return (f);
+}
+
+char *PathTo(char *name)
+{ char *path, *find;
+
+  if (name == NULL)
+    return (NULL);
+  if ((find = rindex(name,'/')) != NULL)
+    { *find = '\0';
+      path = Strdup(name,"Extracting path from");
+      *find = '/';
+    }
+  else
+    path = Strdup(".","Allocating default path");
+  return (path);
+}
+
+char *Root(char *name, char *suffix)
+{ char *path, *find, *dot;
+  int   epos;
+
+  if (name == NULL)
+    return (NULL);
+  find = rindex(name,'/');
+  if (find == NULL)
+    find = name;
+  else
+    find += 1;
+  if (suffix == NULL)
+    { dot = strchr(find,'.');
+      if (dot != NULL)
+        *dot = '\0';
+      path = Strdup(find,"Extracting root from");
+      if (dot != NULL)
+        *dot = '.';
+    }
+  else
+    { epos  = strlen(find);
+      epos -= strlen(suffix);
+      if (epos > 0 && strcasecmp(find+epos,suffix) == 0)
+        { find[epos] = '\0';
+          path = Strdup(find,"Extracting root from");
+          find[epos] = suffix[0];
+        }
+      else
+        path = Strdup(find,"Allocating root");
+    }
+  return (path);
+}
+
+char *Catenate(char *path, char *sep, char *root, char *suffix)
+{ static char *cat = NULL;
+  static int   max = -1;
+  int len;
+
+  if (path == NULL || root == NULL || sep == NULL || suffix == NULL)
+    return (NULL);
+  len =  strlen(path);
+  len += strlen(sep);
+  len += strlen(root);
+  len += strlen(suffix);
+  if (len > max)
+    { max = ((int) (1.2*len)) + 100;
+      if ((cat = (char *) realloc(cat,max+1)) == NULL)
+        { fprintf(stderr,"%s: Out of memory (Making path name for %s)\n",Prog_Name,root);
+          return (NULL);
+        }
+    }
+  sprintf(cat,"%s%s%s%s",path,sep,root,suffix);
+  return (cat);
+}
+
+char *Numbered_Suffix(char *left, int num, char *right)
+{ static char *suffix = NULL;
+  static int   max = -1;
+  int len;
+
+  if (left == NULL || right == NULL)
+    return (NULL);
+  len =  strlen(left);
+  len += strlen(right) + 40;
+  if (len > max)
+    { max = ((int) (1.2*len)) + 100;
+      if ((suffix = (char *) realloc(suffix,max+1)) == NULL)
+        { fprintf(stderr,"%s: Out of memory (Making number suffix for %d)\n",Prog_Name,num);
+          return (NULL);
+        }
+    }
+  sprintf(suffix,"%s%d%s",left,num,right);
+  return (suffix);
+}
+
+
+#define  COMMA  ','
+
+//  Print big integers with commas/periods for better readability
+
+void Print_Number(int64 num, int width, FILE *out)
+{ if (width == 0)
+    { if (num < 1000ll)
+        fprintf(out,"%lld",num);
+      else if (num < 1000000ll)
+        fprintf(out,"%lld%c%03lld",num/1000ll,COMMA,num%1000ll);
+      else if (num < 1000000000ll)
+        fprintf(out,"%lld%c%03lld%c%03lld",num/1000000ll,
+                                           COMMA,(num%1000000ll)/1000ll,COMMA,num%1000ll);
+      else
+        fprintf(out,"%lld%c%03lld%c%03lld%c%03lld",num/1000000000ll,
+                                                  COMMA,(num%1000000000ll)/1000000ll,
+                                                  COMMA,(num%1000000ll)/1000ll,COMMA,num%1000ll);
+    }
+  else
+    { if (num < 1000ll)
+        fprintf(out,"%*lld",width,num);
+      else if (num < 1000000ll)
+        { if (width <= 4)
+            fprintf(out,"%lld%c%03lld",num/1000ll,COMMA,num%1000ll);
+          else
+            fprintf(out,"%*lld%c%03lld",width-4,num/1000ll,COMMA,num%1000ll);
+        }
+      else if (num < 1000000000ll)
+        { if (width <= 8)
+            fprintf(out,"%lld%c%03lld%c%03lld",num/1000000ll,COMMA,(num%1000000ll)/1000ll,
+                                               COMMA,num%1000ll);
+          else
+            fprintf(out,"%*lld%c%03lld%c%03lld",width-8,num/1000000ll,COMMA,(num%1000000ll)/1000ll,
+                                                COMMA,num%1000ll);
+        }
+      else
+        { if (width <= 12)
+            fprintf(out,"%lld%c%03lld%c%03lld%c%03lld",num/1000000000ll,COMMA,
+                                                       (num%1000000000ll)/1000000ll,COMMA,
+                                                       (num%1000000ll)/1000ll,COMMA,num%1000ll);
+          else
+            fprintf(out,"%*lld%c%03lld%c%03lld%c%03lld",width-12,num/1000000000ll,COMMA,
+                                                            (num%1000000000ll)/1000000ll,COMMA,
+                                                            (num%1000000ll)/1000ll,COMMA,num%1000ll);
+        }
+    }
+}
+
+//  Return the number of digits, base 10, of num
+
+int  Number_Digits(int64 num)
+{ int digit;
+
+  digit = 0;
+  while (num >= 1)
+    { num /= 10;
+      digit += 1;
+    }
+  return (digit);
+}
+
+
+/*******************************************************************************************
+ *
+ *  READ COMPRESSION/DECOMPRESSION UTILITIES
+ *
+ ********************************************************************************************/
+
+//  Compress read into 2-bits per base (from [0-3] per byte representation
+
+void Compress_Read(int len, char *s)
+{ int   i;
+  char  c, d;
+  char *s0, *s1, *s2, *s3;
+
+  s0 = s;
+  s1 = s0+1;
+  s2 = s1+1;
+  s3 = s2+1;
+
+  c = s1[len];
+  d = s2[len];
+  s0[len] = s1[len] = s2[len] = 0;
+
+  for (i = 0; i < len; i += 4)
+    *s++ = (char ) ((s0[i] << 6) | (s1[i] << 4) | (s2[i] << 2) | s3[i]);
+
+  s1[len] = c;
+  s2[len] = d;
+}
+
+//  Uncompress read form 2-bits per base into [0-3] per byte representation
+
+void Uncompress_Read(int len, char *s)
+{ int   i, tlen, byte;
+  char *s0, *s1, *s2, *s3;
+  char *t;
+
+  s0 = s;
+  s1 = s0+1;
+  s2 = s1+1;
+  s3 = s2+1;
+
+  tlen = (len-1)/4;
+
+  t = s+tlen;
+  for (i = tlen*4; i >= 0; i -= 4)
+    { byte = *t--;
+      s0[i] = (char) ((byte >> 6) & 0x3);
+      s1[i] = (char) ((byte >> 4) & 0x3);
+      s2[i] = (char) ((byte >> 2) & 0x3);
+      s3[i] = (char) (byte & 0x3);
+    }
+  s[len] = 4;
+}
+
+//  Convert read in [0-3] representation to ascii representation (end with '\n')
+
+void Lower_Read(char *s)
+{ static char letter[4] = { 'a', 'c', 'g', 't' };
+
+  for ( ; *s != 4; s++)
+    *s = letter[(int) *s];
+  *s = '\0';
+}
+
+void Upper_Read(char *s)
+{ static char letter[4] = { 'A', 'C', 'G', 'T' };
+
+  for ( ; *s != 4; s++)
+    *s = letter[(int) *s];
+  *s = '\0';
+}
+
+//  Convert read in ascii representation to [0-3] representation (end with 4)
+
+void Number_Read(char *s)
+{ static char number[128] =
+    { 0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 1, 0, 0, 0, 2,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 3, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 1, 0, 0, 0, 2,
+      0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 3, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0,
+    };
+
+  for ( ; *s != '\0'; s++)
+    *s = number[(int) *s];
+  *s = 4;
+}
+
+
+/*******************************************************************************************
+ *
+ *  DB OPEN, TRIM & CLOSE ROUTINES
+ *
+ ********************************************************************************************/
+
+
+// Open the given database or dam, "path" into the supplied HITS_DB record "db". If the name has
+//   a part # in it then just the part is opened.  The index array is allocated (for all or
+//   just the part) and read in.
+// Return status of routine:
+//    -1: The DB could not be opened for a reason reported by the routine to stderr
+//     0: Open of DB proceeded without mishap
+//     1: Open of DAM proceeded without mishap
+
+int Open_DB(char* path, HITS_DB *db)
+{ char *root, *pwd, *bptr, *fptr, *cat;
+  int   nreads;
+  FILE *index, *dbvis;
+  int   status, plen, isdam;
+  int   part, cutoff, all;
+  int   ofirst, bfirst, olast;
+
+
+  plen = strlen(path);
+  if (strcmp(path+(plen-4),".dam") == 0)
+    root = Root(path,".dam");
+  else
+    root = Root(path,".db");
+  pwd = PathTo(path);
+
+  bptr = rindex(root,'.');
+  if (bptr != NULL && bptr[1] != '\0' && bptr[1] != '-')
+    { part = strtol(bptr+1,&fptr,10);
+      if (*fptr != '\0' || part == 0)
+        part = 0;
+      else
+        *bptr = '\0';
+    }
+  else
+    part = 0;
+
+  isdam = 0;
+  cat = Catenate(pwd,"/",root,".db");
+  if (cat == NULL)
+    exit (1);
+  if ((dbvis = fopen(cat,"r")) == NULL)
+    { cat = Catenate(pwd,"/",root,".dam");
+      if (cat == NULL)
+        exit (1);
+      if ((dbvis = fopen(cat,"r")) == NULL)
+        { status = -1;
+          fprintf(stderr,"%s: Could not open database %s\n",Prog_Name,path);
+          goto exit;
+        }
+      isdam = 1;
+    }
+
+  if ((index = Fopen(Catenate(pwd,PATHSEP,root,".idx"),"rm")) == NULL)
+    { status = -1;
+      goto exit1;
+    }
+  if (fread(db,sizeof(HITS_DB),1,index) != 1)
+    SYSTEM_ERROR
+  nreads = db->oreads;
+
+  { int   p, nblocks, nfiles, blast;
+    int64 size;
+    char  fname[MAX_NAME], prolog[MAX_NAME];
+
+    nblocks = 0;
+    if (fscanf(dbvis,DB_NFILE,&nfiles) != 1)
+      SYSTEM_ERROR
+    for (p = 0; p < nfiles; p++)
+      if (fscanf(dbvis,DB_FDATA,&blast,fname,prolog) != 3)
+        SYSTEM_ERROR
+    if (fscanf(dbvis,DB_NBLOCK,&nblocks) != 1)
+      if (part == 0)
+        { cutoff = 0;
+          all    = 1;
+        }
+      else
+        { fprintf(stderr,"%s: DB %s has not yet been partitioned, cannot request a block !\n",
+                         Prog_Name,root);
+          status = -1;
+          goto exit2;
+        }
+    else
+      { if (fscanf(dbvis,DB_PARAMS,&size,&cutoff,&all) != 3)
+          SYSTEM_ERROR
+        if (part > nblocks)
+          { fprintf(stderr,"%s: DB %s has only %d blocks\n",Prog_Name,root,nblocks);
+            status = -1;
+            goto exit2;
+          }
+      }
+
+    if (part > 0)
+      { for (p = 1; p <= part; p++)
+          if (fscanf(dbvis,DB_BDATA,&ofirst,&bfirst) != 2)
+            SYSTEM_ERROR
+        if (fscanf(dbvis,DB_BDATA,&olast,&blast) != 2)
+          SYSTEM_ERROR
+      }
+    else
+      { ofirst = bfirst = 0;
+        olast  = nreads;
+      }
+  }
+
+  db->trimmed = 0;
+  db->tracks  = NULL;
+  db->part    = part;
+  db->cutoff  = cutoff;
+  db->all     = all;
+  db->ofirst  = ofirst;
+  db->bfirst  = bfirst;
+
+  if (part <= 0)
+    { db->reads = (HITS_READ *) Malloc(sizeof(HITS_READ)*(nreads+1),"Allocating Open_DB index");
+      if (fread(db->reads,sizeof(HITS_READ),nreads,index) != (size_t) nreads)
+        SYSTEM_ERROR
+    }
+  else
+    { HITS_READ *reads;
+      int        i, r, maxlen;
+      int64      totlen;
+
+      nreads = olast-ofirst;
+      reads  = (HITS_READ *) Malloc(sizeof(HITS_READ)*(nreads+1),"Allocating Open_DB index");
+
+      fseeko(index,sizeof(HITS_READ)*ofirst,SEEK_CUR);
+      if (fread(reads,sizeof(HITS_READ),nreads,index) != (size_t) nreads)
+        SYSTEM_ERROR
+
+      totlen = 0;
+      maxlen = 0;
+      for (i = 0; i < nreads; i++)
+        { r = reads[i].rlen;
+          totlen += r;
+          if (r > maxlen)
+            maxlen = r;
+        }
+
+      db->maxlen = maxlen;
+      db->totlen = totlen;
+      db->reads  = reads;
+    }
+
+  db->nreads = nreads;
+  db->path   = Strdup(Catenate(pwd,PATHSEP,root,""),"Allocating Open_DB path");
+  db->bases  = NULL;
+  db->loaded = 0;
+
+  status = isdam;
+
+exit2:
+  fclose(index);
+exit1:
+  fclose(dbvis);
+exit:
+  if (bptr != NULL)
+    *bptr = '.';
+
+  free(pwd);
+  free(root);
+
+  return (status);
+}
+
+
+// Trim the DB or part thereof and all loaded tracks according to the cuttof and all settings
+//   of the current DB partition.  Reallocate smaller memory blocks for the information kept
+//   for the retained reads.
+
+void Trim_DB(HITS_DB *db)
+{ int         i, j, r;
+  int         allflag, cutoff;
+  int64       totlen;
+  int         maxlen, nreads;
+  HITS_TRACK *record;
+  HITS_READ  *reads;
+
+  if (db->trimmed) return;
+
+  if (db->cutoff <= 0 && db->all) return;
+
+  cutoff = db->cutoff;
+  if (db->all)
+    allflag = 0;
+  else
+    allflag = DB_BEST;
+
+  reads  = db->reads;
+  nreads = db->nreads;
+
+  for (record = db->tracks; record != NULL; record = record->next)
+  { int   *anno4, size;
+    int64 *anno8;
+    char  *anno, *data;
+
+    size = record->size;
+    data = (char *) record->data; 
+    if (data == NULL)
+    { anno = (char *) record->anno;
+      j = 0;
+      for (i = r = 0; i < db->nreads; i++, r += size)
+        if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+        { memmove(anno+j,anno+r,size);
+          j += size;
+        }
+        memmove(anno+j,anno+r,size);
+    }
+    else if (size == 4)
+    { int ai;
+
+      anno4 = (int *) (record->anno);
+      j = anno4[0] = 0;
+      for (i = 0; i < db->nreads; i++)
+        if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+        { ai = anno4[i];
+          anno4[j+1] = anno4[j] + (anno4[i+1]-ai);
+          memmove(data+anno4[j],data+ai,anno4[i+1]-ai);
+          j += 1;
+        }
+      record->data = Realloc(record->data,anno4[j],NULL);
+    }
+    else // size == 8
+    { int64 ai;
+
+      anno8 = (int64 *) (record->anno);
+      j = anno8[0] = 0;
+      for (i = 0; i < db->nreads; i++)
+        if ((reads[i].flags & DB_BEST) >= allflag && reads[i].rlen >= cutoff)
+        { ai = anno8[i];
+          anno8[j+1] = anno8[j] + (anno8[i+1]-ai);
+          memmove(data+anno8[j],data+ai,anno8[i+1]-ai);
+          j += 1;
+        }
+      record->data = Realloc(record->data,anno8[j],NULL);
+    }
+    record->anno = Realloc(record->anno,record->size*(j+1),NULL);
+  }
+
+  totlen = maxlen = 0;
+  for (j = i = 0; i < nreads; i++)
+    { r = reads[i].rlen;
+      if ((reads[i].flags & DB_BEST) >= allflag && r >= cutoff)
+        { totlen += r;
+          if (r > maxlen)
+            maxlen = r;
+          reads[j++] = reads[i];
+        }
+    }
+  
+  db->totlen  = totlen;
+  db->maxlen  = maxlen;
+  db->nreads  = j;
+  db->trimmed = 1;
+
+  if (j < nreads)
+    db->reads = Realloc(reads,sizeof(HITS_READ)*(j+1),NULL);
+}
+
+// Shut down an open 'db' by freeing all associated space, including tracks and QV structures, 
+//   and any open file pointers.  The record pointed at by db however remains (the user
+//   supplied it and so should free it).
+
+void Close_DB(HITS_DB *db)
+{ HITS_TRACK *t, *p;
+
+  if (db->loaded)
+    free(((char *) (db->bases)) - 1);
+  else if (db->bases != NULL)
+    fclose((FILE *) db->bases);
+  free(db->reads);
+  free(db->path);
+
+  for (t = db->tracks; t != NULL; t = p)
+    { p = t->next;
+      free(t->anno);
+      free(t->data);
+      free(t);
+    }
+}
+
+/*******************************************************************************************
+ *
+ *  TRACK LOAD & CLOSE ROUTINES
+ *
+ ********************************************************************************************/
+
+//  Return status of track:
+//     1: Track is for trimmed DB
+//     0: Track is for untrimmed DB
+//    -1: Track is not the right size of DB either trimmed or untrimmed
+//    -2: Could not find the track 
+
+int Check_Track(HITS_DB *db, char *track)
+{ FILE       *afile;
+  int         tracklen;
+
+  afile = fopen(Catenate(db->path,".",track,".anno"),"r");
+  if (afile == NULL)
+    return (-2);
+
+  if (fread(&tracklen,sizeof(int),1,afile) != 1)
+    SYSTEM_ERROR
+
+  fclose(afile);
+
+  if (tracklen == db->breads)
+    return (1);
+  else if (tracklen == db->oreads)
+    return (0);
+  else
+    return (-1);
+}
+
+// If track is not already in the db's track list, then allocate all the storage for it,
+//   read it in from the appropriate file, add it to the track list, and return a pointer
+//   to the newly created HITS_TRACK record.  If the track does not exist or cannot be
+//   opened for some reason, then NULL is returned.
+
+HITS_TRACK *Load_Track(HITS_DB *db, char *track)
+{ FILE       *afile, *dfile;
+  int         tracklen, size;
+  int         nreads;
+  void       *anno;
+  void       *data;
+  HITS_TRACK *record;
+
+  if (track[0] == '.')
+    { fprintf(stderr,"Track names cannot begin with a .\n");
+      exit (1);
+    }
+
+  for (record = db->tracks; record != NULL; record = record->next)
+    if (strcmp(record->name,track) == 0)
+      return (record);
+
+  afile = fopen(Catenate(db->path,".",track,".anno"),"r");
+  if (afile == NULL)
+    return (NULL);
+  dfile = fopen(Catenate(db->path,".",track,".data"),"r");
+
+  if (fread(&tracklen,sizeof(int),1,afile) != 1)
+    SYSTEM_ERROR
+  if (fread(&size,sizeof(int),1,afile) != 1)
+    SYSTEM_ERROR
+
+  if (db->trimmed)
+    { if (tracklen != db->breads)
+        { fprintf(stderr,"%s: Track %s not same size as database !\n",Prog_Name,track);
+          exit (1);
+        }
+      if (db->part > 0)
+        fseeko(afile,size*db->bfirst,SEEK_CUR);
+    }
+  else
+    { if (tracklen != db->oreads)
+        { fprintf(stderr,"%s: Track %s not same size as database !\n",Prog_Name,track);
+          exit (1);
+        }
+      if (db->part > 0)
+        fseeko(afile,size*db->ofirst,SEEK_CUR);
+    }
+  nreads = db->nreads;
+
+  anno = (void *) Malloc(size*(nreads+1),"Allocating Track Anno Vector");
+
+  if (size > 0)
+    { if (dfile == NULL)
+        { if (fread(anno,size,nreads,afile) != (size_t) nreads)
+            SYSTEM_ERROR
+        }
+      else
+        { if (fread(anno,size,nreads+1,afile) != (size_t) (nreads+1))
+            SYSTEM_ERROR
+        }
+    }
+  else
+    SYSTEM_ERROR
+
+  if (dfile != NULL)
+    { int64 *anno8, off8, dlen;
+      int   *anno4, off4;
+      int    i;
+
+      if (size == 4)
+        { anno4 = (int *) anno;
+          off4  = anno4[0];
+          if (off4 != 0)
+            { for (i = 0; i <= nreads; i++)
+                anno4[i] -= off4;
+              fseeko(dfile,off4,SEEK_SET);
+            }
+          dlen = anno4[nreads];
+          data = (void *) Malloc(dlen,"Allocating Track Data Vector");
+        }
+      else
+        { anno8 = (int64 *) anno;
+          off8  = anno8[0];
+          if (off8 != 0)
+            { for (i = 0; i <= nreads; i++)
+                anno8[i] -= off8;
+              fseeko(dfile,off8,SEEK_SET);
+            }
+          dlen = anno8[nreads];
+          data = (void *) Malloc(dlen,"Allocating Track Data Vector");
+        }
+      if (dlen > 0)
+        { if (fread(data,dlen,1,dfile) != 1)
+            SYSTEM_ERROR
+        }
+      fclose(dfile);
+    }
+  else
+    data = NULL;
+
+  fclose(afile);
+
+  record = (HITS_TRACK *) Malloc(sizeof(HITS_TRACK),"Allocating Track Record");
+  record->name = Strdup(track,"Allocating Track Name");
+  record->data = data;
+  record->anno = anno;
+  record->size = size;
+
+  if (db->tracks != NULL && strcmp(db->tracks->name,". at qvs") == 0)
+    { record->next     = db->tracks->next;
+      db->tracks->next = record;
+    }
+  else
+    { record->next = db->tracks;
+      db->tracks   = record;
+    }
+
+  return (record);
+}
+
+void Close_Track(HITS_DB *db, char *track)
+{ HITS_TRACK *record, *prev;
+
+  prev = NULL;
+  for (record = db->tracks; record != NULL; record = record->next)
+    { if (strcmp(record->name,track) == 0)
+        { free(record->anno);
+          free(record->data);
+          free(record->name);
+          if (prev == NULL)
+            db->tracks = record->next;
+          else
+            prev->next = record->next;
+          free(record);
+          return;
+        }
+      prev = record;
+    }
+  return;
+}
+
+
+/*******************************************************************************************
+ *
+ *  READ BUFFER ALLOCATION AND READ ACCESS
+ *
+ ********************************************************************************************/
+
+// Allocate and return a buffer big enough for the largest read in 'db', leaving room
+//   for an initial delimiter character
+
+char *New_Read_Buffer(HITS_DB *db)
+{ char *read;
+
+  read = (char *) Malloc(db->maxlen+4,"Allocating New Read Buffer");
+  if (read == NULL)
+    exit (1);
+  return (read+1);
+}
+
+// Load into 'read' the i'th read in 'db'.  As an upper case ASCII string if ascii is 2, as a
+//   lower-case ASCII string is ascii is 1, and as a numeric string over 0(A), 1(C), 2(G), and
+//   3(T) otherwise.
+//
+// **NB**, the byte before read will be set to a delimiter character!
+
+void Load_Read(HITS_DB *db, int i, char *read, int ascii)
+{ FILE      *bases  = (FILE *) db->bases;
+  int64      off;
+  int        len, clen;
+  HITS_READ *r = db->reads;
+
+  if (bases == NULL)
+    { db->bases = (void *) (bases = Fopen(Catenate(db->path,"","",".bps"),"r"));
+      if (bases == NULL)
+        exit (1);
+    }
+  if (i >= db->nreads)
+    { fprintf(stderr,"%s: Index out of bounds (Load_Read)\n",Prog_Name);
+      exit (1);
+    }
+
+  off = r[i].boff;
+  len = r[i].rlen;
+
+  if (ftello(bases) != off)
+    fseeko(bases,off,SEEK_SET);
+  clen = COMPRESSED_LEN(len);
+  if (clen > 0)
+    { if (fread(read,clen,1,bases) != 1)
+        SYSTEM_ERROR
+    }
+  Uncompress_Read(len,read);
+  if (ascii == 1)
+    { Lower_Read(read);
+      read[-1] = '\0';
+    }
+  else if (ascii == 2)
+    { Upper_Read(read);
+      read[-1] = '\0';
+    }
+  else
+    read[-1] = 4;
+}
+
+char *Load_Subread(HITS_DB *db, int i, int beg, int end, char *read, int ascii)
+{ FILE      *bases  = (FILE *) db->bases;
+  int64      off;
+  int        len, clen;
+  int        bbeg, bend;
+  HITS_READ *r = db->reads;
+
+  if (bases == NULL)
+    { db->bases = (void *) (bases = Fopen(Catenate(db->path,"","",".bps"),"rm"));
+      if (bases == NULL)
+        exit (1);
+    }
+  if (i >= db->nreads)
+    { fprintf(stderr,"%s: Index out of bounds (Load_Read)\n",Prog_Name);
+      exit (1);
+    }
+
+  bbeg = beg/4;
+  bend = (end-1)/4+1;
+
+  off = r[i].boff + bbeg;
+  len = end - beg;
+
+  if (ftello(bases) != off)
+    fseeko(bases,off,SEEK_SET);
+  clen = bend-bbeg;
+  if (clen > 0)
+    { if (fread(read,clen,1,bases) != 1)
+        SYSTEM_ERROR
+    }
+  Uncompress_Read(4*clen,read);
+  read += beg%4;
+  read[len] = 4;
+  if (ascii == 1)
+    { Lower_Read(read);
+      read[-1] = '\0';
+    }
+  else if (ascii == 2)
+    { Upper_Read(read);
+      read[-1] = '\0';
+    }
+  else
+    read[-1] = 4;
+
+  return (read);
+}
+
+
+/*******************************************************************************************
+ *
+ *  BLOCK LOAD OF ALL READS (PRIMARILY FOR DALIGNER)
+ *
+ ********************************************************************************************/
+
+// Allocate a block big enough for all the uncompressed sequences, read them into it,
+//   reset the 'off' in each read record to be its in-memory offset, and set the
+//   bases pointer to point at the block after closing the bases file.  If ascii is
+//   non-zero then the reads are converted to ACGT ascii, otherwise the reads are left
+//   as numeric strings over 0(A), 1(C), 2(G), and 3(T).
+
+void Read_All_Sequences(HITS_DB *db, int ascii)
+{ FILE      *bases  = (FILE *) db->bases;
+  int        nreads = db->nreads;
+  HITS_READ *reads = db->reads;
+  void     (*translate)(char *s);
+
+  char  *seq;
+  int64  o, off;
+  int    i, len, clen;
+
+  if (bases == NULL)
+    db->bases = (void *) (bases = Fopen(Catenate(db->path,"","",".bps"),"r"));
+  else
+    rewind(bases);
+
+  seq = (char *) Malloc(db->totlen+nreads+4,"Allocating All Sequence Reads");
+
+  *seq++ = 4;
+
+  if (ascii == 1)
+    translate = Lower_Read;
+  else
+    translate = Upper_Read;
+
+  o = 0;
+  for (i = 0; i < nreads; i++)
+    { len = reads[i].rlen;
+      off = reads[i].boff;
+      if (ftello(bases) != off)
+        fseeko(bases,off,SEEK_SET);
+      clen = COMPRESSED_LEN(len);
+      if (clen > 0)
+        { if (fread(seq+o,clen,1,bases) != 1)
+            SYSTEM_ERROR
+        }
+      Uncompress_Read(len,seq+o);
+      if (ascii)
+        translate(seq+o);
+      reads[i].boff = o;
+      o += (len+1);
+    }
+  reads[nreads].boff = o;
+
+  fclose(bases);
+
+  db->bases  = (void *) seq;
+  db->loaded = 1;
+}
+
+int List_DB_Files(char *path, void foreach(char *path, char *extension))
+{ int            status, rlen, dlen;
+  char          *root, *pwd, *name;
+  int            isdam;
+  DIR           *dirp;
+  struct dirent *dp;
+
+  status = 0;
+  pwd    = PathTo(path);
+  root   = Root(path,".db");
+  rlen   = strlen(root);
+
+  if (root == NULL || pwd == NULL)
+    { status = 1;
+      goto exit;
+    }
+
+  if ((dirp = opendir(pwd)) == NULL)
+    { status = 1;
+      goto exit;
+    }
+
+  isdam = 0;
+  while ((dp = readdir(dirp)) != NULL)     //   Get case dependent root name (if necessary)
+    { name = dp->d_name;
+      if (strcmp(name,Catenate("","",root,".db")) == 0)
+        break;
+      if (strcmp(name,Catenate("","",root,".dam")) == 0)
+        { isdam = 1;
+          break;
+        }
+      if (strcasecmp(name,Catenate("","",root,".db")) == 0)
+        { strncpy(root,name,rlen);
+          break;
+        }
+      if (strcasecmp(name,Catenate("","",root,".dam")) == 0)
+        { strncpy(root,name,rlen);
+          isdam = 1;
+          break;
+        }
+    }
+  if (dp == NULL)
+    { status = 1;
+      closedir(dirp);
+      goto exit;
+    }
+
+  if (isdam)
+    foreach(Catenate(pwd,"/",root,".dam"),"dam");
+  else
+    foreach(Catenate(pwd,"/",root,".db"),"db");
+
+  rewinddir(dirp);                         //   Report each auxiliary file
+  while ((dp = readdir(dirp)) != NULL)
+    { name = dp->d_name;
+      dlen = strlen(name);
+#ifdef HIDE_FILES
+      if (name[0] != '.')
+        continue;
+      dlen -= 1;
+      name += 1;
+#endif
+      if (dlen < rlen+1)
+        continue;
+      if (name[rlen] != '.')
+        continue;
+      if (strncmp(name,root,rlen) != 0)
+        continue;
+      foreach(Catenate(pwd,PATHSEP,name,""),name+(rlen+1));
+    }
+  closedir(dirp);
+
+exit:
+  free(pwd);
+  free(root);
+  return (status);
+}
+
+void Print_Read(char *s, int width)
+{ int i;
+
+  if (s[0] < 4)
+    { for (i = 0; s[i] != 4; i++)
+        { if (i%width == 0 && i != 0)
+            printf("\n");
+          printf("%d",s[i]);
+        }
+      printf("\n");
+    }
+  else
+    { for (i = 0; s[i] != '\0'; i++)
+        { if (i%width == 0 && i != 0)
+            printf("\n");
+          printf("%c",s[i]);
+        }
+      printf("\n");
+    }
+}
diff --git a/src/cpp/DB.h b/src/cpp/DB.h
new file mode 100644
index 0000000..16f57ba
--- /dev/null
+++ b/src/cpp/DB.h
@@ -0,0 +1,357 @@
+/************************************************************************************\
+*                                                                                    *
+* Copyright (c) 2014, Dr. Eugene W. Myers (EWM). All rights reserved.                *
+*                                                                                    *
+* Redistribution and use in source and binary forms, with or without modification,   *
+* are permitted provided that the following conditions are met:                      *
+*                                                                                    *
+*  · Redistributions of source code must retain the above copyright notice, this     *
+*    list of conditions and the following disclaimer.                                *
+*                                                                                    *
+*  · Redistributions in binary form must reproduce the above copyright notice, this  *
+*    list of conditions and the following disclaimer in the documentation and/or     *
+*    other materials provided with the distribution.                                 *
+*                                                                                    *
+*  · The name of EWM may not be used to endorse or promote products derived from     *
+*    this software without specific prior written permission.                        *
+*                                                                                    *
+* THIS SOFTWARE IS PROVIDED BY EWM ”AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,    *
+* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND       *
+* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EWM BE LIABLE   *
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  *
+* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY      *
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING     *
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN  *
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                      *
+*                                                                                    *
+* For any issues regarding this software and its use, contact EWM at:                *
+*                                                                                    *
+*   Eugene W. Myers Jr.                                                              *
+*   Bautzner Str. 122e                                                               *
+*   01099 Dresden                                                                    *
+*   GERMANY                                                                          *
+*   Email: gene.myers at gmail.com                                                      *
+*                                                                                    *
+\************************************************************************************/
+
+/*******************************************************************************************
+ *
+ *  Compressed data base module.  Auxiliary routines to open and manipulate a data base for
+ *    which the sequence and read information are separated into two separate files, and the
+ *    sequence is compressed into 2-bits for each base.  Support for tracks of additional
+ *    information, and trimming according to the current partition.  Eventually will also
+ *    support compressed quality information. 
+ *
+ *  Author :  Gene Myers
+ *  Date   :  July 2013
+ *  Revised:  April 2014
+ *
+ ********************************************************************************************/
+
+#ifndef _HITS_DB
+
+#define _HITS_DB
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef unsigned char      uint8;
+typedef unsigned short     uint16;
+typedef unsigned int       uint32;
+typedef unsigned long long uint64;
+typedef signed char        int8;
+typedef signed short       int16;
+typedef signed int         int32;
+typedef signed long long   int64;
+typedef float              float32;
+typedef double             float64;
+
+#define HIDE_FILES          //  Auxiliary DB files start with a . so they are "hidden"
+                            //    Undefine if you don't want this
+
+
+/*******************************************************************************************
+ *
+ *  COMMAND LINE INTERPRETATION MACROS
+ *
+ ********************************************************************************************/
+
+extern char *Prog_Name;   //  Name of program
+
+#define SYSTEM_ERROR							\
+  { fprintf(stderr,"%s: System error, read failed!\n",Prog_Name);	\
+    exit (2);								\
+  }
+
+#define ARG_INIT(name)                  \
+  Prog_Name = Strdup(name,"");          \
+  for (i = 0; i < 128; i++)             \
+    flags[i] = 0;
+
+#define ARG_FLAGS(set)                                                                  \
+  for (k = 1; argv[i][k] != '\0'; k++)                                                  \
+    { if (index(set,argv[i][k]) == NULL)                                                \
+        { fprintf(stderr,"%s: -%c is an illegal option\n",Prog_Name,argv[i][k]);        \
+          exit (1);                                                                     \
+        }                                                                               \
+      flags[(int) argv[i][k]] = 1;                                                      \
+    }
+
+#define ARG_POSITIVE(var,name)                                                          \
+  var = strtol(argv[i]+2,&eptr,10);                                                     \
+  if (*eptr != '\0' || argv[i][2] == '\0')                                              \
+    { fprintf(stderr,"%s: -%c argument is not an integer\n",Prog_Name,argv[i][1]);      \
+      exit (1);                                                                         \
+    }                                                                                   \
+  if (var <= 0)                                                                         \
+    { fprintf(stderr,"%s: %s must be positive (%d)\n",Prog_Name,name,var);              \
+      exit (1);                                                                         \
+    }
+
+#define ARG_NON_NEGATIVE(var,name)                                                      \
+  var = strtol(argv[i]+2,&eptr,10);                                                     \
+  if (*eptr != '\0' || argv[i][2] == '\0')                                              \
+    { fprintf(stderr,"%s: -%c argument is not an integer\n",Prog_Name,argv[i][1]);      \
+      exit (1);                                                                         \
+    }                                                                                   \
+  if (var < 0)	                                                                        \
+    { fprintf(stderr,"%s: %s must be non-negative (%d)\n",Prog_Name,name,var);          \
+      exit (1);                                                                         \
+    }
+
+#define ARG_REAL(var)                                                                   \
+  var = strtod(argv[i]+2,&eptr);                                                        \
+  if (*eptr != '\0' || argv[i][2] == '\0')                                              \
+    { fprintf(stderr,"%s: -%c argument is not a real number\n",Prog_Name,argv[i][1]);   \
+      exit (1);                                                                         \
+    }
+
+/*******************************************************************************************
+ *
+ *  UTILITIES
+ *
+ ********************************************************************************************/
+
+//  The following general utilities return NULL if any of their input pointers are NULL, or if they
+//    could not perform their function (in which case they also print an error to stderr).
+
+void *Malloc(int64 size, char *mesg);                    //  Guarded versions of malloc, realloc
+void *Realloc(void *object, int64 size, char *mesg);     //  and strdup, that output "mesg" to
+char *Strdup(char *string, char *mesg);                  //  stderr if out of memory
+
+FILE *Fopen(char *path, char *mode);     // Open file path for "mode"
+char *PathTo(char *path);                // Return path portion of file name "path"
+char *Root(char *path, char *suffix);    // Return the root name, excluding suffix, of "path"
+
+// Catenate returns concatenation of path.sep.root.suffix in a *temporary* buffer
+// Numbered_Suffix returns concatenation of left.<num>.right in a *temporary* buffer
+
+char *Catenate(char *path, char *sep, char *root, char *suffix);
+char *Numbered_Suffix(char *left, int num, char *right);
+
+
+// DB-related utilities
+
+void Print_Number(int64 num, int width, FILE *out);   //  Print readable big integer
+int  Number_Digits(int64 num);                        //  Return # of digits in printed number
+
+#define COMPRESSED_LEN(len)  (((len)+3) >> 2)
+
+void   Compress_Read(int len, char *s);   //  Compress read in-place into 2-bit form
+void Uncompress_Read(int len, char *s);   //  Uncompress read in-place into numeric form
+void      Print_Read(char *s, int width);
+
+void Lower_Read(char *s);     //  Convert read from numbers to lowercase letters (0-3 to acgt)
+void Upper_Read(char *s);     //  Convert read from numbers to uppercase letters (0-3 to ACGT)
+void Number_Read(char *s);    //  Convert read from letters to numbers
+
+
+/*******************************************************************************************
+ *
+ *  DB IN-CORE DATA STRUCTURES
+ *
+ ********************************************************************************************/
+
+#define DB_CSS  0x0400   //  This is the second or later of a group of reads from a given insert
+#define DB_BEST 0x0800   //  This is the longest read of a given insert (may be the only 1)
+
+typedef struct
+  { int     origin; //  Well #
+    int     rlen;   //  Length of the sequence (Last pulse = fpulse + rlen)
+    int     fpulse; //  First pulse
+    int64   boff;   //  Offset (in bytes) of compressed read in 'bases' file, or offset of
+                    //    uncompressed bases in memory block
+    int64   coff;   //  Offset (in bytes) of compressed quiva streams in 'quiva' file
+    int     flags;  //  QV of read + flags above
+  } HITS_READ;
+
+//  A track can be of 3 types:
+//    data == NULL: there are nreads+1 'anno' records of size 'size'.
+//    data != NULL && size == 4: anno is an array of nreads+1 int's and data[anno[i]..anno[i+1])
+//                                    contains the variable length data
+//    data != NULL && size == 8: anno is an array of nreads+1 int64's and data[anno[i]..anno[i+1])
+//                                    contains the variable length data
+
+typedef struct _track
+  { struct _track *next;  //  Link to next track
+    char          *name;  //  Symbolic name of track
+    int            size;  //  Size in bytes of anno records
+    void          *anno;  //  over [0,nreads]: read i annotation: int, int64, or 'size' records 
+    void          *data;  //     data[anno[i] .. anno[i+1]-1] is data if data != NULL
+  } HITS_TRACK;
+
+
+//  The DB record holds all information about the current state of an active DB including an
+//    array of HITS_READS, one per read, and a linked list of HITS_TRACKs the first of which
+//    is always a HITS_QV pseudo-track (if the QVs have been loaded).
+
+typedef struct
+  { int         oreads;     //  Total number of reads in DB
+    int         breads;     //  Total number of reads in trimmed DB (if trimmed set)
+    int         cutoff;     //  Minimum read length in block (-1 if not yet set)
+    int         all;        //  Consider multiple reads from a given well
+    float       freq[4];    //  frequency of A, C, G, T, respectively
+
+    //  Set with respect to "active" part of DB (all vs block, untrimmed vs trimmed)
+
+    int         maxlen;     //  length of maximum read (initially over all DB)
+    int64       totlen;     //  total # of bases (initially over all DB)
+
+    int         nreads;     //  # of reads in actively loaded portion of DB
+    int         trimmed;    //  DB has been trimmed by cutoff/all
+    int         part;       //  DB block (if > 0), total DB (if == 0)
+    int         ofirst;     //  Index of first read in block (without trimming)
+    int         bfirst;     //  Index of first read in block (with trimming)
+
+    char       *path;       //  Root name of DB for .bps and tracks
+    int         loaded;     //  Are reads loaded in memory?
+    void       *bases;      //  file pointer for bases file (to fetch reads from),
+                            //    or memory pointer to uncompressed block of all sequences.
+    HITS_READ  *reads;      //  Array [0..nreads] of HITS_READ
+    HITS_TRACK *tracks;     //  Linked list of loaded tracks
+  } HITS_DB; 
+
+
+/*******************************************************************************************
+ *
+ *  DB STUB FILE FORMAT = NFILE FDATA^nfile NBLOCK PARAMS BDATA^nblock
+ *
+ ********************************************************************************************/
+
+#define MAX_NAME 10000      //  Longest file name or fasta header line
+
+#define DB_NFILE  "files = %9d\n"   //  number of files
+#define DB_FDATA  "  %9d %s %s\n"   //  last read index + 1, fasta prolog, file name
+#define DB_NBLOCK "blocks = %9d\n"  //  number of blocks
+#define DB_PARAMS "size = %9lld cutoff = %9d all = %1d\n"  //  block size, len cutoff, all in well
+#define DB_BDATA  " %9d %9d\n"      //  First read index (untrimmed), first read index (trimmed)
+
+
+/*******************************************************************************************
+ *
+ *  DB ROUTINES
+ *
+ ********************************************************************************************/
+
+  // Suppose DB is the name of an original database.  Then there will be files .DB.idx, .DB.bps,
+  //    .DB.qvs, and files .DB.<track>.anno and DB.<track>.data where <track> is a track name
+  //    (not containing a . !).
+
+  // A DAM is basically a DB except that:
+  //    1. there are no QV's, instead .coff points the '\0' terminated fasta header of the read
+  //          in the file .<dam>.hdr file
+  //    2. .origin contains the contig # of the read within a fasta entry (assembly sequences
+  //          contain N-separated contigs), and .fpulse the first base ofn the contig in the
+  //          fasta entry
+
+  // Open the given database or dam, "path" into the supplied HITS_DB record "db". If the name has
+  //   a part # in it then just the part is opened.  The index array is allocated (for all or
+  //   just the part) and read in.
+  // Return status of routine:
+  //    -1: The DB could not be opened for a reason reported by the routine to stderr
+  //     0: Open of DB proceeded without mishap
+  //     1: Open of DAM proceeded without mishap
+
+int Open_DB(char *path, HITS_DB *db);
+
+  // Trim the DB or part thereof and all loaded tracks according to the cuttof and all settings
+  //   of the current DB partition.  Reallocate smaller memory blocks for the information kept
+  //   for the retained reads.
+
+void Trim_DB(HITS_DB *db);
+
+  // Shut down an open 'db' by freeing all associated space, including tracks and QV structures,
+  //   and any open file pointers.  The record pointed at by db however remains (the user
+  //   supplied it and so should free it).
+
+void Close_DB(HITS_DB *db);
+
+  // Look up the file and header in the file of the indicated track.  Return:
+  //     1: Track is for trimmed DB
+  //     0: Track is for untrimmed DB
+  //    -1: Track is not the right size of DB either trimmed or untrimmed
+  //    -2: Could not find the track
+
+int Check_Track(HITS_DB *db, char *track);
+
+  // If track is not already in the db's track list, then allocate all the storage for it,
+  //   read it in from the appropriate file, add it to the track list, and return a pointer
+  //   to the newly created HITS_TRACK record.  If the track does not exist or cannot be
+  //   opened for some reason, then NULL is returned.
+
+HITS_TRACK *Load_Track(HITS_DB *db, char *track);
+
+  // If track is on the db's track list, then it is removed and all storage associated with it
+  //   is freed.
+
+void Close_Track(HITS_DB *db, char *track);
+
+  // Allocate and return a buffer big enough for the largest read in 'db'.
+  // **NB** free(x-1) if x is the value returned as *prefix* and suffix '\0'(4)-byte
+  // are needed by the alignment algorithms.
+
+char *New_Read_Buffer(HITS_DB *db);
+
+  // Load into 'read' the i'th read in 'db'.  As a lower case ascii string if ascii is 1, an
+  //   upper case ascii string if ascii is 2, and a numeric string over 0(A), 1(C), 2(G), and 3(T)
+  //   otherwise.  A '\0' (or 4) is prepended and appended to the string so it has a delimeter
+  //   for traversals in either direction.
+
+void  Load_Read(HITS_DB *db, int i, char *read, int ascii);
+
+  // Load into 'read' the subread [beg,end] of the i'th read in 'db' and return a pointer to the
+  //   the start of the subinterval (not necessarily = to read !!! ).  As a lower case ascii
+  //   string if ascii is 1, an upper case ascii string if ascii is 2, and a numeric string
+  //   over 0(A), 1(C), 2(G), and 3(T) otherwise.  A '\0' (or 4) is prepended and appended to
+  //   the string holding the substring so it has a delimeter for traversals in either direction.
+
+char *Load_Subread(HITS_DB *db, int i, int beg, int end, char *read, int ascii);
+
+  // Allocate a block big enough for all the uncompressed sequences, read them into it,
+  //   reset the 'off' in each read record to be its in-memory offset, and set the
+  //   bases pointer to point at the block after closing the bases file.  If ascii is
+  //   1 then the reads are converted to lowercase ascii, if 2 then uppercase ascii, and
+  //   otherwise the reads are left as numeric strings over 0(A), 1(C), 2(G), and 3(T).
+
+void Read_All_Sequences(HITS_DB *db, int ascii);
+
+  // For the DB or DAM "path" = "prefix/root[.db|.dam]", find all the files for that DB, i.e. all
+  //   those of the form "prefix/[.]root.part" and call foreach with the complete path to each file
+  //   pointed at by path, and the suffix of the path by extension.  The . proceeds the root
+  //   name if the defined constant HIDE_FILES is set.  Always the first call is with the
+  //   path "prefix/root.db" and extension "db".  There will always be calls for
+  //   "prefix/[.]root.idx" and "prefix/[.]root.bps".  All other calls are for *tracks* and
+  //   so this routine gives one a way to know all the tracks associated with a given DB.
+  //   Return non-zero iff path could not be opened for any reason.
+
+int List_DB_Files(char *path, void foreach(char *path, char *extension));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _HITS_DB
diff --git a/src/cpp/DazAlnProvider.cpp b/src/cpp/DazAlnProvider.cpp
new file mode 100644
index 0000000..0300734
--- /dev/null
+++ b/src/cpp/DazAlnProvider.cpp
@@ -0,0 +1,452 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+#include <stdlib.h>
+#include <cstring>
+#include <iostream>
+#include <sstream>
+#include <cstdlib>
+#include <cstdio>
+#include <algorithm>
+#include <numeric>
+#include <set>
+#include "DazAlnProvider.hpp"
+
+#undef DEBUG
+
+using namespace PacBio::DagCon; // for IOException
+
+IOException::IOException(const char* desc) : desc_(desc) {}
+
+static char ToU[8] = { 'A', 'C', 'G', 'T', '.', '[', ']', '-' };
+static int BORDER = 10;
+
+// Should write my own, but for reasons of expediency, this borrows heavily 
+// from LAshow.c
+DazAlnProvider::DazAlnProvider(const ProgramOpts& popts) :
+    popts_(popts),
+    novl_(0),
+    covl_(0) {
+
+    // Initialize the sequence DB accessor
+
+    // Open_DB takes a mutable char pointer, booo
+    char* path = new char[popts_.seqFile.length()+1];
+    std::strcpy(path,popts_.seqFile.c_str());
+    path[popts_.seqFile.length()] = '\0';
+    int status = Open_DB(path, &db_);
+    if (status < 0)
+        throw IOException("Failed to open DB");
+
+    delete[] path;
+
+    Trim_DB(&db_);
+
+    // Initialize the alignment file reader
+    input_ = fopen(popts_.alnFile.c_str(),"r");
+    if (input_ == NULL) {
+        std::string msg = "Open failed: " + popts_.alnFile;
+        throw IOException(msg.c_str());
+    }
+
+    if (fread(&novl_,sizeof(int64),1,input_) != 1)
+        throw IOException("Failed to read novl");
+
+    int tspace;
+    if (fread(&tspace,sizeof(int),1,input_) != 1)
+        throw IOException("Failed to read tspace");
+
+    int small;
+    if (tspace <= TRACE_XOVR) { 
+        small  = 1;
+        tbytes_ = sizeof(uint8);
+    } else { 
+        small  = 0;
+        tbytes_ = sizeof(uint16);
+    }
+
+    trg_ = new Target(db_, tspace, small);
+    targSeqBuf_ = New_Read_Buffer(&db_);
+
+    // Read in the first record
+    nextRecord(prevRec_);
+}
+
+DazAlnProvider::~DazAlnProvider() {
+    fclose(input_);
+    Close_DB(&db_);
+    free(targSeqBuf_-1);
+    delete trg_;
+}
+
+bool DazAlnProvider::nextTarget(std::vector<dagcon::Alignment> &dest) {
+    dest.clear();
+
+    // constructor initializes the first prevRec_ struct
+    trg_->firstRecord(prevRec_, popts_.properOvls);
+
+    std::set<int> tfilt = popts_.targets;
+    unsigned int filter = tfilt.size();
+    bool skipTarget = false;
+    while(covl_++ < novl_) {
+        Record rec;
+        nextRecord(rec);
+
+        if (rec.ovl.aread != trg_->id || covl_ == novl_) {
+            int tid = trg_->id + 1;
+            if (filter == 0 || tfilt.find(tid) != tfilt.end()) {
+                trg_->getAlignments(dest, popts_.maxHits, popts_.sortCov);
+                if (dest.size() < popts_.minCov) {
+                    dest.clear();
+                    skipTarget = true; 
+                }
+            } else {
+                skipTarget = true;
+            }
+
+            if (skipTarget && covl_ != novl_) {
+                trg_->firstRecord(rec, popts_.properOvls);
+                skipTarget = false;
+                continue;
+            }
+            // This will initialize the target object on the next call
+            prevRec_ = rec;
+            break;
+        }
+        trg_->addRecord(rec, popts_.properOvls);
+    }
+    
+    return covl_ != novl_;
+}
+
+bool DazAlnProvider::nextTarget(std::string& targSeq, std::vector<dagcon::Alignment>& dest) {
+     
+    bool hasNext = nextTarget(dest);
+
+    targSeq.resize(trg_->length);
+    char* seq;
+    seq = Load_Subread(&db_, trg_->id, 0, trg_->length, targSeqBuf_, 0);
+
+    int i;
+    for (i = 0; i < trg_->length; i++)
+        targSeq[i] = ToU[(int)seq[i]];
+    
+    return hasNext;
+}
+
+void DazAlnProvider::nextRecord(Record& rec) {
+    Read_Overlap(input_,&rec.ovl);
+    int tmax = ((int)1.2*rec.ovl.path.tlen) + 100; 
+    rec.trace.resize(tmax,0);
+    rec.ovl.path.trace = (void *) &rec.trace.front();
+    Read_Trace(input_, &rec.ovl, tbytes_);
+}
+
+TargetHit::TargetHit() : 
+    ovlScore(0.0f),
+    covScore(0.0f),
+    aread(-1),
+    bread(0),
+    flags(0) {}
+
+TargetHit::TargetHit(Record& rec) : TargetHit() {
+    aread = rec.ovl.aread;
+    bread = rec.ovl.bread;
+    flags = rec.ovl.flags;
+    add(rec);
+}
+
+std::ostream& operator<<(std::ostream& ostrm, TargetHit& hit) {
+    ostrm << "target: " << (hit.aread+1) << " query: " << (hit.bread+1);
+    ostrm << " flags: " << hit.flags << " tstart: " << hit.abeg();
+    ostrm << " tend: " << hit.aend() << " gaps: ";
+    ostrm << hit.records.size() << " ovlscore: " << hit.ovlScore;
+    ostrm << " covScore: " << hit.covScore << std::endl;
+    return ostrm;
+}
+
+bool TargetHit::belongs(Overlap& ovl) {
+    return aread == ovl.aread &&
+           bread == ovl.bread &&
+           flags == ovl.flags;
+}
+
+void TargetHit::add(Record& rec) {
+    if (records.size() == 0) {
+        records.push_back(std::move(rec));
+    } else {
+        Path prev = records.back().ovl.path;
+        int prevLen = prev.aepos - prev.abpos;
+
+        Path curr = rec.ovl.path;
+        int currLen = curr.aepos - curr.abpos;
+
+        if (curr.abpos > prev.aepos) {
+            records.push_back(std::move(rec));
+        } else if (currLen > prevLen) {
+            records.pop_back();
+            records.push_back(std::move(rec));
+        }
+    }
+}
+
+void TargetHit::computeOvlScore(bool proper) {
+    int ahlen = 0, bhlen = 0, diff = 0;
+
+    // XXX: penalize for gaps between records?
+    for (auto& rec: records) {
+        Path p = rec.ovl.path;
+        ahlen += p.aepos - p.abpos;
+        bhlen += p.bepos - p.bbpos;
+        diff += std::abs(ahlen - bhlen) + p.diffs; 
+    }
+
+    ovlScore = (1 - diff/(float)ahlen) * ahlen;
+
+    if (proper) {
+        const Path& f = records.front().ovl.path;
+        const Path& b = records.back().ovl.path; 
+        if (f.abpos != 0 && b.bbpos != 0) 
+            ovlScore = 0.0f;
+        if (f.aepos != alen && b.bepos != blen)
+            ovlScore = 0.0f;
+    }
+}
+
+int TargetHit::abeg() {
+    return records.front().ovl.path.abpos;
+}
+
+int TargetHit::aend() {
+    return records.back().ovl.path.aepos;
+}
+
+// Simplify unit testing, don't burden with malloc'd 
+// daligner structures.
+Target::Target(): needsFree_(false) { }
+
+Target::Target(HITS_DB& db, int tspace, int small) : 
+    db_(db), 
+    tspace_(tspace), 
+    small_(small),
+    needsFree_(true) {
+    
+    work_ = New_Work_Data();
+    abuffer_ = New_Read_Buffer(&db_);
+    bbuffer_ = New_Read_Buffer(&db_);
+}
+
+Target::~Target() {
+    if (needsFree_) {
+        free(abuffer_-1);
+        free(bbuffer_-1);
+        Free_Work_Data(work_);
+    }
+}
+
+void Target::firstRecord(Record& rec, bool proper) {
+    id = rec.ovl.aread;
+    length = db_.reads[id].rlen;
+
+    hits.clear();
+    coverage_.clear();
+
+    if (coverage_.size() < (unsigned int) length)
+        coverage_.resize(length);
+   
+    auto beg = coverage_.begin();
+    std::for_each(beg, beg+length, [](unsigned int& x){x=0;});
+
+    TargetHit hit(rec);
+    hit.alen = length;
+    hit.blen = db_.reads[rec.ovl.bread].rlen;
+    hit.computeOvlScore(proper);
+    hits.push_back(std::move(hit));
+}
+
+void Target::addRecord(Record& rec, bool proper) {
+    // collapse into target hits, maintaining only the best hit from either
+    // forward or reverse.
+    if (hits.size() > 0) {
+        TargetHit& prev = hits.back();
+
+        // Add overlap to previous hit, instead of storing a new one
+        if (prev.belongs(rec.ovl)) {
+            prev.add(rec);
+            prev.computeOvlScore(proper);
+            return;
+        }
+    }
+
+    TargetHit hit(rec);
+    hit.alen = length;
+    hit.blen = db_.reads[rec.ovl.bread].rlen;
+    hit.computeOvlScore(proper);
+    hits.push_back(std::move(hit));
+}
+
+void Target::sortHits(bool sortCov) {
+    // sort descending based on ovl length x percent id
+    std::sort(hits.begin(), hits.end(), cmpHitOvlScore);
+
+    if (! sortCov) return;
+
+    // Coverage based scoring
+    for (auto& hit : hits) {
+        for (auto const& rec : hit.records) {
+            auto beg = coverage_.begin() + rec.ovl.path.abpos;
+            auto end = coverage_.begin() + rec.ovl.path.aepos;
+            std::for_each(beg, end, [](unsigned int& x){++x;});
+            hit.covScore = std::accumulate(beg, end, 0.0, invertedSum);
+        }
+    }
+
+    std::sort(hits.begin(), hits.end(), cmpHitCovScore);
+}
+
+void Target::getAlignments(std::vector<dagcon::Alignment> &alns, unsigned int max, bool sortCov) {
+    sortHits(sortCov);
+    auto hbeg = hits.begin();
+    auto hend = hits.size() > max ? hbeg + max : hits.end();
+    for (auto& hit = hbeg; hit != hend; ++hit) {
+        //std::cerr << *hit;
+        for (auto const& rec : hit->records) {
+            Overlap ovl = rec.ovl;
+            char* aseq, *bseq;
+            int amin, amax;
+            int bmin, bmax;
+            Alignment aln;
+            aln.path = &(ovl.path);
+            aln.alen  = length;
+            aln.blen  = hit->blen;
+            aln.flags = ovl.flags;
+
+            if (small_)
+                Decompress_TraceTo16(&ovl);
+
+            amin = ovl.path.abpos - BORDER;
+            if (amin < 0) amin = 0;
+            amax = ovl.path.aepos + BORDER;
+            if (amax > aln.alen) amax = aln.alen;
+            if (COMP(aln.flags)) { 
+                bmin = (aln.blen-ovl.path.bepos) - BORDER;
+                if (bmin < 0) bmin = 0;
+                bmax = (aln.blen-ovl.path.bbpos) + BORDER;
+                if (bmax > aln.blen) bmax = aln.blen;
+            } else { 
+                bmin = ovl.path.bbpos - BORDER;
+                if (bmin < 0) bmin = 0;
+                bmax = ovl.path.bepos + BORDER;
+                if (bmax > aln.blen) bmax = aln.blen;
+            }
+
+            // XXX: potential optimization, cache reads
+            aseq = Load_Subread(&db_, ovl.aread, amin, amax, abuffer_, 0);
+            bseq = Load_Subread(&db_, ovl.bread, bmin, bmax, bbuffer_, 0);
+
+            aln.aseq = aseq - amin;
+            if (COMP(aln.flags)) { 
+                Complement_Seq(bseq,bmax-bmin);
+                aln.bseq = bseq - (aln.blen - bmax);
+            } else
+                aln.bseq = bseq - bmin;
+
+            Compute_Trace_PTS(&aln, work_, tspace_);
+
+            // initialize the dagcon alignment class
+            dagcon::Alignment dest;
+            std::stringstream tid, qid;
+            tid << (ovl.aread+1);
+            qid << (ovl.bread+1);
+            dest.id = tid.str();
+            dest.sid = qid.str();
+            dest.tlen = aln.alen;
+
+            dest.start = ovl.path.abpos+1;
+            dest.end = ovl.path.aepos+1;
+
+            decodeAlignment(&aln, dest);
+            alns.push_back(dest);
+        }
+    }
+}
+
+bool cmpHitOvlScore(const TargetHit& l, const TargetHit& r) {
+    return l.ovlScore > r.ovlScore;
+}
+
+bool cmpHitCovScore(const TargetHit& l, const TargetHit& r) {
+    return l.covScore > r.covScore;
+}
+
+float invertedSum(float x, unsigned int y) {
+    return x + 1/(float)y;
+}
+
+void decodeAlignment(Alignment* src, dagcon::Alignment& dest) {
+    int i, j, tlen, c, p;
+    char* a, *b; // pointers to the sequence
+    int* trace = (int *) src->path->trace;
+    a = src->aseq;
+    b = src->bseq;
+    tlen = src->path->tlen;
+    i = src->path->abpos;
+    j = src->path->bbpos;
+    // XXX: slow. pre-allocate string length or work directly with
+    // encoded strings.
+    for (c = 0; c < tlen; c++) {
+        if ((p = trace[c]) < 0) {
+            p = -p;
+            while (i != p) {
+                dest.tstr += ToU[(int)a[i++]];
+                dest.qstr += ToU[(int)b[j++]];
+            }
+            dest.tstr += ToU[7];
+            dest.qstr += ToU[(int)b[j++]];
+        } else {
+            while (j != p) {
+                dest.tstr += ToU[(int)a[i++]];
+                dest.qstr += ToU[(int)b[j++]];
+            }
+            dest.tstr += ToU[(int)a[i++]];
+            dest.qstr += ToU[7];
+        }
+    }
+    p = src->path->aepos;
+    while(i <= p) {
+        dest.tstr += ToU[(int)a[i++]];
+        dest.qstr += ToU[(int)b[j++]];
+    }
+}
diff --git a/src/cpp/DazAlnProvider.hpp b/src/cpp/DazAlnProvider.hpp
new file mode 100644
index 0000000..a059c51
--- /dev/null
+++ b/src/cpp/DazAlnProvider.hpp
@@ -0,0 +1,222 @@
+
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#ifndef __GCON_DAZ_ALN_PROVIDER__
+#define __GCON_DAZ_ALN_PROVIDER__
+
+#include <stdio.h>
+#include <string>
+#include <vector>
+#include <iostream>
+#include <atomic>
+#include "ProgramOpts.hpp"
+#include "Alignment.hpp"
+#include "AlnProvider.hpp"
+
+// Dazzler headers
+#include "DB.h"
+#include "align.h"
+
+// Represents one record from the LAS file, essentially a thin container for
+// a dazzler overlap so we can manage things on the stack.
+struct Record {
+    Overlap ovl;
+    std::vector<uint16> trace;
+
+    Record() {}
+    ~Record() {}
+
+    Record(Record &&o) noexcept : 
+        ovl(std::move(o.ovl)), 
+        trace(std::move(o.trace)) {
+        o.ovl.path.trace = NULL;
+    }
+
+    Record& operator=(Record &&o) noexcept {
+        ovl = std::move(o.ovl);
+        trace = std::move(o.trace);
+        o.ovl.path.trace = NULL;
+        return *this;
+    }
+
+    Record(const Record& o) : ovl(o.ovl), trace(o.trace) {
+        ovl.path.trace = (void *) &trace.front();
+    }
+
+    Record& operator=(const Record& o) {
+        ovl = o.ovl;
+        trace = o.trace;
+        ovl.path.trace = (void *) &trace.front();
+        return *this;
+    }
+
+};
+
+// Holds information for all the a,b overlaps in a particular direction,
+// either forward or reverse. Overlaps for a particular a,b,strand 
+// combination may come in as multiple overlaps.  This class allows us to
+// handle them as a unit.
+class TargetHit {
+public:
+    TargetHit();
+    TargetHit(Record& rec);
+
+    // Returns true if given overlap belongs to this hit, otherwise false
+    bool belongs(Overlap& ovl);
+
+    // Adds the next overlap to this hit set (a,b,strand combination)
+    void add(Record& rec);
+
+    void computeOvlScore(bool proper=false);
+
+    int abeg();
+    int aend();
+
+    int alen;
+    int blen;
+
+    // Tracks the overlap score, (pct id) x (a-read aln len)
+    float ovlScore;
+
+    // Tracks the coverage score sum(1/depth for aln len)
+    float covScore;
+
+    // information promoted from the overlap struct
+    int aread;
+    int bread;
+    uint32 flags;
+
+    // container holding records related to this hit
+    std::vector<Record> records;
+};
+
+std::ostream& operator<<(std::ostream& ostrm, TargetHit& hit);
+
+// Re-usable class container for a target and its overlaps
+class Target {
+public:
+    Target();
+    Target(HITS_DB& db, int tspace, int small);
+    ~Target();
+
+    // Initializes this target based on the given record, possibly scoring as
+    // a 'proper' overlap (more stringent).
+    void firstRecord(Record& rec, bool proper=false);
+
+    // Adds the next overlap record to this target, possibly scoring as 
+    // a 'proper' overlap (more stringent).
+    void addRecord(Record& rec, bool proper=false);
+
+    // Sorts overlaps based on a two-phase scoring system
+    void sortHits(bool sortCov);
+
+    void getAlignments(std::vector<dagcon::Alignment> &alns, unsigned int max, bool sortCov);
+
+    // ID of the target
+    int id;
+
+    // Length of the target
+    int length; 
+
+    std::vector<TargetHit> hits;
+
+private:
+    HITS_DB db_;
+    char* abuffer_, *bbuffer_;
+    Work_Data* work_;
+    int tspace_, small_;
+    std::vector<unsigned int> coverage_;
+    bool needsFree_; // track if we need to free memory on destruct
+};
+
+///
+/// Provides sets of alignments for a given target sequence from a daligner
+/// output file.  
+///
+class DazAlnProvider : public AlnProvider {
+public:
+    /// Constructs a new alignment provider.  Checks the format of the file and
+    /// throws an exception if it's malformed.
+    /// \param popts options passed to the program on the command line
+    DazAlnProvider(const ProgramOpts& popts);
+
+    /// Cleans up some stuff.
+    ~DazAlnProvider();
+
+    /// Gets the set of alignments for the next target and puts them into the
+    /// given vector.  Note this function will clear the contents of the vector
+    /// prior to adding the next set of alignments.
+    /// In dazzler parlance, this will correct the A reads as targets.
+    /// \param dest reference to a vector to hold the alignments.
+    /// \return True if there are more targets, otherwise false.
+    bool nextTarget(std::vector<dagcon::Alignment>& dest);
+
+    bool nextTarget(std::string& targSeq, std::vector<dagcon::Alignment>& dest);
+
+private:
+    // maintains the previous record
+    Record prevRec_;
+    Target* trg_;
+    const ProgramOpts popts_;
+
+    // Dazzler-related data 
+    HITS_DB db_;
+    int64 novl_, covl_;
+    int tbytes_;
+    FILE* input_;
+    char* targSeqBuf_;
+
+    /// Obtain data from the las file for the next overlap record
+    void nextRecord(Record& rec);
+
+};
+
+
+/// Compares the hits based on (percent id) x (query alignment length)
+bool cmpHitOvlScore(const TargetHit& l, const TargetHit& r); 
+
+/// Compares based on coverage score
+bool cmpHitCovScore(const TargetHit& l, const TargetHit& r); 
+
+float invertedSum(float x, unsigned int y);
+
+/// Convert dazzler alignment into a dagcon alignment. Eventually, we
+/// should update the alignment graph to process the dazzler alignment
+/// directly, but this will be useful for debugging purposes.
+void decodeAlignment(Alignment* src, dagcon::Alignment& dest);
+
+#endif //__GCON_DAZ_ALN_PROVIDER__
diff --git a/src/cpp/Makefile b/src/cpp/Makefile
new file mode 100644
index 0000000..f1e7099
--- /dev/null
+++ b/src/cpp/Makefile
@@ -0,0 +1,38 @@
+include pbi.mk
+include boost.mk
+
+COMMON_OBJECTS := Alignment.o AlnGraphBoost.o
+PBDAGCON_OBJECTS := BlasrM5AlnProvider.o main.o SimpleAligner.o
+DAZCON_OBJECTS := DB.o align.o DazAlnProvider.o dazcon.o
+
+CXXFLAGS = -O3 -std=c++11 -Wall -Wuninitialized -pedantic -I third-party \
+		   -I $(BOOST_HEADERS)
+
+CFLAGS = -O3 -Wall -Wextra -fno-strict-aliasing
+
+INCDIRS := -I$(PBDATA) -I$(BLASR) $(EXTRA_INCDIRS)
+LDFLAGS := -L$(PBDATA) -L$(BLASR) $(EXTRA_LDFLAGS)
+
+all: pbdagcon
+
+dazcon: LDLIBS = -lpthread
+dazcon: $(COMMON_OBJECTS) $(DAZCON_OBJECTS)
+	$(CXX) -Wl,--no-as-needed -o $@ $^ $(LDLIBS)
+
+pbdagcon: LDLIBS = -lpbdata -lblasr -lpthread $(EXTRA_LDLIBS)
+pbdagcon: CXXFLAGS += $(INCDIRS)
+pbdagcon: $(COMMON_OBJECTS) $(PBDAGCON_OBJECTS)
+	$(CXX) -Wl,--no-as-needed $(LIBDIRS) -o $@ $^ $(LDFLAGS) $(LDLIBS)
+
+$(COMMON_OBJECTS): $(BOOST_HEADERS)
+
+$(BOOST_HEADERS):
+	cd third-party && $(GET_BOOST)
+
+clean:
+	$(RM) *.d
+	$(RM) *.o
+	$(RM) pbdagcon
+	$(RM) dazcon
+
+.PHONY: all clean
diff --git a/src/cpp/ProgramOpts.hpp b/src/cpp/ProgramOpts.hpp
new file mode 100644
index 0000000..45242ad
--- /dev/null
+++ b/src/cpp/ProgramOpts.hpp
@@ -0,0 +1,39 @@
+#ifndef __GCON_PROGRAMOPTS__
+#define __GCON_PROGRAMOPTS__
+
+#include <string>
+#include <set>
+
+// Normalizing *slightly* difference cmd line interfaces between pbdagcon and
+// dazcon.  May unify someday ...
+struct ProgramOpts {
+    // Common to both pbdagcon and dazcon
+    /// Minimum alignment coverage for consensus
+    size_t minCov;
+    /// Minimum consensus length to output
+    size_t minLen;
+    /// Amount to trim alignments by on either side.
+    unsigned int trim;
+    /// Number of threads to use
+    int threads;
+
+    // Specific to pbdagcon
+    bool align;
+    std::string input;
+
+    // Specific to dazcon
+    /// Path to the alignment file
+    std::string alnFile;
+    /// Path to the sequence file
+    std::string seqFile;
+    /// Maximimum number of hits to include in correction
+    unsigned int maxHits;
+    /// Sort hits by coverage score
+    bool sortCov;
+    /// Use only proper overlaps for correction
+    bool properOvls;
+    /// Limit correction to these targets
+    std::set<int> targets;
+};
+
+#endif // __GCON_PROGRAMOPTS__
diff --git a/src/cpp/SimpleAligner.cpp b/src/cpp/SimpleAligner.cpp
new file mode 100644
index 0000000..6086187
--- /dev/null
+++ b/src/cpp/SimpleAligner.cpp
@@ -0,0 +1,103 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#include <vector>
+#include <stdint.h>
+#include <cstring>
+#include <string>
+#include <cassert>
+#include <algorithm>
+#include "Alignment.hpp"
+#include "SimpleAligner.hpp"
+
+SimpleAligner::SimpleAligner() {
+    config_.indelRate = 0.3;
+    config_.indel = 5;
+    config_.match = 0;
+    config_.sdpIndel = 5;
+    config_.sdpIns = 5;
+    config_.sdpDel = 10;
+    config_.kmer = 11;
+    config_.bandSize = 10;
+    tupleMetrics_.Initialize(config_.kmer);
+    distScoreFn_.del = config_.indel;
+    distScoreFn_.ins = 4;
+    distScoreFn_.InitializeScoreMatrix(SMRTDistanceMatrix);
+}
+
+void SimpleAligner::align(dagcon::Alignment& aln) {
+    // This alignment type defined in blasr code base
+    blasr::Alignment initialAln, refinedAln;
+    FASTQSequence query;
+    query.seq = (Nucleotide*)aln.qstr.c_str();
+    query.length = aln.qstr.length();
+
+    DNASequence target;
+    target.seq = (Nucleotide*)aln.tstr.c_str();
+    target.length = aln.tstr.length();
+    SDPAlign(query, target, distScoreFn_, tupleMetrics_.tupleSize,
+             config_.sdpIndel, config_.sdpIndel, config_.indelRate*2,
+             initialAln, Local);
+
+    GuidedAlign(query, target, initialAln, distScoreFn_, 
+        config_.bandSize, refinedAln);
+
+    std::string queryStr, alignStr, targetStr;
+
+    //StickPrintAlignment(initialAln, query, target, std::cout);
+    //StickPrintAlignment(refinedAln, query, target, std::cout);
+
+    CreateAlignmentStrings(refinedAln, query.seq, target.seq, 
+            targetStr, alignStr, queryStr, query.length, target.length);
+
+    // alignment coordinates may change, update alignment object
+    aln.start += refinedAln.GenomicTBegin();
+    aln.end = aln.start + refinedAln.GenomicTEnd();
+
+    if (aln.strand == '-') {
+        aln.start = aln.tlen - aln.end;
+        aln.qstr = revComp(queryStr);
+        aln.tstr = revComp(targetStr);
+    } else {
+        aln.qstr = queryStr;
+        aln.tstr = targetStr;
+    }
+    aln.start++;
+}
+
+void SimpleAligner::operator() (dagcon::Alignment& aln) {
+    align(aln);
+}
diff --git a/src/cpp/SimpleAligner.hpp b/src/cpp/SimpleAligner.hpp
new file mode 100644
index 0000000..209b0dc
--- /dev/null
+++ b/src/cpp/SimpleAligner.hpp
@@ -0,0 +1,73 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#ifndef __GCON_SIMPLE_ALIGNER__
+#define __GCON_SIMPLE_ALIGNER__
+#include "Types.h"
+#include "Enumerations.h"
+#include "DNASequence.hpp"
+#include "datastructures/alignment/Alignment.hpp"
+#include "algorithms/alignment/AlignmentUtils.hpp"
+#include "algorithms/alignment/SDPAlign.hpp"
+#include "algorithms/alignment/GuidedAlign.hpp"
+#include "format/StickAlignmentPrinter.hpp"
+#include "FASTQSequence.hpp"
+
+namespace Aligner {
+struct Config {
+    float indelRate;
+    int indel;
+    int match;
+    int sdpIndel;
+    int sdpIns;
+    int sdpDel;
+    int kmer;
+    int bandSize;
+};
+}
+
+class SimpleAligner {
+public:
+    SimpleAligner();
+    void align(dagcon::Alignment& aln); 
+    void operator() (dagcon::Alignment& aln);
+private:
+    Aligner::Config config_;
+    TupleMetrics tupleMetrics_;
+    DistanceMatrixScoreFunction<DNASequence, FASTQSequence> distScoreFn_;
+};
+
+#endif // __GCON_SIMPLE_ALIGNER__
diff --git a/src/cpp/align.c b/src/cpp/align.c
new file mode 100644
index 0000000..b60c6c0
--- /dev/null
+++ b/src/cpp/align.c
@@ -0,0 +1,3805 @@
+/************************************************************************************\
+*                                                                                    *
+* Copyright (c) 2014, Dr. Eugene W. Myers (EWM). All rights reserved.                *
+*                                                                                    *
+* Redistribution and use in source and binary forms, with or without modification,   *
+* are permitted provided that the following conditions are met:                      *
+*                                                                                    *
+*  · Redistributions of source code must retain the above copyright notice, this     *
+*    list of conditions and the following disclaimer.                                *
+*                                                                                    *
+*  · Redistributions in binary form must reproduce the above copyright notice, this  *
+*    list of conditions and the following disclaimer in the documentation and/or     *
+*    other materials provided with the distribution.                                 *
+*                                                                                    *
+*  · The name of EWM may not be used to endorse or promote products derived from     *
+*    this software without specific prior written permission.                        *
+*                                                                                    *
+* THIS SOFTWARE IS PROVIDED BY EWM ”AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,    *
+* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND       *
+* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EWM BE LIABLE   *
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  *
+* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY      *
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING     *
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN  *
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                      *
+*                                                                                    *
+* For any issues regarding this software and its use, contact EWM at:                *
+*                                                                                    *
+*   Eugene W. Myers Jr.                                                              *
+*   Bautzner Str. 122e                                                               *
+*   01099 Dresden                                                                    *
+*   GERMANY                                                                          *
+*   Email: gene.myers at gmail.com                                                      *
+*                                                                                    *
+\************************************************************************************/
+
+/*******************************************************************************************
+ *
+ *  Fast alignment discovery and trace generation along with utilites for displaying alignments
+ *     Based on previously unpublished ideas from 2005, subsequently refined in 2013-14.  Basic
+ *     idea is to keep a dynamically selected interval of the f.r. waves from my 1986 O(nd) paper.
+ *     A recent cool idea is to not record all the details of an alignment while discovering it
+ *     but simply record trace points through which the optimal alignment passes every 100bp,
+ *     allowing rapid recomputation of the alignment details between trace points.
+ *
+ *  Author :  Gene Myers
+ *  First  :  June 2013
+ *  Current:  June 1, 2014
+ *
+ ********************************************************************************************/
+
+// align1: Derived from the original BOA aligner
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include <limits.h>
+
+#include "DB.h"
+#include "align.h"
+
+#define   DELTAS
+
+#undef    DEBUG_PASSES     //  Show forward / backward extension termini for Local_Alignment
+#undef    DEBUG_POINTS     //  Show trace points
+#undef    DEBUG_WAVE       //  Show waves of Local_Alignment
+#undef     SHOW_MATCH_WAVE //  For waves of Local_Alignment also show # of matches
+#undef    SHOW_TRAIL       //  Show trace at the end of forward and reverse passes
+#undef    SHOW_TPS         //  Show trace points as they are encountered in a wave
+
+#undef  DEBUG_EXTEND       //  Show waves of Extend_Until_Overlap
+
+#undef DEBUG_ALIGN        //  Show division points of Compute_Trace
+#undef  DEBUG_SCRIPT       //  Show trace additions for Compute_Trace
+#undef  DEBUG_AWAVE        //  Show F/R waves of Compute_Trace
+#define   SMALL_BIT 100
+
+#undef  SHOW_TRACE         //  Show full trace for Print_Alignment
+
+#undef  WAVE_STATS
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  Working Storage Abstraction                                                           *
+*                                                                                        *
+\****************************************************************************************/
+
+typedef struct            //  Hidden from the user, working space for each thread
+  { int     vecmax;
+    void   *vector;
+    int     celmax;
+    void   *cells;
+    int     pntmax;
+    void   *points;
+    int     tramax;
+    void   *trace;
+  } _Work_Data;
+
+Work_Data *New_Work_Data()
+{ _Work_Data *work;
+  
+  work = (_Work_Data *) Malloc(sizeof(_Work_Data),"Allocating work data block");
+  if (work == NULL)
+    exit (1);
+  work->vecmax = 0;
+  work->vector = NULL;
+  work->pntmax = 0;
+  work->points = NULL;
+  work->tramax = 0;
+  work->trace  = NULL;
+  work->celmax = 0;
+  work->cells  = NULL;
+  return ((Work_Data *) work);
+}
+
+static void enlarge_vector(_Work_Data *work, int newmax)
+{ work->vecmax = ((int) (newmax*1.2)) + 10000;
+  work->vector = Realloc(work->vector,work->vecmax,"Enlarging DP vector");
+  if (work->vector == NULL)
+    exit (1);
+}
+
+static void enlarge_points(_Work_Data *work, int newmax)
+{ work->pntmax = ((int) (newmax*1.2)) + 10000;
+  work->points = Realloc(work->points,work->pntmax,"Enlarging point vector");
+  if (work->points == NULL)
+    exit (1);
+}
+
+static void enlarge_trace(_Work_Data *work, int newmax)
+{ work->tramax = ((int) (newmax*1.2)) + 10000;
+  work->trace  = Realloc(work->trace,work->tramax,"Enlarging trace vector");
+  if (work->trace == NULL)
+    exit (1);
+}
+
+void Free_Work_Data(Work_Data *ework)
+{ _Work_Data *work = (_Work_Data *) ework;
+  if (work->vector != NULL)
+    free(work->vector);
+  if (work->cells != NULL)
+    free(work->cells);
+  if (work->trace != NULL)
+    free(work->trace);
+  if (work->points != NULL)
+    free(work->points);
+  free(work);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  ADAPTIVE PATH FINDING                                                                 *
+*                                                                                        *
+\****************************************************************************************/
+
+  //  Absolute/Fixed Parameters
+
+#define BVEC  uint64     //  Can be uint32 if PATH_LEN <= 32
+
+#define TRIM_LEN    15   //  Report as the tip, the last wave maximum for which the last
+                         //     2*TRIM_LEN edits are prefix-positive at rate ave_corr*f(bias)
+                         //     (max value is 20)
+
+#define PATH_LEN    60   //  Follow the last PATH_LEN columns/edges (max value is 63)
+
+  //  Derivative fixed parameters
+
+#define PATH_TOP  0x1000000000000000ll   //  Must be 1 << PATH_LEN
+#define PATH_INT  0x0fffffffffffffffll   //  Must be PATH_TOP-1
+#define TRIM_MASK 0x7fff                 //  Must be (1 << TRIM_LEN) - 1
+#define TRIM_MLAG 200                    //  How far can last trim point be behind best point
+#define WAVE_LAG   30                    //  How far can worst point be behind the best point
+
+static double Bias_Factor[10] = { .690, .690, .690, .690, .780,
+                                  .850, .900, .933, .966, 1.000 };
+
+  //  Micro-Sat Band Parameters
+
+#define MICRO_SAT  20
+
+static int Sat_Width[MICRO_SAT+1] =
+    { -1, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 };
+
+#define SAT_LOW   .75
+#define SAT_HGH  1.25
+
+  //  Adjustable paramters
+
+typedef struct
+  { double ave_corr;
+    int    trace_space;
+    float  freq[4];
+    int    ave_path;
+    int16 *score;
+    int16 *table;
+  } _Align_Spec;
+ 
+/* Fill in bit table: TABLE[x] = 1 iff the alignment modeled by x (1 = match, 0 = mismatch)
+     has a non-negative score for every suffix of the alignment under the scoring scheme
+     where match = MATCH and mismatch = -1.  MATCH is set so that an alignment with TRIM_PCT
+     matches has zero score ( (1-TRIM_PCT) / TRIM_PCT ).                                     */
+
+#define FRACTION 1000  //  Implicit fractional part of scores, i.e. score = x/FRACTION
+
+typedef struct
+  { int    mscore;
+    int    dscore;
+    int16 *table;
+    int16 *score;
+  } Table_Bits;
+
+static void set_table(int bit, int prefix, int score, int max, Table_Bits *parms)
+{ if (bit >= TRIM_LEN)
+    { parms->table[prefix] = (int16) (score-max);
+      parms->score[prefix] = (int16) score;
+    }
+  else
+    { if (score > max)
+        max = score;
+      set_table(bit+1,(prefix<<1),score - parms->dscore,max,parms);
+      set_table(bit+1,(prefix<<1) | 1,score + parms->mscore,max,parms);
+    }
+}
+
+/* Create an alignment specification record including path tip tables & values */
+
+Align_Spec *New_Align_Spec(double ave_corr, int trace_space, float *freq)
+{ _Align_Spec *spec;
+  Table_Bits   parms;
+  double       match;
+  int          bias;
+
+  spec = (_Align_Spec *) Malloc(sizeof(_Align_Spec),"Allocating alignment specification");
+  if (spec == NULL)
+    exit (1);
+
+  spec->ave_corr    = ave_corr;
+  spec->trace_space = trace_space;
+  spec->freq[0]     = freq[0];
+  spec->freq[1]     = freq[1];
+  spec->freq[2]     = freq[2];
+  spec->freq[3]     = freq[3];
+
+  match = freq[0] + freq[3];
+  if (match > .5)
+    match = 1.-match;
+  bias = (int) ((match+.025)*20.-1.);
+  if (match < .2)
+    { fprintf(stderr,"Warning: Base bias worse than 80/20%% !\n");
+      bias = 3;
+    }
+
+  spec->ave_path = (int) (PATH_LEN * (1. - Bias_Factor[bias] * (1. - ave_corr)));
+  parms.mscore   = (int) (FRACTION * Bias_Factor[bias] * (1. - ave_corr));
+  parms.dscore   = FRACTION - parms.mscore;
+
+  parms.score = (int16 *) Malloc(sizeof(int16)*(TRIM_MASK+1)*2,"Allocating trim table");
+  if (parms.score == NULL)
+    exit (1);
+  parms.table = parms.score + (TRIM_MASK+1);
+
+  set_table(0,0,0,0,&parms);
+
+  spec->table = parms.table;
+  spec->score = parms.score;
+
+  return ((Align_Spec *) spec);
+}
+
+void Free_Align_Spec(Align_Spec *espec)
+{ _Align_Spec *spec = (_Align_Spec *) espec;
+  free(spec->score);
+  free(spec);
+}
+
+double Average_Correlation(Align_Spec *espec)
+{ return (((_Align_Spec *) espec)->ave_corr); }
+
+int Trace_Spacing(Align_Spec *espec)
+{ return (((_Align_Spec *) espec)->trace_space); }
+
+float *Base_Frequencies(Align_Spec *espec)
+{ return (((_Align_Spec *) espec)->freq); }
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  LOCAL ALIGNMENT FINDER: forward_/reverse_wave and Local_Alignment                     *
+*                                                                                        *
+\****************************************************************************************/
+
+
+#ifdef WAVE_STATS
+
+static int64 MAX, TOT, NWV;
+static int64 RESTARTS;
+
+void Init_Stats()
+{ MAX = TOT = NWV = 0;
+  RESTARTS = 0;
+}
+
+void Print_Stats()
+{ printf("\nMax = %lld  Ave = %.1f  # = %lld\n",MAX,(1.*TOT)/NWV,NWV);
+  printf("\nRestarts = %lld\n",RESTARTS);
+}
+
+#endif
+
+
+#ifdef DEBUG_WAVE
+
+static void print_wave(int *V, int *M, int low, int hgh, int besta)
+{ int k, bestk;
+
+  (void) M;
+  printf("  [%6d,%6d]: ",low,hgh);
+  for (k = low; k <= hgh; k++)
+    { if (besta == V[k])
+        bestk = k;
+      // printf(" %3d",(V[k]+k)/2);
+      printf(" %3d",besta-V[k]);
+    }
+  printf(" : %d (%d,%d)\n",besta,(besta+bestk)/2,(besta-bestk)/2);
+#ifdef SHOW_MATCH_WAVE
+  printf("                   ");
+  for (k = low; k <= hgh; k++)
+    printf(" %3d",M[k]);
+  printf("\n");
+#endif
+  fflush(stdout);
+}
+
+#endif
+
+/* At each furthest reaching point, keep a-coordinate of point (V), bitvector
+     recording the last TRIM_LEN columns of the implied alignment (T), and the
+     # of matches (1-bits) in the bitvector (M).                               */
+
+typedef struct
+  { int ptr;
+    int diag;
+#ifdef DELTAS
+    int diff;
+#endif
+    int mark;
+  } Pebble;
+
+// This (and reverse) take the bulk of the processing time in daligner
+// Isn't easily vectorizable given all the 'while/break' looping
+static int forward_wave(_Work_Data *work, _Align_Spec *spec,
+                        Alignment *align, Path *bpath,
+                        int mind, int maxd, int mida)
+{ char *aseq  = align->aseq;
+  char *bseq  = align->bseq;
+  Path *apath = align->path;
+
+  int     hgh, low, dif;
+  int     minp, maxp;
+  int    *V, *M;
+  BVEC   *T;
+
+  int    *HA, *HB;
+  int    *NA, *NB;
+  Pebble *cells;
+  int     avail, cmax, boff;
+
+  int     TRACE_SPACE = spec->trace_space;
+  int     PATH_AVE    = spec->ave_path;
+  int16  *SCORE       = spec->score;
+  int16  *TABLE       = spec->table;
+
+  int     besta, besty;
+  int     trima, trimy, trimd;
+  int     trimha, trimhb;
+  int     morea, morey, mored;
+  int     moreha, morehb;
+  int     more, morem, lasta;
+  int     aclip, bclip;
+
+  { int alen = align->alen + 1;
+    int blen = align->blen + 1;
+    int tlen = alen + blen + 1;
+
+    V  = ((int *) work->vector) + blen;
+    M  = V + tlen;
+    HA = M + tlen;
+    HB = HA + tlen;
+    NA = HB + tlen;
+    NB = NA + tlen;
+    T  = ((BVEC *) (NB + alen)) + blen;
+
+    cells = (Pebble *) (work->cells);
+    cmax  = work->celmax;
+    avail = 0;
+
+    if (COMP(align->flags))
+      boff = align->blen % TRACE_SPACE;
+    else
+      boff = 0;
+  }
+
+  /* Compute 0-wave starting from mid-line */
+
+  hgh = maxd;
+  low = mind;
+  if (aseq == bseq)
+    { if (low < 0)
+        { int big = -low;
+          int sml = -hgh;
+
+          if (big <= MICRO_SAT)
+            minp = low - Sat_Width[big];
+          else
+            minp = -SAT_HGH*big;
+          if (sml <= MICRO_SAT)
+            maxp = hgh + Sat_Width[sml];
+          else
+            maxp = -SAT_LOW*sml;
+        }
+      else
+        { if (low <= MICRO_SAT)
+            minp = low - Sat_Width[low];
+          else
+            minp = SAT_LOW*low;
+          if (hgh <= MICRO_SAT)
+            maxp = hgh + Sat_Width[hgh];
+          else
+            maxp = SAT_HGH*hgh;
+        }
+    }
+  else
+    { minp = -INT32_MAX;
+      maxp =  INT32_MAX;
+    }
+  dif = 0;
+
+  more  = 1;
+  aclip =  INT32_MAX;
+  bclip = -INT32_MAX;
+
+  besta  = trima  = morea = lasta = mida;
+  besty  = trimy  = morey = (mida-hgh) >> 1;
+  trimd  = mored  = 0;
+  trimha = moreha = 0;
+  trimhb = morehb = 1;
+  morem  = -1;
+
+  { int   k;
+    char *a;
+
+    a  = aseq + hgh;
+    for (k = hgh; k >= low; k--)
+      { int     y, c, d;
+        int     ha, hb;
+        int     na, nb;
+        Pebble *pb;
+
+        y = (mida-k) >> 1;
+
+        if (avail >= cmax-1)
+          { cmax  = ((int) (avail*1.2)) + 10000;
+            cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+            if (cells == NULL)
+              exit (1);
+          }
+
+        na = ((y+k)/TRACE_SPACE)*TRACE_SPACE;
+#ifdef SHOW_TPS
+        printf(" A %d: %d,%d,0,%d\n",avail,-1,k,na); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+#ifdef DELTAS
+        pb->diff = 0;
+#endif
+        pb->mark = na;
+        ha  = avail++;
+        na += TRACE_SPACE;
+
+        nb = ((y+(TRACE_SPACE-boff))/TRACE_SPACE-1)*TRACE_SPACE+boff;
+#ifdef SHOW_TPS
+        printf(" B %d: %d,%d,0,%d\n",avail,-1,k,nb); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+#ifdef DELTAS
+        pb->diff = 0;
+#endif
+        pb->mark = nb;
+        hb  = avail++;
+        nb += TRACE_SPACE;
+
+        while (1)
+          { c = bseq[y];
+            if (c == 4)
+              { more = 0;
+                if (bclip < k)
+                  bclip = k;
+                break;
+              }
+            d = a[y];
+            if (c != d)
+              { if (d == 4) 
+                  { more  = 0;
+                    aclip = k;
+                  }
+                break;
+              }
+            y += 1;
+          }
+        c = (y << 1) + k;
+
+        while (y+k >= na)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  exit (1);
+              }
+#ifdef SHOW_TPS
+            printf(" A %d: %d,%d,0,%d\n",avail,ha,k,na); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = ha;
+            pb->diag = k;
+#ifdef DELTAS
+            pb->diff = 0;
+#endif
+            pb->mark = na;
+            ha  = avail++;
+            na += TRACE_SPACE;
+          }
+        while (y >= nb)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  exit (1);
+              }
+#ifdef SHOW_TPS
+            printf(" B %d: %d,%d,0,%d\n",avail,hb,k,nb); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = hb;
+            pb->diag = k;
+#ifdef DELTAS
+            pb->diff = 0;
+#endif
+            pb->mark = nb;
+            hb  = avail++;
+            nb += TRACE_SPACE;
+          }
+
+        if (c > besta)
+          { besta  = trima = lasta = c;
+            besty  = trimy = y;
+            trimha = ha;
+            trimhb = hb;
+          }
+
+        V[k]  = c;
+        T[k]  = PATH_INT;
+        M[k]  = PATH_LEN;
+        HA[k] = ha;
+        HB[k] = hb;
+        NA[k] = na;
+        NB[k] = nb;
+
+        a -= 1;
+      }
+  }
+
+  if (more == 0)
+    { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+        more = 1;
+      if (hgh >= aclip)
+        { hgh = aclip-1;
+          if (morem <= M[aclip])
+            { morem  = M[aclip];
+              morea  = V[aclip];
+              morey  = (morea - aclip)/2;
+              moreha = HA[aclip];
+              morehb = HB[aclip];
+            }
+        }
+      if (low <= bclip)
+        { low = bclip+1;
+          if (morem <= M[bclip])
+            { morem  = M[bclip];
+              morea  = V[bclip];
+              morey  = (morea - bclip)/2;
+              moreha = HA[bclip];
+              morehb = HB[bclip];
+            }
+        }
+      aclip =  INT32_MAX;
+      bclip = -INT32_MAX;
+    }
+
+#ifdef DEBUG_WAVE
+  printf("\nFORWARD WAVE:\n");
+  print_wave(V,M,low,hgh,besta);
+#endif
+
+  /* Compute successive waves until no furthest reaching points remain */
+
+  while (more && lasta >= besta - TRIM_MLAG)
+    { int     k, n;
+      int     ua, ub;
+      BVEC    t;
+      int     am, ac, ap;
+      char   *a;
+
+      if (low > minp)
+        { low -= 1;
+          NA[low] = NA[low+1];
+          NB[low] = NB[low+1];
+          V[low]  = -1;
+        }
+      if (hgh < maxp)
+        { hgh += 1;
+          NA[hgh] = NA[hgh-1];
+          NB[hgh] = NB[hgh-1];
+          V[hgh]  = am = -1;
+        }
+      else
+        am = V[hgh];
+      dif += 1;
+
+      ac = V[hgh+1] = V[low-1] = -1;
+      a  = aseq + hgh;
+      t  = PATH_INT;
+      n  = PATH_LEN;
+      ua = ub = -1;
+      for (k = hgh; k >= low; k--)
+        { int     y, m;
+          int     ha, hb;
+          int     c, d;
+          BVEC    b;
+          Pebble *pb;
+
+          ap = ac;
+          ac = am;
+          am = V[d = k-1];
+
+          if (ac < am)
+            if (am < ap)
+              { c  = ap+1;
+                m  = n;
+                b  = t;
+                ha = ua;
+                hb = ub;
+              }
+            else
+              { c  = am+1;
+                m  = M[d];
+                b  = T[d]; 
+                ha = HA[d];
+                hb = HB[d];
+              }
+          else
+            if (ac < ap)
+              { c  = ap+1;
+                m  = n;
+                b  = t;
+                ha = ua;
+                hb = ub;
+              }
+            else
+              { c  = ac+2;
+                m  = M[k];
+                b  = T[k];
+                ha = HA[k];
+                hb = HB[k];
+              }
+
+          if ((b & PATH_TOP) != 0)
+            m -= 1;
+          b <<= 1;
+
+          y = (c-k) >> 1;
+          while (1)
+            { c = bseq[y];
+              if (c == 4)
+                { more = 0;
+                  if (bclip < k)
+                    bclip = k;
+                  break;
+                }
+              d = a[y];
+              if (c != d)
+                { if (d == 4) 
+                    { more  = 0;
+                      aclip = k;
+                    }
+                  break;
+                }
+              y += 1;
+              if ((b & PATH_TOP) == 0)
+                m += 1;
+              b = (b << 1) | 1;
+            }
+          c = (y << 1) + k;
+
+          while (y+k >= NA[k])
+            { if (cells[ha].mark < NA[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        exit (1);
+                    }
+#ifdef SHOW_TPS
+                  printf(" A %d: %d,%d,%d,%d\n",avail,ha,k,dif,NA[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = ha;
+                  pb->diag = k;
+#ifdef DELTAS
+                  pb->diff = dif;
+#endif
+                  pb->mark = NA[k];
+                  ha = avail++;
+                }
+              NA[k] += TRACE_SPACE;
+            }
+
+          while (y >= NB[k])
+            { if (cells[hb].mark < NB[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        exit (1);
+                    }
+#ifdef SHOW_TPS
+                  printf(" B %d: %d,%d,%d,%d\n",avail,hb,k,dif,NB[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = hb;
+                  pb->diag = k;
+#ifdef DELTAS
+                  pb->diff = dif;
+#endif
+                  pb->mark = NB[k];
+                  hb = avail++;
+                }
+              NB[k] += TRACE_SPACE;
+            }
+
+          if (c > besta)
+            { besta = c;
+              besty = y;
+              if (m >= PATH_AVE)
+                { lasta = c;
+                  if (TABLE[b & TRIM_MASK] >= 0)
+                    if (TABLE[(b >> TRIM_LEN) & TRIM_MASK] + SCORE[b & TRIM_MASK] >= 0)
+                      { trima  = c;
+                        trimy  = y;
+                        trimd  = dif;
+                        trimha = ha;
+                        trimhb = hb;
+                      }
+                }
+            }
+
+          t  = T[k];
+          n  = M[k];
+          ua = HA[k];
+          ub = HB[k];
+          V[k]  = c;
+          T[k]  = b;
+          M[k]  = m;
+          HA[k] = ha;
+          HB[k] = hb;
+
+          a -= 1;
+        }
+
+      if (more == 0)
+        { if (bseq[besty] != 4 && aseq[besta-besty] != 4)
+            more = 1;
+          if (hgh >= aclip)
+            { hgh = aclip-1;
+              if (morem <= M[aclip])
+                { morem  = M[aclip];
+                  morea  = V[aclip];
+                  morey  = (morea - aclip)/2;
+                  mored  = dif;
+                  moreha = HA[aclip];
+                  morehb = HB[aclip];
+                }
+            }
+          if (low <= bclip)
+            { low = bclip+1;
+              if (morem <= M[bclip])
+                { morem  = M[bclip];
+                  morea  = V[bclip];
+                  morey  = (morea - bclip)/2;
+                  mored  = dif;
+                  moreha = HA[bclip];
+                  morehb = HB[bclip];
+                }
+            }
+          aclip =  INT32_MAX;
+          bclip = -INT32_MAX;
+        }
+
+      n = besta - WAVE_LAG;
+      while (hgh >= low)
+        if (V[hgh] < n)
+          hgh -= 1;                               
+        else
+          { while (V[low] < n)
+              low += 1;
+            break;
+          }
+
+#ifdef WAVE_STATS
+      k = (hgh-low)+1;
+      if (k > MAX)
+        MAX = k;
+      TOT += k;
+      NWV += 1;
+#endif
+
+#ifdef DEBUG_WAVE
+      print_wave(V,M,low,hgh,besta);
+#endif
+    }
+
+  { uint16 *atrace = (uint16 *) apath->trace;
+    uint16 *btrace = (uint16 *) bpath->trace;
+    int     atlen, btlen;
+    int     trimx;
+    int     a, b, k, h;
+#ifdef DELTAS
+    int     d, e;
+#endif
+
+    if (morem >= 0)
+      { trimx  = morea-morey;
+        trimy  = morey;
+        trimd  = mored;
+        trimha = moreha;
+        trimhb = morehb;
+      }
+    else
+      trimx = trima-trimy;
+
+    atlen = btlen = 0;
+
+    a = -1;
+    for (h = trimha; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+#ifdef DELTAS
+
+    k = cells[h].diag;
+    b = (mida-k)/2;
+    e = 0;
+#ifdef SHOW_TRAIL
+    printf("  A path = (%5d,%5d)\n",(mida+k)/2,b); fflush(stdout);
+#endif
+    for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+      { k = cells[h].diag;
+        a = cells[h].mark - k;
+        d = cells[h].diff;
+        atrace[atlen++] = (uint16) (d-e);
+        atrace[atlen++] = (uint16) (a-b);
+#ifdef SHOW_TRAIL
+        printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,a-b); fflush(stdout);
+#endif
+        b = a;
+        e = d;
+      }
+    if (b+k != trimx)
+      { atrace[atlen++] = (uint16) (trimd-e);
+        atrace[atlen++] = (uint16) (trimy-b);
+#ifdef SHOW_TRAIL
+        printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimy-b); fflush(stdout);
+#endif
+      }
+    else if (b != trimy)
+      { atrace[atlen-1] = (uint16) (atrace[atlen-1] + (trimy-b));
+        atrace[atlen-2] = (uint16) (atrace[atlen-2] + (trimd-e));
+#ifdef SHOW_TRAIL
+        printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimy-b); fflush(stdout);
+#endif
+      }
+
+#else    //  DELTAS
+
+    k = cells[h].diag;
+    b = (mida-k)/2;
+#ifdef SHOW_TRAIL
+    printf("  A path = (%5d,%5d)\n",(mida+k)/2,b); fflush(stdout);
+#endif
+    for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+      { k = cells[h].diag;
+        a = cells[h].mark - k;
+        atrace[atlen++] = (uint16) (a-b);
+#ifdef SHOW_TRAIL
+        printf("     %4d: (%5d,%5d): %3d\n",h,a+k,a,a-b); fflush(stdout);
+#endif
+        b = a;
+      }
+    if (b+k != trimx)
+      { atrace[atlen++] = (uint16) (trimy-b);
+#ifdef SHOW_TRAIL
+        printf("           (%5d,%5d): %3d\n",trimx,trimy,trimy-b); fflush(stdout);
+#endif
+      }
+    else if (b != trimy)
+      { atrace[atlen-1] = (uint16) (atrace[atlen-1] + (trimy-b));
+#ifdef SHOW_TRAIL
+        printf("         @ (%5d,%5d): %3d\n",trimx,trimy,trimy-b); fflush(stdout);
+#endif
+      }
+
+#endif   //  DELTAS
+
+    a = -1;
+    for (h = trimhb; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+#ifdef DELTAS
+
+    k = cells[h].diag;
+    b = (mida+k)/2;
+    e = 0;
+    low = k;
+#ifdef SHOW_TRAIL
+    printf("  B path = (%5d,%5d)\n",b,(mida-k)/2); fflush(stdout);
+#endif
+    for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+      { k = cells[h].diag;
+        a = cells[h].mark + k;
+        d = cells[h].diff;
+        btrace[btlen++] = (uint16) (d-e);
+        btrace[btlen++] = (uint16) (a-b);  
+#ifdef SHOW_TRAIL
+        printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a,a-k,d-e,a-b); fflush(stdout);
+#endif
+        b = a;
+        e = d;
+      }
+    if (b-k != trimy)
+      { btrace[btlen++] = (uint16) (trimd-e);
+        btrace[btlen++] = (uint16) (trimx-b);  
+#ifdef SHOW_TRAIL
+        printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimx-b); fflush(stdout);
+#endif
+      }
+    else if (b != trimx)
+      { btrace[btlen-1] = (uint16) (btrace[btlen-1] + (trimx-b));
+        btrace[btlen-2] = (uint16) (btrace[btlen-2] + (trimd-e));
+#ifdef SHOW_TRAIL
+        printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,trimx-b); fflush(stdout);
+#endif
+      }
+
+#else  //  DELTAS
+
+    k = cells[h].diag;
+    b = (mida+k)/2;
+    low = k;
+#ifdef SHOW_TRAIL
+    printf("  B path = (%5d,%5d)\n",b,(mida-k)/2); fflush(stdout);
+#endif
+    for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+      { k = cells[h].diag;
+        a = cells[h].mark + k;
+        btrace[btlen++] = (uint16) (a-b);
+#ifdef SHOW_TRAIL
+        printf("     %4d: (%5d,%5d): %3d\n",h,a,a-k,a-b); fflush(stdout);
+#endif
+        b = a;
+      }
+    if (b-k != trimy)
+      { btrace[btlen++] = (uint16) (trimx-b);
+#ifdef SHOW_TRAIL
+        printf("           (%5d,%5d): %3d\n",trimx,trimy,trimx-b); fflush(stdout);
+#endif
+      }
+    else if (b != trimx)
+      { btrace[btlen-1] = (uint16) (btrace[btlen-1] + (trimx-b));
+#ifdef SHOW_TRAIL
+        printf("         @ (%5d,%5d): %3d\n",trimx,trimy,trimx-b); fflush(stdout);
+#endif
+      }
+
+#endif   //  DELTAS
+
+    apath->aepos = trimx;
+    apath->bepos = trimy;
+    apath->diffs = trimd;
+    apath->tlen  = atlen;
+    if (COMP(align->flags))
+      { bpath->abpos = align->blen - apath->bepos;
+        bpath->bbpos = align->alen - apath->aepos;
+      }
+    else
+      { bpath->aepos = apath->bepos;
+        bpath->bepos = apath->aepos;
+      }
+    bpath->diffs = trimd;
+    bpath->tlen  = btlen;
+  }
+
+  work->cells  = (void *) cells;
+  work->celmax = cmax;
+
+  return (low);
+}
+
+/*** Reverse Wave ***/
+
+static void reverse_wave(_Work_Data *work, _Align_Spec *spec,
+                         Alignment *align, Path *bpath, int mind, int maxd, int mida)
+{ char *aseq  = align->aseq - 1;
+  char *bseq  = align->bseq - 1;
+  Path *apath = align->path;
+
+  int     hgh, low, dif;
+  int     minp, maxp;
+  int    *V, *M;
+  BVEC   *T;
+
+  int    *HA, *HB;
+  int    *NA, *NB;
+  Pebble *cells;
+  int     avail, cmax, boff;
+
+  int     TRACE_SPACE = spec->trace_space;
+  int     PATH_AVE    = spec->ave_path;
+  int16  *SCORE       = spec->score;
+  int16  *TABLE       = spec->table;
+
+  int     besta, besty;
+  int     trima, trimy, trimd;
+  int     trimha, trimhb;
+  int     morea, morey, mored;
+  int     moreha, morehb;
+  int     more, morem, lasta;
+  int     aclip, bclip;
+
+  { int alen = align->alen + 1;
+    int blen = align->blen + 1;
+    int tlen = alen + blen + 1;
+
+    V  = ((int *) work->vector) + blen;
+    M  = V + tlen;
+    HA = M + tlen;
+    HB = HA + tlen;
+    NA = HB + tlen;
+    NB = NA + tlen;
+    T  = ((BVEC *) (NB + alen)) + blen;
+
+    cells = (Pebble *) (work->cells);
+    cmax  = work->celmax;
+    avail = 0;
+
+    if (COMP(align->flags))
+      boff = align->blen % TRACE_SPACE;
+    else
+      boff = 0;
+  }
+
+  hgh = maxd;
+  low = mind;
+  if (aseq == bseq)
+    { if (low < 0)
+        { int big = -low;
+          int sml = -hgh;
+
+          if (big <= MICRO_SAT)
+            minp = low - Sat_Width[big];
+          else
+            minp = -SAT_HGH*big;
+          if (sml <= MICRO_SAT)
+            maxp = hgh + Sat_Width[sml];
+          else
+            maxp = -SAT_LOW*sml;
+        }
+      else
+        { if (low <= MICRO_SAT)
+            minp = low - Sat_Width[low];
+          else
+            minp = SAT_LOW*low;
+          if (hgh <= MICRO_SAT)
+            maxp = hgh + Sat_Width[hgh];
+          else
+            maxp = SAT_HGH*hgh;
+        }
+    }
+  else
+    { minp = -INT32_MAX;
+      maxp = INT32_MAX;
+    }
+  dif = 0;
+
+  more  = 1;
+  aclip = -INT32_MAX;
+  bclip =  INT32_MAX;
+
+  besta  = trima  = morea = lasta = mida;
+  besty  = trimy  = morey = (mida-hgh) >> 1;
+  trimd  = mored  = 0;
+  trimha = moreha = 0;
+  trimhb = morehb = 1;
+  morem  = -1;
+
+  { int   k;
+    char *a;
+
+    a = aseq + low;
+    for (k = low; k <= hgh; k++)
+      { int     y, c, d;
+        int     ha, hb;
+        int     na, nb;
+        Pebble *pb;
+
+        y = (mida-k) >> 1;
+
+        if (avail >= cmax-1)
+          { cmax  = ((int) (avail*1.2)) + 10000;
+            cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+            if (cells == NULL)
+              exit (1);
+          }
+
+        na = ((y+k+TRACE_SPACE-1)/TRACE_SPACE-1)*TRACE_SPACE;
+#ifdef SHOW_TPS
+        printf(" A %d: -1,%d,0,%d\n",avail,k,na+TRACE_SPACE); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+#ifdef DELTAS
+        pb->diff = 0;
+#endif
+        pb->mark = y+k;
+        ha  = avail++;
+
+        nb = ((y+(TRACE_SPACE-boff)-1)/TRACE_SPACE-1)*TRACE_SPACE+boff;
+#ifdef SHOW_TPS
+        printf(" B %d: -1,%d,0,%d\n",avail,k,nb+TRACE_SPACE); fflush(stdout);
+#endif
+        pb = cells+avail;
+        pb->ptr  = -1;
+        pb->diag = k;
+#ifdef DELTAS
+        pb->diff = 0;
+#endif
+        pb->mark = y;
+        hb  = avail++;
+
+        while (1)
+          { c = bseq[y];
+            if (c == 4)
+              { more = 0;
+                if (bclip > k)
+                  bclip = k;
+                break;
+              }
+            d = a[y];
+            if (c != d)
+              { if (d == 4) 
+                  { more  = 0;
+                    aclip = k;
+                  }
+                break;
+              }
+            y -= 1;
+          }
+        c = (y << 1) + k;
+
+        while (y+k <= na)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  exit (1);
+              }
+#ifdef SHOW_TPS
+            printf(" A %d: %d,%d,0,%d\n",avail,ha,k,na); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = ha;
+            pb->diag = k;
+#ifdef DELTAS
+            pb->diff = 0;
+#endif
+            pb->mark = na;
+            ha  = avail++;
+            na -= TRACE_SPACE;
+          }
+        while (y <= nb)
+          { if (avail >= cmax)
+              { cmax  = ((int) (avail*1.2)) + 10000;
+                cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),"Reallocating trace cells");
+                if (cells == NULL)
+                  exit (1);
+              }
+#ifdef SHOW_TPS
+            printf(" B %d: %d,%d,0,%d\n",avail,hb,k,nb); fflush(stdout);
+#endif
+            pb = cells+avail;
+            pb->ptr  = hb;
+            pb->diag = k;
+#ifdef DELTAS
+            pb->diff = 0;
+#endif
+            pb->mark = nb;
+            hb  = avail++;
+            nb -= TRACE_SPACE;
+          }
+
+        if (c < besta)
+          { besta  = trima = lasta = c;
+            besty  = trimy = y;
+            trimha = ha;
+            trimhb = hb;
+          }
+
+        V[k]  = c;
+        T[k]  = PATH_INT;
+        M[k]  = PATH_LEN;
+        HA[k] = ha;
+        HB[k] = hb;
+        NA[k] = na;
+        NB[k] = nb;
+
+        a += 1;
+      }
+  }
+
+  if (more == 0)
+    { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+        more = 1;
+      if (low <= aclip)
+        { low = aclip+1;
+          if (morem <= M[aclip])
+            { morem  = M[aclip];
+              morea  = V[aclip];
+              morey  = (morea - aclip)/2;
+              moreha = HA[aclip];
+              morehb = HB[aclip];
+            }
+        }
+      if (hgh >= bclip)
+        { hgh = bclip-1;
+          if (morem <= M[bclip])
+            { morem  = M[bclip];
+              morea  = V[bclip];
+              morey  = (morea - bclip)/2;
+              moreha = HA[bclip];
+              morehb = HB[bclip];
+            }
+        }
+      aclip = -INT32_MAX;
+      bclip =  INT32_MAX;
+    }
+
+#ifdef DEBUG_WAVE
+  printf("\nREVERSE WAVE:\n");
+  print_wave(V,M,low,hgh,besta);
+#endif
+
+  while (more && lasta <= besta + TRIM_MLAG)
+    { int    k, n;
+      int    ua, ub;
+      BVEC   t;
+      int    am, ac, ap;
+      char  *a;
+
+      if (low > minp)
+        { low -= 1;
+          NA[low] = NA[low+1];
+          NB[low] = NB[low+1];
+          V[low]  = ap = INT32_MAX;
+        }
+      else
+        ap = V[low]; 
+      if (hgh < maxp)
+        { hgh += 1;
+          NA[hgh] = NA[hgh-1];
+          NB[hgh] = NB[hgh-1];
+          V[hgh] = INT32_MAX;
+        }
+      dif += 1;
+
+      ac = V[hgh+1] = V[low-1] = INT32_MAX;
+      a  = aseq + low;
+      t  = PATH_INT;
+      n  = PATH_LEN;
+      ua = ub = -1;
+      for (k = low; k <= hgh; k++)
+        { int     y, m;
+          int     ha, hb;
+          int     c, d;
+          BVEC    b;
+          Pebble *pb;
+
+          am = ac;
+          ac = ap;
+          ap = V[d = k+1];
+
+          if (ac > ap)
+            if (ap > am)
+              { c = am-1;
+                m  = n;
+                b  = t;
+                ha = ua;
+                hb = ub;
+              }
+            else
+              { c  = ap-1;
+                m  = M[d];
+                b  = T[d];
+                ha = HA[d];
+                hb = HB[d];
+              }
+          else
+            if (ac > am)
+              { c  = am-1;
+                m  = n;
+                b  = t;
+                ha = ua;
+                hb = ub;
+              }
+            else
+              { c  = ac-2;
+                m  = M[k];
+                b  = T[k];
+                ha = HA[k];
+                hb = HB[k];
+              }
+
+          if ((b & PATH_TOP) != 0)
+            m -= 1;
+          b <<= 1;
+
+          y = (c-k) >> 1;
+          while (1)
+            { c = bseq[y];
+              if (c == 4)
+                { more = 0;
+                  if (bclip > k)
+                    bclip = k;
+                  break;
+                }
+              d = a[y];
+              if (c != d)
+                { if (d == 4) 
+                    { more  = 0;
+                      aclip = k;
+                    }
+                  break;
+                }
+              y -= 1;
+              if ((b & PATH_TOP) == 0)
+                m += 1;
+              b = (b << 1) | 1;
+            }
+          c = (y << 1) + k;
+
+          while (y+k <= NA[k])
+            { if (cells[ha].mark > NA[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        exit (1);
+                    }
+#ifdef SHOW_TPS
+                  printf(" A %d: %d,%d,%d,%d\n",avail,ha,k,dif,NA[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = ha;
+                  pb->diag = k;
+#ifdef DELTAS
+                  pb->diff = dif;
+#endif
+                  pb->mark = NA[k];
+                  ha = avail++;
+                }
+              NA[k] -= TRACE_SPACE;
+            }
+          while (y <= NB[k])
+            { if (cells[hb].mark > NB[k])
+                { if (avail >= cmax)
+                    { cmax  = ((int) (avail*1.2)) + 10000;
+                      cells = (Pebble *) Realloc(cells,cmax*sizeof(Pebble),
+                                                       "Reallocating trace cells");
+                      if (cells == NULL)
+                        exit (1);
+                    }
+#ifdef SHOW_TPS
+                  printf(" B %d: %d,%d,%d,%d\n",avail,hb,k,dif,NB[k]); fflush(stdout);
+#endif
+                  pb = cells+avail;
+                  pb->ptr  = hb;
+                  pb->diag = k;
+#ifdef DELTAS
+                  pb->diff = dif;
+#endif
+                  pb->mark = NB[k];
+                  hb = avail++;
+                }
+              NB[k] -= TRACE_SPACE;
+            }
+
+          if (c < besta)
+            { besta = c;
+              besty = y;
+              if (m >= PATH_AVE)
+                { lasta = c;
+                  if (TABLE[b & TRIM_MASK] >= 0)
+                    if (TABLE[(b >> TRIM_LEN) & TRIM_MASK] + SCORE[b & TRIM_MASK] >= 0)
+                      { trima  = c;
+                        trimy  = y;
+                        trimd  = dif;
+                        trimha = ha;
+                        trimhb = hb;
+                      }
+                }
+            }
+
+          t  = T[k];
+          n  = M[k];
+          ua = HA[k];
+          ub = HB[k];
+          V[k]  = c;
+          T[k]  = b;
+          M[k]  = m;
+          HA[k] = ha;
+          HB[k] = hb;
+
+          a += 1;
+        }
+
+      if (more == 0)
+        { if (bseq[besty] != 4 && aseq[besta - besty] != 4)
+            more = 1;
+          if (low <= aclip)
+            { low = aclip+1;
+              if (morem <= M[aclip])
+                { morem  = M[aclip];
+                  morea  = V[aclip];
+                  morey  = (morea - aclip)/2;
+                  mored  = dif;
+                  moreha = HA[aclip];
+                  morehb = HB[aclip];
+                }
+            }
+          if (hgh >= bclip)
+            { hgh = bclip-1;
+              if (morem <= M[bclip])
+                { morem  = M[bclip];
+                  morea  = V[bclip];
+                  morey  = (morea - bclip)/2;
+                  mored  = dif;
+                  moreha = HA[bclip];
+                  morehb = HB[bclip];
+                }
+            }
+          aclip = -INT32_MAX;
+          bclip =  INT32_MAX;
+        }
+
+      n = besta + WAVE_LAG;
+      while (hgh >= low)
+        if (V[hgh] > n)
+          hgh -= 1;                               
+        else
+          { while (V[low] > n)
+              low += 1;
+            break;
+          }
+
+#ifdef WAVE_STATS
+      k = (hgh-low)+1;
+      if (k > MAX)
+        MAX = k;
+      TOT += k;
+      NWV += 1;
+#endif
+
+#ifdef DEBUG_WAVE
+      print_wave(V,M,low,hgh,besta);
+#endif
+    }
+
+  { uint16 *atrace = (uint16 *) apath->trace;
+    uint16 *btrace = (uint16 *) bpath->trace;
+    int     atlen, btlen;
+    int     trimx;
+    int     a, b, k, h;
+#ifdef DELTAS
+    int     d, e;
+#endif
+
+    if (morem >= 0)
+      { trimx  = morea-morey;
+        trimy  = morey;
+        trimd  = mored;
+        trimha = moreha;
+        trimhb = morehb;
+      }
+    else
+      trimx = trima-trimy;
+
+    atlen = btlen = 0;
+
+    a = -1;
+    for (h = trimha; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+#ifdef DELTAS
+
+    k = cells[h].diag;
+    b = cells[h].mark - k;
+    e = 0;
+#ifdef SHOW_TRAIL
+    printf("  A path = (%5d,%5d)\n",b+k,b); fflush(stdout);
+#endif
+    if ((b+k)%TRACE_SPACE != 0)
+      { h = cells[h].ptr;
+        if (h < 0)
+          { a = trimy;
+            d = trimd;
+          }
+        else
+          { k = cells[h].diag;
+            a = cells[h].mark - k;
+            d = cells[h].diff;
+          }
+#ifdef SHOW_TRAIL
+        printf("    +%4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,b-a); fflush(stdout);
+#endif
+        if (apath->tlen == 0)
+          { atrace[--atlen] = (uint16) (b-a);
+            atrace[--atlen] = (uint16) (d-e);
+          }
+        else
+          { atrace[1] = (uint16) (atrace[1] + (b-a));
+            atrace[0] = (uint16) (atrace[0] + (d-e));
+          }
+        b = a;
+        e = d;
+      }
+    if (h >= 0)
+      { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+          { k = cells[h].diag;
+            a = cells[h].mark - k;
+            atrace[--atlen] = (uint16) (b-a);
+            d = cells[h].diff;
+            atrace[--atlen] = (uint16) (d-e);
+#ifdef SHOW_TRAIL
+            printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a+k,a,d-e,b-a); fflush(stdout);
+#endif
+            b = a;
+            e = d;
+          }
+        if (b+k != trimx)
+          { atrace[--atlen] = (uint16) (b-trimy);
+            atrace[--atlen] = (uint16) (trimd-e);
+#ifdef SHOW_TRAIL
+            printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimy); fflush(stdout);
+#endif
+          }
+        else if (b != trimy)
+          { atrace[atlen+1] = (uint16) (atrace[atlen+1] + (b-trimy));
+            atrace[atlen]   = (uint16) (atrace[atlen]   + (trimd-e));
+#ifdef SHOW_TRAIL
+            printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimy); fflush(stdout);
+#endif
+          }
+      }
+
+#else   //  DELTAS
+
+    k = cells[h].diag;
+    b = cells[h].mark - k;
+#ifdef SHOW_TRAIL
+    printf("  A path = (%5d,%5d)\n",b+k,b); fflush(stdout);
+#endif
+    if ((b+k)%TRACE_SPACE != 0)
+      { h = cells[h].ptr;
+        if (h < 0)
+          a = trimy;
+        else
+          { k = cells[h].diag;
+            a = cells[h].mark - k;
+          }
+#ifdef SHOW_TRAIL
+        printf("    +%4d: (%5d,%5d): %3d\n",h,a+k,a,b-a); fflush(stdout);
+#endif
+        if (apath->tlen == 0)
+          atrace[--atlen] = (uint16) (b-a);
+        else
+          atrace[0] = (uint16) (atrace[0] + (b-a));
+        b = a;
+      }
+    if (h >= 0)
+      { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+          { k = cells[h].diag;
+            a = cells[h].mark - k;
+            atrace[--atlen] = (uint16) (b-a);
+#ifdef SHOW_TRAIL
+            printf("     %4d: (%5d,%5d): %3d\n",h,a+k,a,b-a); fflush(stdout);
+#endif
+            b = a;
+          }
+        if (b+k != trimx)
+          { atrace[--atlen] = (uint16) (b-trimy);
+#ifdef SHOW_TRAIL
+            printf("           (%5d,%5d): %3d\n",trimx,trimy,b-trimy); fflush(stdout);
+#endif
+          }
+        else if (b != trimy)
+          { atrace[atlen] = (uint16) (atrace[atlen] + (b-trimy));
+#ifdef SHOW_TRAIL
+            printf("         @ (%5d,%5d): %3d\n",trimx,trimy,b-trimy); fflush(stdout);
+#endif
+          }
+      }
+
+#endif     // DELTAS
+
+    a = -1;
+    for (h = trimhb; h >= 0; h = b)
+      { b = cells[h].ptr; 
+        cells[h].ptr = a;
+        a = h;
+      }
+    h = a;
+
+#ifdef DELTAS
+
+    k = cells[h].diag;
+    b = cells[h].mark + k;
+    e = 0;
+#ifdef SHOW_TRAIL
+    printf("  B path = (%5d,%5d)\n",b,b-k); fflush(stdout);
+#endif
+    if ((b-k)%TRACE_SPACE != boff)
+      { h = cells[h].ptr;
+        if (h < 0)
+          { a = trimx;
+            d = trimd;
+          } 
+        else
+          { k = cells[h].diag;
+            a = cells[h].mark + k;
+            d = cells[h].diff;
+          }
+#ifdef SHOW_TRAIL
+        printf("    +%4d: (%5d,%5d): %3d / %3d\n",h,a,a-k,d-e,b-a); fflush(stdout);
+#endif
+        if (bpath->tlen == 0)
+          { btrace[--btlen] = (uint16) (b-a);
+            btrace[--btlen] = (uint16) (b-a);
+          }
+        else
+          { btrace[1] = (uint16) (btrace[1] + (b-a));
+            btrace[0] = (uint16) (btrace[0] + (d-e));
+          }
+        b = a;
+        e = d;
+      }
+
+    if (h >= 0)
+      { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+          { k = cells[h].diag;
+            a = cells[h].mark + k;
+            btrace[--btlen] = (uint16) (b-a);
+            d = cells[h].diff;
+            btrace[--btlen] = (uint16) (d-e);
+#ifdef SHOW_TRAIL
+            printf("     %4d: (%5d,%5d): %3d / %3d\n",h,a,a-k,d-e,b-a); fflush(stdout);
+#endif
+            b = a;
+            e = d;
+          }
+        if (b-k != trimy)
+          { btrace[--btlen] = (uint16) (b-trimx);
+            btrace[--btlen] = (uint16) (trimd-e);
+#ifdef SHOW_TRAIL
+            printf("           (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimx); fflush(stdout);
+#endif
+          }
+        else if (b != trimx)
+          { btrace[btlen+1] = (uint16) (btrace[btlen+1] + (b-trimx));
+            btrace[btlen]   = (uint16) (btrace[btlen]   + (trimd-e));
+#ifdef SHOW_TRAIL
+            printf("         @ (%5d,%5d): %3d / %3d\n",trimx,trimy,trimd-e,b-trimx); fflush(stdout);
+#endif
+          }
+      }
+
+#else //  DELTAS
+
+    k = cells[h].diag;
+    b = cells[h].mark + k;
+#ifdef SHOW_TRAIL
+    printf("  B path = (%5d,%5d)\n",b,b-k); fflush(stdout);
+#endif
+    if ((b-k)%TRACE_SPACE != boff)
+      { h = cells[h].ptr;
+        if (h < 0)
+          a = trimx;
+        else
+          { k = cells[h].diag;
+            a = cells[h].mark + k;
+          }
+#ifdef SHOW_TRAIL
+        printf("    +%4d: (%5d,%5d): %3d\n",h,a,a-k,b-a); fflush(stdout);
+#endif
+        if (bpath->tlen == 0)
+          btrace[--btlen] = (uint16) (b-a);
+        else
+          btrace[0] = (uint16) (btrace[0] + (b-a));
+        b = a;
+      }
+
+    if (h >= 0)
+      { for (h = cells[h].ptr; h >= 0; h = cells[h].ptr)
+          { k = cells[h].diag;
+            a = cells[h].mark + k;
+            btrace[--btlen] = (uint16) (b-a);
+#ifdef SHOW_TRAIL
+            printf("     %4d: (%5d,%5d): %3d\n",h,a,a-k,b-a); fflush(stdout);
+#endif
+            b = a;
+          }
+        if (b-k != trimy)
+          { btrace[--btlen] = (uint16) (b-trimx);
+#ifdef SHOW_TRAIL
+            printf("           (%5d,%5d): %3d\n",trimx,trimy,b-trimx); fflush(stdout);
+#endif
+          }
+        else if (b != trimx)
+          { btrace[btlen] = (uint16) (btrace[btlen] + (b-trimx));
+#ifdef SHOW_TRAIL
+            printf("         @ (%5d,%5d): %3d\n",trimx,trimy,b-trimx); fflush(stdout);
+#endif
+          }
+      }
+
+#endif   // DELTAS
+
+    apath->abpos = trimx;
+    apath->bbpos = trimy;
+    apath->diffs = apath->diffs + trimd;
+    apath->tlen  = apath->tlen  - atlen;
+    apath->trace = atrace + atlen;
+    if (COMP(align->flags))
+      { bpath->aepos = align->blen - apath->bbpos;
+        bpath->bepos = align->alen - apath->abpos;
+      }
+    else
+      { bpath->abpos = apath->bbpos;
+        bpath->bbpos = apath->abpos;
+      }
+    bpath->diffs = bpath->diffs + trimd;
+    bpath->tlen  = bpath->tlen  - btlen;
+    bpath->trace = btrace + btlen;
+  }
+
+  work->cells  = (void *) cells;
+  work->celmax = cmax;
+}
+
+
+/* Find the longest local alignment between aseq and bseq through (xcnt,ycnt)
+   See associated .h file for the precise definition of the interface.
+*/
+
+Path *Local_Alignment(Alignment *align, Work_Data *ework, Align_Spec *espec,
+                      int xlow, int xhgh, int ycnt)
+{ _Work_Data  *work = ( _Work_Data *) ework;
+  _Align_Spec *spec = (_Align_Spec *) espec;
+
+  Path *apath, *bpath;
+
+  { int alen, blen;
+    int maxtp, wsize;
+
+    alen = align->alen;
+    blen = align->blen;
+
+    wsize = (6*sizeof(int) + sizeof(BVEC))*(alen+blen+3);
+    if (wsize >= work->vecmax)
+      enlarge_vector(work,wsize);
+
+    if (alen < blen)
+      maxtp = 2*(blen/spec->trace_space+2);
+    else
+      maxtp = 2*(alen/spec->trace_space+2);
+    wsize = 4*maxtp*sizeof(uint16) + sizeof(Path);
+    if (wsize > work->pntmax)
+      enlarge_points(work,wsize);
+
+    apath = align->path;
+    bpath = (Path *) work->points;
+
+    apath->trace = ((uint16 *) (bpath+1)) + maxtp;
+    bpath->trace = ((uint16 *) apath->trace) +  2*maxtp;
+  }
+
+#ifdef DEBUG_PASSES
+  printf("\n");
+#endif
+
+  { int l, h, a;
+
+    l = xlow-ycnt;
+    h = xhgh-ycnt;
+    a = (xlow+xhgh)/2+ycnt;
+
+    l = forward_wave(work,spec,align,bpath,l,h,a);
+#ifdef DEBUG_PASSES
+    printf("F1 (%d-%d,%d) => (%d,%d) %d\n",xlow,xhgh,ycnt,apath->aepos,apath->bepos,apath->diffs);
+#endif
+
+    reverse_wave(work,spec,align,bpath,l,l,a);
+#ifdef DEBUG_PASSES
+    printf("R1 (%d,%d) => (%d,%d) %d\n",l,ycnt,apath->abpos,apath->bbpos,apath->diffs);
+#endif
+  }
+
+  if (COMP(align->flags))
+    { uint16 *trace = (uint16 *) bpath->trace;
+      uint16  p;
+      int     i, j;
+
+#ifdef DELTAS
+      i = bpath->tlen-2;
+      j = 0;
+      while (j < i)
+        { p = trace[i];
+          trace[i] = trace[j];
+          trace[j] = p;
+          p = trace[i+1];
+          trace[i+1] = trace[j+1];
+          trace[j+1] = p;
+          i -= 2;
+          j += 2;
+        }
+#else
+      i = bpath->tlen-1;
+      j = 0;
+      while (j < i)
+        { p = trace[i];
+          trace[i] = trace[j];
+          trace[j] = p;
+          i -= 1;
+          j += 1;
+        }
+#endif
+    }
+
+#ifdef DEBUG_POINTS
+  { uint16 *trace = (uint16 *) apath->trace;
+    int     a, h;
+
+    printf("\nA-path (%d,%d)->(%d,%d)",apath->abpos,apath->bbpos,apath->aepos,apath->bepos);
+    printf(" %c\n",(COMP(align->flags) ? 'c' : 'n'));
+    a = apath->bbpos;
+#ifdef DELTAS
+    for (h = 1; h < apath->tlen; h += 2)
+      { int dif = trace[h-1];
+        int del = trace[h];
+        a += del;
+        printf("      %d / %d (%d)\n",dif,del,a);
+      }
+#else
+    for (h = 0; h < apath->tlen; h++)
+      { int del = trace[h];
+        a += del;
+        printf("      %d (%d)\n",del,a);
+      }
+#endif
+  }
+
+  { uint16 *trace = (uint16 *) bpath->trace;
+    int     a, h;
+
+    printf("\nB-path (%d,%d)->(%d,%d)",bpath->abpos,bpath->bbpos,bpath->aepos,bpath->bepos);
+    printf(" %c [%d,%d]\n",(COMP(align->flags) ? 'c' : 'n'),align->blen,align->alen);
+    a = bpath->bbpos;
+#ifdef DELTAS
+    for (h = 1; h < bpath->tlen; h += 2)
+      { int dif = trace[h-1];
+        int del = trace[h];
+        a += del;
+        printf("      %d / %d (%d)\n",dif,del,a);
+      }
+#else
+    for (h = 0; h < bpath->tlen; h++)
+      { int del = trace[h];
+        a += del;
+        printf("      %d (%d)\n",del,a);
+      }
+#endif
+  }
+#endif
+
+  return (bpath);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  OVERLAP MANIPULATION                                                                  *
+*                                                                                        *
+\****************************************************************************************/
+
+static int64 PtrSize   = sizeof(void *);
+static int64 OvlIOSize = sizeof(Overlap) - sizeof(void *);
+
+int Read_Overlap(FILE *input, Overlap *ovl)
+{ if (fread( ((char *) ovl) + PtrSize, OvlIOSize, 1, input) != 1)
+    return (1);
+  return (0);
+}
+
+int Read_Trace(FILE *input, Overlap *ovl, int tbytes)
+{ if (tbytes > 0 && ovl->path.tlen > 0)
+    { if (fread(ovl->path.trace, tbytes*ovl->path.tlen, 1, input) != 1)
+        return (1);
+    }
+  return (0);
+}
+
+void Write_Overlap(FILE *output, Overlap *ovl, int tbytes)
+{ fwrite( ((char *) ovl) + PtrSize, OvlIOSize, 1, output);
+  if (ovl->path.trace != NULL)
+    fwrite(ovl->path.trace,tbytes,ovl->path.tlen,output);
+}
+
+void Compress_TraceTo8(Overlap *ovl)
+{ uint16 *t16 = (uint16 *) ovl->path.trace;
+  uint8  *t8  = (uint8  *) ovl->path.trace;
+  int     j;
+
+  for (j = 0; j < ovl->path.tlen; j++)
+    t8[j] = (uint8) (t16[j]);
+}
+
+void Decompress_TraceTo16(Overlap *ovl)
+{ uint16 *t16 = (uint16 *) ovl->path.trace;
+  uint8  *t8  = (uint8  *) ovl->path.trace;
+  int     j;
+
+  for (j = ovl->path.tlen-1; j >= 0; j--)
+    t16[j] = t8[j];
+}
+
+void Print_Overlap(FILE *output, Overlap *ovl, int tbytes, int indent)
+{ int     i;
+
+  fprintf(output,"%*s%d vs. ",indent,"",ovl->aread);
+  if (COMP(ovl->flags))
+    fprintf(output,"c(%d)\n",ovl->bread);
+  else
+    fprintf(output,"%d\n",ovl->bread);
+  fprintf(output,"%*s  [%d,%d] vs [%d,%d] w. %d diffs\n",indent,"",
+                 ovl->path.abpos,ovl->path.aepos,ovl->path.bbpos,ovl->path.bepos,ovl->path.diffs);
+
+  if (tbytes == 1)
+    { uint8 *trace = (uint8 *) (ovl->path.trace);
+      if (trace != NULL)
+#ifdef DELTAS
+        { int p = ovl->path.bbpos + trace[1];
+          fprintf(output,"%*sTrace: %3d/%5d",indent,"",trace[0],p);
+          for (i = 3; i < ovl->path.tlen; i += 2)
+            { if (i%10 == 0)
+                fprintf(output,"\n%*s",indent+6,"");
+              p += trace[i];
+              fprintf(output," %3d/%5d",trace[i-1],p);
+            }
+#else
+        { int p = ovl->path.bbpos + trace[0];
+          fprintf(output,"%*sTrace: %5d",indent,"",p);
+          for (i = 1; i < ovl->path.tlen; i++)
+            { if (i%10 == 0)
+                fprintf(output,"\n%*s",indent+6,"");
+              p += trace[i];
+              fprintf(output," %5d",p);
+            }
+#endif
+          fprintf(output,"\n");
+        }
+    }
+  else
+    { uint16 *trace = (uint16 *) (ovl->path.trace);
+      if (trace != NULL)
+#ifdef DELTAS
+        { int p = ovl->path.bbpos + trace[1];
+          fprintf(output,"%*sTrace: %3d/%5d",indent,"",trace[0],p);
+          for (i = 3; i < ovl->path.tlen; i += 2)
+            { if (i%10 == 0)
+                fprintf(output,"\n%*s",indent+6,"");
+              p += trace[i];
+              fprintf(output," %3d/%5d",trace[i-1],p);
+            }
+#else
+        { int p = ovl->path.bbpos + trace[0];
+          fprintf(output,"%*sTrace: %5d",indent,"",p);
+          for (i = 1; i < ovl->path.tlen; i++)
+            { if (i%10 == 0)
+                fprintf(output,"\n%*s",indent+6,"");
+              p += trace[i];
+              fprintf(output," %5d",p);
+            }
+#endif
+          fprintf(output,"\n");
+        }
+    }
+}
+
+int Check_Trace_Points(Overlap *ovl, int tspace, int verbose, char *fname)
+{ int     i, p; 
+
+#ifdef DELTAS
+  if (((ovl->path.aepos-1)/tspace - ovl->path.abpos/tspace)*2 != ovl->path.tlen-2)
+#else
+  if ((ovl->path.aepos-1)/tspace - ovl->path.abpos/tspace != ovl->path.tlen-1)
+#endif
+    { if (verbose) 
+        fprintf(stderr,"  %s: Wrong number of trace points\n",fname);
+      return (1);
+    }         
+  p = ovl->path.bbpos;
+  if (tspace <= TRACE_XOVR)
+    { uint8 *trace8 = (uint8 *) ovl->path.trace;
+#ifdef DELTAS
+      for (i = 1; i < ovl->path.tlen; i += 2)
+#else
+      for (i = 0; i < ovl->path.tlen; i++)
+#endif
+        p += trace8[i];
+    }
+  else      
+    { uint16 *trace16 = (uint16 *) ovl->path.trace;
+#ifdef DELTAS
+      for (i = 1; i < ovl->path.tlen; i += 2)
+#else
+      for (i = 0; i < ovl->path.tlen; i++)
+#endif
+        p += trace16[i];
+    }
+  if (p != ovl->path.bepos)
+    { if (verbose)
+        fprintf(stderr,"  %s: Trace point sum != aligned interval\n",fname);
+      return (1); 
+    }         
+  return (0);
+}
+
+
+void Flip_Alignment(Alignment *align, int full)
+{ char *aseq  = align->aseq;
+  char *bseq  = align->bseq;
+  int   alen  = align->alen;
+  int   blen  = align->blen;
+  Path *path  = align->path;
+  int   comp  = COMP(align->flags);
+
+  int  *trace = (int *) path->trace;
+  int   tlen  = path->tlen;
+
+  int   i, j, p;
+
+  if (comp)
+    { p = path->abpos;
+      path->abpos = blen - path->bepos;
+      path->bepos = alen - p;
+      p = path->aepos;
+      path->aepos = blen - path->bbpos;
+      path->bbpos = alen - p;
+
+      if (full)
+        { alen += 2;
+          blen += 2;
+
+          for (i = 0; i < tlen; i++)
+            if ((p = trace[i]) < 0)
+              trace[i] = alen + p;
+            else
+              trace[i] = p - blen;
+
+          i = tlen-1;
+          j = 0;
+          while (j < i)
+            { p = trace[i];
+              trace[i] = trace[j];
+              trace[j] = p;
+              i -= 1;
+              j += 1;
+            }
+
+          alen -= 2;
+          blen -= 2;
+        }
+    }
+  else
+    { p = path->abpos;
+      path->abpos = path->bbpos;
+      path->bbpos = p;
+      p = path->aepos;
+      path->aepos = path->bepos;
+      path->bepos = p;
+
+      if (full)
+        for (i = 0; i < tlen; i++)
+          trace[i] = - (trace[i]);
+    }
+
+  align->aseq  = bseq;
+  align->bseq  = aseq;
+  align->alen  = blen;
+  align->blen  = alen;
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  ALIGNMENT PRINTING                                                                    *
+*                                                                                        *
+\****************************************************************************************/
+
+/* Complement the sequence in fragment aseq.  The operation does the
+   complementation/reversal in place.  Calling it a second time on a
+   given fragment restores it to its original state.                */
+
+void Complement_Seq(char *aseq, int len)
+{ char *s, *t;
+  int   c;
+
+  s = aseq;
+  t = aseq + (len-1);
+  while (s < t)
+    { c    = 3 - *s;
+      *s++ = (char) (3 - *t);
+      *t-- = (char) c;
+    }
+  if (s == t)
+    *s = (char) (3 - *s);
+}
+
+/* Print an alignment to file between a and b given in trace (unpacked).
+   Prefix gives the length of the initial prefix of a that is unaligned.  */
+
+static char ToL[8] = { 'a', 'c', 'g', 't', '.', '[', ']', '-' };
+static char ToU[8] = { 'A', 'C', 'G', 'T', '.', '[', ']', '-' };
+
+void Print_Alignment(FILE *file, Alignment *align, Work_Data *ework,
+                     int indent, int width, int border, int upper, int coord)
+{ _Work_Data *work  = (_Work_Data *) ework;
+  int        *trace = align->path->trace;
+  int         tlen  = align->path->tlen;
+
+  char *Abuf, *Bbuf, *Dbuf;
+  int   i, j, o;
+  char *a, *b;
+  char  mtag, dtag;
+  int   prefa, prefb;
+  int   aend, bend;
+  int   sa, sb;
+  int   match, diff;
+  char *N2A;
+
+  if (trace == NULL) return;
+
+#ifdef SHOW_TRACE
+  fprintf(file,"\nTrace:\n");
+  for (i = 0; i < tlen; i++)
+    fprintf(file,"  %3d\n",trace[i]);
+#endif
+
+  o = sizeof(char)*3*(width+1);
+  if (o > work->vecmax)
+    enlarge_vector(work,o);
+
+  if (upper)
+    N2A = ToU;
+  else
+    N2A = ToL;
+
+  Abuf = (char *) work->vector;
+  Bbuf = Abuf + (width+1);
+  Dbuf = Bbuf + (width+1);
+
+  aend = align->path->aepos;
+  bend = align->path->bepos;
+
+  Abuf[width] = Bbuf[width] = Dbuf[width] = '\0';
+                                           /* buffer/output next column */
+#define COLUMN(x,y)							\
+{ int u, v;								\
+  if (o >= width)							\
+    { fprintf(file,"\n");						\
+      fprintf(file,"%*s",indent,"");					\
+      if (coord > 0)							\
+        { if (sa <= aend)						\
+            fprintf(file," %*d",coord,sa);				\
+          else								\
+            fprintf(file," %*s",coord,"");				\
+          fprintf(file," %s\n",Abuf);					\
+          fprintf(file,"%*s %*s %s\n",indent,"",coord,"",Dbuf);		\
+          fprintf(file,"%*s",indent,"");				\
+          if (sb <= bend)						\
+            fprintf(file," %*d",coord,sb);				\
+          else								\
+            fprintf(file," %*s",coord,"");				\
+          fprintf(file," %s",Bbuf);					\
+        }								\
+      else								\
+        { fprintf(file," %s\n",Abuf);					\
+          fprintf(file,"%*s %s\n",indent,"",Dbuf);			\
+          fprintf(file,"%*s %s",indent,"",Bbuf);			\
+        }								\
+      fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));		\
+      o  = 0;								\
+      sa = i;								\
+      sb = j;								\
+      match = diff = 0;							\
+    }									\
+  u = (x);								\
+  v = (y);								\
+  if (u == 4 || v == 4)							\
+    Dbuf[o] = ' ';							\
+  else if (u == v)							\
+    Dbuf[o] = mtag;							\
+  else									\
+    Dbuf[o] = dtag;							\
+  Abuf[o] = N2A[u];							\
+  Bbuf[o] = N2A[v];							\
+  o += 1;								\
+}
+
+  a = align->aseq - 1;
+  b = align->bseq - 1;
+
+  o  = 0;
+  i = j = 1;
+
+  prefa = align->path->abpos;
+  prefb = align->path->bbpos;
+
+  if (prefa > border)
+    { i = prefa-(border-1);
+      prefa = border;
+    }
+  if (prefb > border)
+    { j = prefb-(border-1);
+      prefb = border;
+    }
+
+  sa   = i;
+  sb   = j;
+  mtag = ':';
+  dtag = ':';
+
+  while (prefa > prefb)
+    { COLUMN(a[i],4)
+      i += 1;
+      prefa -= 1;
+    }
+  while (prefb > prefa)
+    { COLUMN(4,b[j])
+      j += 1;
+      prefb -= 1;
+    }
+  while (prefa > 0)
+    { COLUMN(a[i],b[j])
+      i += 1;
+      j += 1;
+      prefa -= 1;
+    }
+
+  mtag = '[';
+  if (prefb > 0)
+    COLUMN(5,5)
+
+  mtag  = '|';
+  dtag  = '*';
+
+  match = diff = 0;
+
+  { int p, c;      /* Output columns of alignment til reach trace end */
+
+    for (c = 0; c < tlen; c++)
+      if ((p = trace[c]) < 0)
+        { p = -p;
+          while (i != p)
+            { COLUMN(a[i],b[j])
+              if (a[i] == b[j])
+                match += 1;
+              else
+                diff += 1;
+              i += 1;
+              j += 1;
+            }
+          COLUMN(7,b[j])
+          j += 1;
+          diff += 1;
+        }
+      else
+        { while (j != p)
+            { COLUMN(a[i],b[j])
+              if (a[i] == b[j])
+                match += 1;
+              else
+                diff += 1;
+              i += 1;
+              j += 1;
+            }
+          COLUMN(a[i],7)
+          i += 1;
+          diff += 1;
+        }
+    p = align->path->aepos;
+    while (i <= p)
+      { COLUMN(a[i],b[j])
+        if (a[i] == b[j])
+          match += 1;
+        else
+          diff += 1;
+        i += 1;
+        j += 1;
+      }
+  }
+
+  { int c;     /* Output remaining column including unaligned suffix */
+
+    mtag = ']';
+    if (a[i] != 4 && b[j] != 4 && border > 0)
+      COLUMN(6,6)
+
+    mtag = ':';
+    dtag = ':';
+
+    c = 0;
+    while (c < border && (a[i] != 4 || b[j] != 4))
+      { if (a[i] != 4)
+          if (b[j] != 4)
+            { COLUMN(a[i],b[j])
+              i += 1;
+              j += 1;
+            }
+          else
+            { COLUMN(a[i],4)
+              i += 1;
+            }
+        else
+          { COLUMN(4,b[j])
+            j += 1;
+          }
+        c += 1;
+      }
+  }
+
+  /* Print remainder of buffered col.s */
+
+  fprintf(file,"\n");
+  fprintf(file,"%*s",indent,"");
+  if (coord > 0)
+    { if (sa <= aend)
+        fprintf(file," %*d",coord,sa);
+      else
+        fprintf(file," %*s",coord,"");
+      fprintf(file," %.*s\n",o,Abuf);
+      fprintf(file,"%*s %*s %.*s\n",indent,"",coord,"",o,Dbuf);
+      fprintf(file,"%*s",indent,"");
+      if (sb <= bend)
+        fprintf(file," %*d",coord,sb);
+      else
+        fprintf(file," %*s",coord,"");
+      fprintf(file," %.*s",o,Bbuf);
+    }
+  else
+    { fprintf(file," %.*s\n",o,Abuf);
+      fprintf(file,"%*s %.*s\n",indent,"",o,Dbuf);
+      fprintf(file,"%*s %.*s",indent,"",o,Bbuf);
+    }
+  if (diff+match > 0)
+    fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));
+  else
+    fprintf(file,"\n");
+
+  fflush(file);
+}
+
+void Print_Reference(FILE *file, Alignment *align, Work_Data *ework,
+                     int indent, int block, int border, int upper, int coord)
+{ _Work_Data *work  = (_Work_Data *) ework;
+  int        *trace = align->path->trace;
+  int         tlen  = align->path->tlen;
+
+  char *Abuf, *Bbuf, *Dbuf;
+  int   i, j, o;
+  char *a, *b;
+  char  mtag, dtag;
+  int   prefa, prefb;
+  int   aend, bend;
+  int   sa, sb, s0;
+  int   match, diff;
+  char *N2A;
+  int   vmax;
+
+  if (trace == NULL) return;
+
+#ifdef SHOW_TRACE
+  fprintf(file,"\nTrace:\n");
+  for (i = 0; i < tlen; i++)
+    fprintf(file,"  %3d\n",trace[i]);
+#endif
+
+  vmax = work->vecmax/3;
+  o = sizeof(char)*6*(block+1);
+  if (o > vmax)
+    { enlarge_vector(work,3*o);
+      vmax = work->vecmax/3;
+    }
+
+  Abuf = (char *) work->vector;
+  Bbuf = Abuf + vmax;
+  Dbuf = Bbuf + vmax;
+
+  if (upper)
+    N2A = ToU;
+  else
+    N2A = ToL;
+
+  aend = align->path->aepos;
+  bend = align->path->bepos;
+
+#define BLOCK(x,y)							\
+{ int u, v;								\
+  if (i%block == 1 && i != s0 && x != 7 && o > 0)			\
+    { fprintf(file,"\n");						\
+      fprintf(file,"%*s",indent,"");					\
+      if (coord > 0)							\
+        { if (sa <= aend)						\
+            fprintf(file," %*d",coord,sa);				\
+          else								\
+            fprintf(file," %*s",coord,"");				\
+          fprintf(file," %.*s\n",o,Abuf);				\
+          fprintf(file,"%*s %*s %.*s\n",indent,"",coord,"",o,Dbuf);	\
+          fprintf(file,"%*s",indent,"");				\
+          if (sb <= bend)						\
+            fprintf(file," %*d",coord,sb);				\
+          else								\
+            fprintf(file," %*s",coord,"");				\
+          fprintf(file," %.*s",o,Bbuf);					\
+        }								\
+      else								\
+        { fprintf(file," %.*s\n",o,Abuf);				\
+          fprintf(file,"%*s %.*s\n",indent,"",o,Dbuf);			\
+          fprintf(file,"%*s %.*s",indent,"",o,Bbuf);			\
+        }								\
+      fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));		\
+      o  = 0;								\
+      sa = i;								\
+      sb = j;								\
+      match = diff = 0;							\
+    }									\
+  u = (x);								\
+  v = (y);								\
+  if (u == 4 || v == 4)							\
+    Dbuf[o] = ' ';							\
+  else if (u == v)							\
+    Dbuf[o] = mtag;							\
+  else									\
+    Dbuf[o] = dtag;							\
+  Abuf[o] = N2A[u];							\
+  Bbuf[o] = N2A[v];							\
+  o += 1;								\
+  if (o >= vmax)							\
+    { enlarge_vector(work,3*o);						\
+      vmax = work->vecmax/3;						\
+      memmove(work->vector+2*vmax,Dbuf,o);				\
+      memmove(work->vector+vmax,Bbuf,o);				\
+      memmove(work->vector,Abuf,o);					\
+      Abuf = (char *) work->vector;					\
+      Bbuf = Abuf + vmax;						\
+      Dbuf = Bbuf + vmax;						\
+    }									\
+}
+
+  a = align->aseq - 1;
+  b = align->bseq - 1;
+
+  o  = 0;
+  i = j = 1;
+
+  prefa = align->path->abpos;
+  prefb = align->path->bbpos;
+
+  if (prefa > border)
+    { i = prefa-(border-1);
+      prefa = border;
+    }
+  if (prefb > border)
+    { j = prefb-(border-1);
+      prefb = border;
+    }
+
+  s0   = i;
+  sa   = i;
+  sb   = j;
+  mtag = ':';
+  dtag = ':';
+
+  while (prefa > prefb)
+    { BLOCK(a[i],4)
+      i += 1;
+      prefa -= 1;
+    }
+  while (prefb > prefa)
+    { BLOCK(4,b[j])
+      j += 1;
+      prefb -= 1;
+    }
+  while (prefa > 0)
+    { BLOCK(a[i],b[j])
+      i += 1;
+      j += 1;
+      prefa -= 1;
+    }
+
+  mtag = '[';
+  if (prefb > 0)
+    BLOCK(5,5)
+
+  mtag  = '|';
+  dtag  = '*';
+
+  match = diff = 0;
+
+  { int p, c;      /* Output columns of alignment til reach trace end */
+
+    for (c = 0; c < tlen; c++)
+      if ((p = trace[c]) < 0)
+        { p = -p;
+          while (i != p)
+            { BLOCK(a[i],b[j])
+              if (a[i] == b[j])
+                match += 1;
+              else
+                diff += 1;
+              i += 1;
+              j += 1;
+            }
+          BLOCK(7,b[j])
+          j += 1;
+          diff += 1;
+        }
+      else
+        { while (j != p)
+            { BLOCK(a[i],b[j])
+              if (a[i] == b[j])
+                match += 1;
+              else
+                diff += 1;
+              i += 1;
+              j += 1;
+            }
+          BLOCK(a[i],7)
+          i += 1;
+          diff += 1;
+        }
+    p = align->path->aepos;
+    while (i <= p)
+      { BLOCK(a[i],b[j])
+        if (a[i] == b[j])
+		match += 1;
+	else
+          diff += 1;
+        i += 1;
+        j += 1;
+      }
+  }
+
+  { int c;     /* Output remaining column including unaligned suffix */
+
+    mtag = ']';
+    if (a[i] != 4 && b[j] != 4 && border > 0)
+      BLOCK(6,6)
+
+    mtag = ':';
+    dtag = ':';
+
+    c = 0;
+    while (c < border && (a[i] != 4 || b[j] != 4))
+      { if (a[i] != 4)
+          if (b[j] != 4)
+            { BLOCK(a[i],b[j])
+              i += 1;
+              j += 1;
+            }
+          else
+            { BLOCK(a[i],4)
+              i += 1;
+            }
+        else
+          { BLOCK(4,b[j])
+            j += 1;
+          }
+        c += 1;
+      }
+  }
+
+  /* Print remainder of buffered col.s */
+
+  fprintf(file,"\n");
+  fprintf(file,"%*s",indent,"");
+  if (coord > 0)
+    { if (sa <= aend)
+        fprintf(file," %*d",coord,sa);
+      else
+        fprintf(file," %*s",coord,"");
+      fprintf(file," %.*s\n",o,Abuf);
+      fprintf(file,"%*s %*s %.*s\n",indent,"",coord,"",o,Dbuf);
+      fprintf(file,"%*s",indent,"");
+      if (sb <= bend)
+        fprintf(file," %*d",coord,sb);
+      else
+        fprintf(file," %*s",coord,"");
+      fprintf(file," %.*s",o,Bbuf);
+    }
+  else
+    { fprintf(file," %.*s\n",o,Abuf);
+      fprintf(file,"%*s %.*s\n",indent,"",o,Dbuf);
+      fprintf(file,"%*s %.*s",indent,"",o,Bbuf);
+    }
+  if (diff+match > 0)
+    fprintf(file," %5.1f%%\n",(100.*diff)/(diff+match));
+  else
+    fprintf(file,"\n");
+
+  fflush(file);
+}
+
+/* Print an ASCII representation of the overlap in align between fragments
+   a and b to given file.                                                  */
+
+static inline void repchar(FILE *file, int symbol, int rep)
+{ while (rep-- > 0)
+    fputc(symbol,file);
+}
+
+void Alignment_Cartoon(FILE *file, Alignment *align, int indent, int coord)
+{ int   alen = align->alen;
+  int   blen = align->blen;
+  Path *path = align->path;
+  int   comp = COMP(align->flags);
+  int   w;
+
+  fprintf(file,"%*s",indent,"");
+  if (path->abpos > 0)
+    fprintf(file,"    %*d ",coord,path->abpos);
+  else
+    fprintf(file,"%*s",coord+5,"");
+  if (path->aepos < alen)
+    fprintf(file,"%*s%d",coord+8,"",alen-path->aepos);
+  fprintf(file,"\n");
+
+  fprintf(file,"%*s",indent,"");
+  if (path->abpos > 0)
+    { fprintf(file,"A ");
+      w = Number_Digits((int64) path->abpos);
+      repchar(file,' ',coord-w);
+      repchar(file,'=',w+3);
+      fputc('+',file);
+      repchar(file,'-',coord+5);
+    }
+  else
+    { fprintf(file,"A %*s",coord+4,"");
+      repchar(file,'-',coord+5);
+    }
+
+  if (path->aepos < alen)
+    { fputc('+',file);
+      w = Number_Digits((int64) (alen-path->aepos));
+      repchar(file,'=',w+2);
+      fputc('>',file);
+      repchar(file,' ',w);
+    }
+  else
+    { fputc('>',file);
+      repchar(file,' ',coord+3);
+    }
+
+  { int asub, bsub;
+
+    asub = path->aepos - path->abpos;
+    bsub = path->bepos - path->bbpos;
+    fprintf(file,"   dif/(len1+len2) = %d/(%d+%d) = %5.2f%%\n",
+                 path->diffs,asub,bsub,(200.*path->diffs)/(asub+bsub));
+  }
+
+  { int   sym1e, sym2e;
+    int   sym1p, sym2p;
+
+    if (comp > 0)
+      { sym1p = '<'; sym2p = '-'; sym1e = '<'; sym2e = '='; }
+    else
+      { sym1p = '-'; sym2p = '>'; sym1e = '='; sym2e = '>'; }
+
+    fprintf(file,"%*s",indent,"");
+    if (path->bbpos > 0)
+      { fprintf(file,"B ");
+        w = Number_Digits((int64) path->bbpos);
+        repchar(file,' ',coord-w);
+        fputc(sym1e,file);
+        repchar(file,'=',w+2);
+        fputc('+',file);
+        repchar(file,'-',coord+5);
+      }
+    else
+      { fprintf(file,"B ");
+        repchar(file,' ',coord+3);
+        fputc(sym1p,file);
+        repchar(file,'-',coord+5);
+      }
+    if (path->bepos < blen)
+      { fprintf(file,"+");
+        w = Number_Digits((int64) (blen-path->bepos));
+        repchar(file,'=',w+2);
+        fprintf(file,"%c\n",sym2e);
+      }
+    else
+      fprintf(file,"%c\n",sym2p);
+  }
+
+  fprintf(file,"%*s",indent,"");
+  if (path->bbpos > 0)
+    fprintf(file,"    %*d ",coord,path->bbpos);
+  else
+    fprintf(file,"%*s",coord+5,"");
+  if (path->bepos < blen)
+    fprintf(file,"%*s%d",coord+8,"",blen-path->bepos);
+  fprintf(file,"\n");
+
+  fflush(file);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  O(ND) trace algorithm                                                                 *
+*                                                                                        *
+\****************************************************************************************/
+
+#ifdef DEBUG_AWAVE
+
+static void print_awave(int *V, int low, int hgh)
+{ int k;
+
+  printf("  [%6d,%6d]: ",low,hgh);
+  for (k = low; k <= hgh; k++)
+    printf(" %3d",V[k]);
+  printf("\n");
+  fflush(stdout);
+}
+
+#endif
+
+#ifdef DEBUG_ALIGN
+
+static int depth = 0;
+
+#endif
+
+typedef struct
+  { int  *Stop;          //  Ongoing stack of alignment indels
+    char *Aabs, *Babs;   //  Absolute base of A and B sequences
+
+    int  **PVF, **PHF;   //  List of waves for iterative np algorithms
+    int   mida,  midb;   //  mid point division for mid-point algorithms
+
+    int   *VF,   *VB;    //  Forward/Reverse waves for nd algorithms
+                         //  (defunct: were used for O(nd) algorithms)
+  } Trace_Waves;
+
+static int dandc_nd(char *A, int M, char *B, int N, Trace_Waves *wave)
+{ int x, y;
+  int D;
+
+#ifdef DEBUG_ALIGN
+  printf("%*s %ld,%ld: %d vs %d\n",depth,"",A-wave->Aabs,B-wave->Babs,M,N);
+#endif
+
+  if (M <= 0)
+    { x = (wave->Aabs-A)-1;
+      for (y = 1; y <= N; y++)
+        { *wave->Stop++ = x;
+#ifdef DEBUG_SCRIPT
+          printf("%*s *I %ld(%ld)\n",depth,"",y+(B-wave->Babs),(A-wave->Aabs)+1);
+#endif
+        }
+      return (N);
+    }
+  if (N <= 0)
+    { y = (B-wave->Babs)+1;
+      for (x = 1; x <= M; x++)
+        { *wave->Stop++ = y;
+#ifdef DEBUG_SCRIPT
+          printf("%*s *D %ld(%ld)\n",depth,"",x+(A-wave->Aabs),(B-wave->Babs)+1);
+#endif
+        }
+      return (M);
+    }
+
+  { int  *VF = wave->VF;
+    int  *VB = wave->VB;
+    int   flow;  //  fhgh == D !
+    int   blow, bhgh;
+    char *a;
+
+    y = 0;
+    if (N < M)
+      while (y < N && B[y] == A[y])
+        y += 1;
+    else
+      { while (y < M && B[y] == A[y])
+          y += 1;
+        if (y >= M && N == M)
+          return (0);
+      }
+
+    flow   = 0;
+    VF[0]  = y;
+    VF[-1] = -2;
+
+    x = N-M;
+    a = A-x;
+    y = N-1;
+    if (N > M)
+      while (y >= x && B[y] == a[y])
+        y -= 1;
+    else
+      while (y >= 0 && B[y] == a[y])
+        y -= 1;
+
+    blow = bhgh = -x;
+    VB += x;
+    VB[blow]   = y;
+    VB[blow-1] = N+1;
+
+    for (D = 1; 1; D += 1)
+      { int   k, r;
+        int   am, ac, ap;
+
+        //  Forward wave
+
+        flow -= 1;
+        am = ac = VF[flow-1] = -2;
+
+        a = A + D;
+        x = M - D;
+        for (k = D; k >= flow; k--)
+          { ap = ac;
+            ac = am+1;
+            am = VF[k-1];
+
+            if (ac < am)
+              if (ap < am)
+                y  = am;
+              else
+                y = ap;
+            else
+              if (ap < ac)
+                y = ac;
+              else
+                y = ap;
+
+            if (blow <= k && k <= bhgh)
+              { r = VB[k];
+                if (y > r)
+                  { D = (D<<1)-1;
+                    if (ap > r)
+                      y = ap;
+                    else if (ac > r)
+                      y = ac;
+                    else
+                      y = r+1;
+                    x = k+y;
+                    goto OVERLAP2;
+                  }
+              }
+
+            if (N < x)
+              while (y < N && B[y] == a[y])
+                y += 1;
+            else
+              while (y < x && B[y] == a[y])
+                y += 1;
+            
+            VF[k] = y;
+            a -= 1;
+            x += 1;
+          }
+
+#ifdef DEBUG_AWAVE
+        print_awave(VF,flow,D);
+#endif
+
+        //  Reverse Wave
+
+        bhgh += 1;
+        blow -= 1;
+	am = ac = VB[blow-1] = N+1;
+
+        a = A + bhgh;
+        x = -bhgh;
+        for (k = bhgh; k >= blow; k--)
+          { ap = ac+1;
+            ac = am;
+            am = VB[k-1];
+
+            if (ac > am)
+              if (ap > am)
+                y  = am;
+              else
+                y = ap;
+            else
+              if (ap > ac)
+                y = ac;
+              else
+                y = ap;
+
+            if (flow <= k && k <= D)
+              { r = VF[k];
+	        if (y <= r)
+                  { D = (D << 1);
+                    if (ap <= r)
+                      y = ap;
+                    else if (ac <= r)
+                      y = ac;
+                    else
+                      y = r;
+                    x = k+y;
+                    goto OVERLAP2;
+                  }
+              }
+
+            y -= 1;
+            if (x > 0)
+              while (y >= x && B[y] == a[y])
+                y -= 1;
+            else
+              while (y >= 0 && B[y] == a[y])
+                y -= 1;
+
+            VB[k] = y;
+            a -= 1;
+            x += 1;
+          }
+
+#ifdef DEBUG_AWAVE
+        print_awave(VB,blow,bhgh);
+#endif
+      }
+  }
+
+OVERLAP2:
+
+#ifdef DEBUG_ALIGN
+  printf("%*s (%d,%d) @ %d\n",depth,"",x,y,D);
+  fflush(stdout);
+#endif
+  if (D > 1)
+    { 
+#ifdef DEBUG_ALIGN
+      depth += 2;
+#endif
+      dandc_nd(A,x,B,y,wave);
+      dandc_nd(A+x,M-x,B+y,N-y,wave);
+#ifdef DEBUG_ALIGN
+      depth -= 2;
+#endif
+    }
+  else if (D == 1)
+    { if (M > N)
+        { *wave->Stop++ = (B-wave->Babs)+y+1;
+#ifdef DEBUG_SCRIPT
+          printf("%*s  D %ld(%ld)\n",depth,"",(A-wave->Aabs)+x,(B-wave->Babs)+y+1);
+#endif
+        }
+      else if (M < N)
+        { *wave->Stop++ = (wave->Aabs-A)-x-1;
+#ifdef DEBUG_SCRIPT
+          printf("%*s  I %ld(%ld)\n",depth,"",(B-wave->Babs)+y,(A-wave->Aabs)+x+1);
+#endif
+        }
+#ifdef DEBUG_SCRIPT
+      else
+        printf("%*s  %ld S %ld\n",depth,"",(wave->Aabs-A)+x,(B-wave->Babs)+y);
+#endif
+    }
+
+  return (D);
+}
+
+
+static void Compute_Trace_ND_ALL(Alignment *align, Work_Data *ework)
+{ _Work_Data *work = (_Work_Data *) ework;
+  Trace_Waves wave;
+
+  int   L, D;
+  int   asub, bsub;
+  Path *path;
+  int  *trace;
+
+  path = align->path;
+  asub = path->aepos-path->abpos;
+  bsub = path->bepos-path->bbpos;
+
+  if (asub < bsub)
+    L = bsub;
+  else
+    L = asub;
+  L *= sizeof(int);
+  if (L > work->tramax)
+    enlarge_trace(work,L);
+
+  trace = wave.Stop = ((int *) work->trace);
+
+  D = 2*(path->diffs + 4)*sizeof(int);
+  if (D > work->vecmax)
+    enlarge_vector(work,D);
+  
+  D = (path->diffs+3)/2;
+  wave.VF = ((int *) work->vector) + (D+1);
+  wave.VB = wave.VF + (2*D+1);
+
+  wave.Aabs = align->aseq;
+  wave.Babs = align->bseq;
+
+  path->diffs = dandc_nd(align->aseq+path->abpos,path->aepos-path->abpos,
+                         align->bseq+path->bbpos,path->bepos-path->bbpos,&wave);
+  path->trace = trace;
+  path->tlen  = wave.Stop - trace;
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  O(NP) tracing algorithms                                                              *
+*                                                                                        *
+\****************************************************************************************/
+
+/* Iterative O(np) algorithm for finding the alignment between two substrings (specified
+     by a Path record).  The variation includes handling substitutions and guarantees
+     to find left-most alignments so that low complexity runs are always aligned in
+     the same way.
+*/
+
+#ifdef DEBUG_ALIGN
+
+static int ToA[4] = { 'a', 'c', 'g', 't' };
+
+#endif
+
+static int iter_np(char *A, int M, char *B, int N, Trace_Waves *wave)
+{ int  **PVF = wave->PVF; 
+  int  **PHF = wave->PHF;
+  int    D;
+  int    del = M-N;
+
+  { int  *F0, *F1, *F2;
+    int  *HF;
+    int   low, hgh, pos;
+
+#ifdef DEBUG_ALIGN
+    printf("\n%*s BASE %ld,%ld: %d vs %d\n",depth,"",A-wave->Aabs,B-wave->Babs,M,N);
+    printf("%*s A = ",depth,"");
+    for (D = 0; D < M; D++)
+      printf("%c",ToA[(int) A[D]]);
+    printf("\n");
+    printf("%*s B = ",depth,"");
+    for (D = 0; D < N; D++)
+      printf("%c",ToA[(int) B[D]]);
+    printf("\n");
+#endif
+
+    if (del >= 0)
+      { low = 0;
+        hgh = del;
+      }
+    else
+      { low = del;
+        hgh = 0;
+      }
+    if (wave->Aabs == wave->Babs)
+      pos = B-A;
+    else
+      pos = -INT32_MAX;
+
+    F1 = PVF[-2];
+    F0 = PVF[-1];
+
+    for (D = low-1; D <= hgh+1; D++)
+      F1[D] = F0[D] = -2;
+    F0[0] = -1;
+
+    low += 1;
+    hgh -= 1;
+
+    for (D = 0; 1; D += 1)
+      { int   k, i, j;
+        int   am, ac, ap;
+        char *a;
+
+        F2 = F1;
+        F1 = F0;
+        F0 = PVF[D];
+        HF = PHF[D];
+
+        if ((D & 0x1) == 0)
+          { hgh += 1;
+            low -= 1;
+            if (low <= pos)
+              low += 1;
+          }
+        F0[hgh+1] = F0[low-1] = -2;
+
+#define FS_MOVE(mdir,pdir)			\
+  ac = F1[k]+1;					\
+  if (ac < am)					\
+    if (ap < am)				\
+      { HF[k] = mdir;				\
+        j = am;					\
+      }						\
+    else					\
+      { HF[k] = pdir;				\
+        j = ap;					\
+      }						\
+  else						\
+    if (ap < ac)				\
+      { HF[k] = 0;				\
+        j = ac;					\
+      }						\
+    else					\
+      { HF[k] = pdir;				\
+        j = ap;					\
+      }						\
+						\
+  if (N < i)					\
+    while (j < N && B[j] == a[j])		\
+      j += 1;					\
+  else						\
+    while (j < i && B[j] == a[j])		\
+      j += 1;					\
+  F0[k] = j;
+
+        j = -2;
+        a = A + hgh;
+        i = M - hgh;
+        for (k = hgh; k > del; k--)
+          { ap = j+1;
+            am = F2[k-1];
+            FS_MOVE(-1,4)
+            a -= 1;
+            i += 1;
+          }
+
+        j = -2;
+        a = A + low;
+        i = M - low;
+        for (k = low; k < del; k++)
+          { ap = F2[k+1]+1;
+            am = j;
+            FS_MOVE(2,1)
+            a += 1;
+            i -= 1;
+          }
+
+        ap = F0[del+1]+1;
+        am = j;
+        FS_MOVE(2,4)
+
+#ifdef DEBUG_AWAVE
+        print_awave(F0,low,hgh);
+        print_awave(HF,low,hgh);
+#endif
+
+        if (F0[del] >= N)
+          break;
+      }
+  }
+
+  { int   k, h, m, e, c;
+    char *a;
+    int   ap = (wave->Aabs-A)-1;
+    int   bp = (B-wave->Babs)+1;
+
+    PHF[0][0] = 3;
+
+    c = N;
+    k = del;
+    e = PHF[D][k];
+    PHF[D][k] = 3;
+    while (e != 3)
+      { h = k+e;
+        if (e > 1)
+          h -= 3;
+        else if (e == 0)
+          D -= 1;
+        else
+          D -= 2;
+        if (h < k)       // => e = -1 or 2
+          { a = A + k;
+            if (k < 0)
+              m = -k;
+            else
+              m = 0;
+            if (PVF[D][h] <= c)
+              c = PVF[D][h]-1;
+            while (c >= m && a[c] == B[c])
+              c -= 1;
+            if (e < 1)  //  => edge is 2, others are 1, and 0
+              { if (c <= PVF[D+2][k+1])
+                  { e = 4;
+                    h = k+1;
+                    D = D+2;
+                  }
+                else if (c == PVF[D+1][k])
+                  { e = 0;
+                    h = k;
+                    D = D+1;
+                  }
+                else
+                  PVF[D][h] = c+1;
+              }
+            else      //   => edge is 0, others are 1, and 2 (if k != del), 0 (otherwise)
+              { if (k == del)
+                  m = D;
+                else
+                  m = D-2;
+                if (c <= PVF[m][k+1])
+                  { if (k == del)
+                      e = 4;
+                    else
+                      e = 1;
+                    h = k+1;
+                    D = m;
+                  }
+                else if (c == PVF[D-1][k])
+                  { e = 0;
+                    h = k;
+                    D = D-1;
+                  }
+                else
+                  PVF[D][h] = c+1;
+              }
+          }
+        m = PHF[D][h];
+        PHF[D][h] = e;
+        e = m;
+
+        k = h;
+      }
+
+    k = D = 0;
+    e = PHF[D][k];
+    while (e != 3)
+      { h = k-e;
+        c = PVF[D][k];
+        if (e > 1)
+          h += 3;
+        else if (e == 0)
+          D += 1;
+        else
+          D += 2;
+        if (h > k)
+          *wave->Stop++ = bp+c;
+        else if (h < k)
+          *wave->Stop++ = ap-(c+k);
+        k = h;
+        e = PHF[D][h];
+      }
+
+#ifdef DEBUG_SCRIPT
+    k = D = 0;
+    e = PHF[D][k];
+    while (e != 3)
+      { h = k-e;
+        c = PVF[D][k];
+        if (e > 1)
+          h += 3;
+        else if (e == 0)
+          D += 1;
+        else
+          D += 2;
+        if (h > k)
+          printf("%*s  D %d(%d)\n",depth,"",(c-k)-(ap-1),c+bp);
+        else if (h < k)
+          printf("%*s  I %d(%d)\n",depth,"",c+(bp-1),(c+k)-ap);
+        else
+          printf("%*s  %d S %d\n",depth,"",(c+k)-(ap+1),c+(bp-1));
+        k = h;
+        e = PHF[D][h];
+      }
+#endif
+  }
+
+  return (D + abs(del));
+}
+
+static int middle_np(char *A, int M, char *B, int N, Trace_Waves *wave)
+{ int  **PVF = wave->PVF; 
+  int  **PHF = wave->PHF;
+  int    D;
+  int    del = M-N;
+
+  { int  *F0, *F1, *F2;
+    int  *HF;
+    int   low, hgh, pos;
+
+#ifdef DEBUG_ALIGN
+    printf("\n%*s BASE %ld,%ld: %d vs %d\n",depth,"",A-wave->Aabs,B-wave->Babs,M,N);
+    printf("%*s A = ",depth,"");
+    for (D = 0; D < M; D++)
+      printf("%c",ToA[(int) A[D]]);
+    printf("\n");
+    printf("%*s B = ",depth,"");
+    for (D = 0; D < N; D++)
+      printf("%c",ToA[(int) B[D]]);
+    printf("\n");
+#endif
+
+    if (del >= 0)
+      { low = 0;
+        hgh = del;
+      }
+    else
+      { low = del;
+        hgh = 0;
+      }
+    if (wave->Aabs == wave->Babs)
+      pos = B-A;
+    else
+      pos = -INT32_MAX;
+
+    F1 = PVF[-2];
+    F0 = PVF[-1];
+
+    for (D = low-1; D <= hgh+1; D++)
+      F1[D] = F0[D] = -2;
+    F0[0] = -1;
+
+    low += 1;
+    hgh -= 1;
+
+    for (D = 0; 1; D += 1)
+      { int   k, i, j;
+        int   am, ac, ap;
+        char *a;
+
+        F2 = F1;
+        F1 = F0;
+        F0 = PVF[D];
+        HF = PHF[D];
+
+        if ((D & 0x1) == 0)
+          { hgh += 1;
+            low -= 1;
+            if (low <= pos)
+              low += 1;
+          }
+        F0[hgh+1] = F0[low-1] = -2;
+
+        j = -2;
+        a = A + hgh;
+        i = M - hgh;
+        for (k = hgh; k > del; k--)
+          { ap = j+1;
+            am = F2[k-1];
+            FS_MOVE(-1,4)
+            a -= 1;
+            i += 1;
+          }
+
+        j = -2;
+        a = A + low;
+        i = M - low;
+        for (k = low; k < del; k++)
+          { ap = F2[k+1]+1;
+            am = j;
+            FS_MOVE(2,1)
+            a += 1;
+            i -= 1;
+          }
+
+        ap = F0[del+1]+1;
+        am = j;
+        FS_MOVE(2,4)
+
+#ifdef DEBUG_AWAVE
+        print_awave(F0,low,hgh);
+        print_awave(HF,low,hgh);
+#endif
+
+        if (F0[del] >= N)
+          break;
+      }
+  }
+
+  { int   k, h, m, e, c;
+    int   d, f;
+    char *a;
+
+    d = D + abs(del);
+    c = N;
+    k = del;
+    for (f = d/2; d > f; d--)
+      { e = PHF[D][k];
+        h = k+e;
+        if (e > 1)
+          h -= 3;
+        else if (e == 0)
+          D -= 1;
+        else
+          D -= 2;
+        if (h < k)       // => e = -1 or 2
+          { a = A + k;
+            if (k < 0)
+              m = -k;
+            else
+              m = 0;
+            if (PVF[D][h] <= c)
+              c = PVF[D][h]-1;
+            while (c >= m && a[c] == B[c])
+              c -= 1;
+            if (e < 1)  //  => edge is 2, others are 1, and 0
+              { if (c <= PVF[D+2][k+1])
+                  { e = 4;
+                    h = k+1;
+                    D = D+2;
+                  }
+                else if (c == PVF[D+1][k])
+                  { e = 0;
+                    h = k;
+                    D = D+1;
+                  }
+                else
+                  PVF[D][h] = c+1;
+              }
+            else      //   => edge is 0, others are 1, and 2 (if k != del), 0 (otherwise)
+              { if (k == del)
+                  m = D;
+                else
+                  m = D-2;
+                if (c <= PVF[m][k+1])
+                  { if (k == del)
+                      e = 4;
+                    else
+                      e = 1;
+                    h = k+1;
+                    D = m;
+                  }
+                else if (c == PVF[D-1][k])
+                  { e = 0;
+                    h = k;
+                    D = D-1;
+                  }
+                else
+                  PVF[D][h] = c+1;
+              }
+          }
+        k = h;
+      }
+
+    wave->midb = (B-wave->Babs) + PVF[D][k];
+    wave->mida = (A-wave->Aabs) + k + PVF[D][k];
+  }
+
+  return (1);
+}
+
+
+/****************************************************************************************\
+*                                                                                        *
+*  COMPUTE_TRACE FLAVORS                                                                 *
+*                                                                                        *
+\****************************************************************************************/
+
+void Compute_Trace_ALL(Alignment *align, Work_Data *ework)
+{ _Work_Data *work = (_Work_Data *) ework;
+  Trace_Waves wave;
+
+  Path *path;
+  char *aseq, *bseq;
+  int   M, N;
+
+  path = align->path;
+  aseq = align->aseq;
+  bseq = align->bseq;
+
+  M = path->aepos-path->abpos;
+  N = path->bepos-path->bbpos;
+  
+  { int64 s;
+    int   d;
+    int   dmax;
+    int   **PVF, **PHF;
+
+    if (M < N)
+      s = N;
+    else
+      s = M;
+    s *= sizeof(int);
+    if (s > work->tramax)
+      enlarge_trace(work,s);
+
+    dmax = path->diffs - abs(M-N);
+
+    s = (dmax+3)*2*((M+N+3)*sizeof(int) + sizeof(int *));
+
+    if (s > 256000000)
+      { Compute_Trace_ND_ALL(align,ework);
+        return;
+      }
+
+    if (s > work->vecmax)
+      enlarge_vector(work,s);
+
+    wave.PVF = PVF = ((int **) (work->vector)) + 2;
+    wave.PHF = PHF = PVF + (dmax+3);
+
+    s = M+N+3;
+    PVF[-2] = ((int *) (PHF + (dmax+1))) + (N+1);
+    for (d = -1; d <= dmax; d++)
+      PVF[d] = PVF[d-1] + s;
+    PHF[-2] = PVF[dmax] + s;
+    for (d = -1; d <= dmax; d++)
+      PHF[d] = PHF[d-1] + s;
+  }
+
+  wave.Stop = ((int *) work->trace);
+  wave.Aabs = aseq;
+  wave.Babs = bseq;
+
+  path->diffs = iter_np(aseq+path->abpos,M,bseq+path->bbpos,N,&wave);
+  path->trace = work->trace;
+  path->tlen  = wave.Stop - ((int *) path->trace);
+}
+
+void Compute_Trace_PTS(Alignment *align, Work_Data *ework, int trace_spacing)
+{ _Work_Data *work = (_Work_Data *) ework;
+  Trace_Waves wave;
+
+  Path   *path;
+  char   *aseq, *bseq;
+  uint16 *points;
+  int     tlen;
+  int     ab, bb;
+  int     ae, be;
+  int     diffs;
+
+  path   = align->path;
+  aseq   = align->aseq;
+  bseq   = align->bseq;
+  tlen   = path->tlen;
+  points = (uint16 *) path->trace;
+
+  { int64 s;
+    int   d;
+    int   M, N;
+    int   dmax, nmax;
+    int   **PVF, **PHF;
+
+    M = path->aepos-path->abpos;
+    N = path->bepos-path->bbpos;
+    if (M < N)
+      s = N*sizeof(int);
+    else
+      s = M*sizeof(int);
+    if (s > work->tramax)
+      enlarge_trace(work,s);
+
+    nmax = 0;
+#ifdef DELTAS
+    dmax = 0;
+    for (d = 1; d < tlen; d += 2)
+      { if (points[d-1] > dmax)
+          dmax = points[d-1];
+#else
+    for (d = 0; d < tlen; d++)
+      {
+#endif
+        if (points[d] > nmax)
+          nmax = points[d];
+      }
+    if (tlen <= 1)
+      nmax = N;
+#ifdef DELTAS
+    if (points[d-1] > dmax)
+      dmax = points[d-1];
+#else
+    dmax = nmax;
+#endif
+
+    s = (dmax+3)*2*((trace_spacing+nmax+3)*sizeof(int) + sizeof(int *));
+
+    if (s > work->vecmax)
+      enlarge_vector(work,s);
+
+    wave.PVF = PVF = ((int **) (work->vector)) + 2;
+    wave.PHF = PHF = PVF + (dmax+3);
+
+    s = trace_spacing+nmax+3;
+    PVF[-2] = ((int *) (PHF + (dmax+1))) + (nmax+1);
+    for (d = -1; d <= dmax; d++)
+      PVF[d] = PVF[d-1] + s;
+    PHF[-2] = PVF[dmax] + s;
+    for (d = -1; d <= dmax; d++)
+      PHF[d] = PHF[d-1] + s;
+  }
+
+  wave.Stop = (int *) (work->trace);
+  wave.Aabs = aseq;
+  wave.Babs = bseq;
+
+  { int i;
+
+    diffs = 0;
+    ab = path->abpos;
+    ae = (ab/trace_spacing)*trace_spacing;
+    bb = path->bbpos;
+#ifdef DELTAS
+    tlen -= 2;
+    for (i = 1; i < tlen; i += 2)
+#else
+    tlen -= 1;
+    for (i = 0; i < tlen; i++)
+#endif
+      { ae = ae + trace_spacing;
+        be = bb + points[i];
+        diffs += iter_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave);
+        ab = ae;
+        bb = be;
+      }
+    ae = path->aepos;
+    be = path->bepos;
+    diffs += iter_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave);
+  }
+
+  path->trace = work->trace;
+  path->tlen  = wave.Stop - ((int *) path->trace);
+  path->diffs = diffs;
+}
+
+void Compute_Trace_MID(Alignment *align, Work_Data *ework, int trace_spacing)
+{ _Work_Data *work = (_Work_Data *) ework;
+  Trace_Waves wave;
+
+  Path   *path;
+  char   *aseq, *bseq;
+  uint16 *points;
+  int     tlen;
+  int     ab, bb;
+  int     ae, be;
+  int     diffs;
+
+  path   = align->path;
+  aseq   = align->aseq;
+  bseq   = align->bseq;
+  tlen   = path->tlen;
+  points = (uint16 *) path->trace;
+
+  { int64 s;
+    int   d;
+    int   M, N;
+    int   dmax, nmax;
+    int   **PVF, **PHF;
+
+    M = path->aepos-path->abpos;
+    N = path->bepos-path->bbpos;
+    if (M < N)
+      s = N*sizeof(int);
+    else
+      s = M*sizeof(int);
+    if (s > work->tramax)
+      enlarge_trace(work,s);
+
+    nmax = 0;
+#ifdef DELTAS
+    dmax = 0;
+    for (d = 1; d < tlen; d += 2)
+      { if (points[d-1] > dmax)
+          dmax = points[d-1];
+#else
+    for (d = 0; d < tlen; d++)
+      {
+#endif
+        if (points[d] > nmax)
+          nmax = points[d];
+      }
+    if (tlen <= 1)
+      nmax = N;
+#ifdef DELTAS
+    if (points[d-1] > dmax)
+      dmax = points[d-1];
+#else
+    dmax = nmax;
+#endif
+
+    s = (dmax+3)*4*((trace_spacing+nmax+3)*sizeof(int) + sizeof(int *));
+
+    if (s > work->vecmax)
+      enlarge_vector(work,s);
+
+    wave.PVF = PVF = ((int **) (work->vector)) + 2;
+    wave.PHF = PHF = PVF + (dmax+3);
+
+    s = trace_spacing+nmax+3;
+    PVF[-2] = ((int *) (PHF + (dmax+1))) + (nmax+1);
+    for (d = -1; d <= dmax; d++)
+      PVF[d] = PVF[d-1] + s;
+    PHF[-2] = PVF[dmax] + s;
+    for (d = -1; d <= dmax; d++)
+      PHF[d] = PHF[d-1] + s;
+  }
+
+  wave.Stop = ((int *) work->trace);
+  wave.Aabs = aseq;
+  wave.Babs = bseq;
+
+  { int i;
+    int as, bs;
+    int af, bf;
+
+    diffs = 0;
+    ab = as = af = path->abpos;
+    ae = (ab/trace_spacing)*trace_spacing;
+    bb = bs = bf = path->bbpos;
+#ifdef DELTAS
+    tlen -= 2;
+    for (i = 1; i < tlen; i += 2) 
+#else
+    tlen -= 1;
+    for (i = 0; i < tlen; i++) 
+#endif
+      { ae = ae + trace_spacing;
+        be = bb + points[i];
+        if (middle_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave))
+          { af = wave.mida;
+            bf = wave.midb;
+            diffs += iter_np(aseq+as,af-as,bseq+bs,bf-bs,&wave);
+            ab = ae;
+            bb = be;
+            as = af;
+            bs = bf;
+          }
+      }
+    ae = path->aepos;
+    be = path->bepos;
+    if (middle_np(aseq+ab,ae-ab,bseq+bb,be-bb,&wave))
+      { af = wave.mida;
+        bf = wave.midb;
+        diffs  += iter_np(aseq+as,af-as,bseq+bs,bf-bs,&wave);
+        as = af;
+        bs = bf;
+      }
+    diffs += iter_np(aseq+af,ae-as,bseq+bf,be-bs,&wave);
+  }
+
+  path->trace = work->trace;
+  path->tlen  = wave.Stop - ((int *) path->trace);
+  path->diffs = diffs;
+}
diff --git a/src/cpp/align.h b/src/cpp/align.h
new file mode 100644
index 0000000..d0b2d5b
--- /dev/null
+++ b/src/cpp/align.h
@@ -0,0 +1,331 @@
+/************************************************************************************\
+*                                                                                    *
+* Copyright (c) 2014, Dr. Eugene W. Myers (EWM). All rights reserved.                *
+*                                                                                    *
+* Redistribution and use in source and binary forms, with or without modification,   *
+* are permitted provided that the following conditions are met:                      *
+*                                                                                    *
+*  · Redistributions of source code must retain the above copyright notice, this     *
+*    list of conditions and the following disclaimer.                                *
+*                                                                                    *
+*  · Redistributions in binary form must reproduce the above copyright notice, this  *
+*    list of conditions and the following disclaimer in the documentation and/or     *
+*    other materials provided with the distribution.                                 *
+*                                                                                    *
+*  · The name of EWM may not be used to endorse or promote products derived from     *
+*    this software without specific prior written permission.                        *
+*                                                                                    *
+* THIS SOFTWARE IS PROVIDED BY EWM ”AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,    *
+* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND       *
+* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EWM BE LIABLE   *
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
+* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS  *
+* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY      *
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING     *
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN  *
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                      *
+*                                                                                    *
+* For any issues regarding this software and its use, contact EWM at:                *
+*                                                                                    *
+*   Eugene W. Myers Jr.                                                              *
+*   Bautzner Str. 122e                                                               *
+*   01099 Dresden                                                                    *
+*   GERMANY                                                                          *
+*   Email: gene.myers at gmail.com                                                      *
+*                                                                                    *
+\************************************************************************************/
+
+/*******************************************************************************************
+ *
+ *  Local alignment module.  Routines for finding local alignments given a seed position,
+ *    representing such an l.a. with its interval and a set of pass-thru points, so that
+ *    a detailed alignment can be efficiently computed on demand.
+ *
+ *  All routines work on a numeric representation of DNA sequences, i.e. 0 for A, 1 for C,
+ *    2 for G, and 3 for T.
+ *
+ *  Author:  Gene Myers
+ *  Date  :  July 2013
+ *
+ ********************************************************************************************/
+
+#include "DB.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TRACE_XOVR 125   //  If the trace spacing is not more than this value, then can
+                         //    and do compress traces pts to 8-bit unsigned ints
+
+#ifndef _A_MODULE
+
+#define _A_MODULE
+
+/*** PATH ABSTRACTION:
+
+     Coordinates are *between* characters where 0 is the tick just before the first char,
+     1 is the tick between the first and second character, and so on.  Our data structure
+     is called a Path refering to its conceptualization in an edit graph.
+
+     A local alignment is specified by the point '(abpos,bbpos)' at which its path in
+     the underlying edit graph starts, and the point '(aepos,bepos)' at which it ends.
+     In otherwords A[abpos+1..aepos] is aligned to B[bbpos+1..bepos] (assuming X[1] is
+     the *first* character of X).
+
+     There are 'diffs' differences in an optimal local alignment between the beginning and
+     end points of the alignment (if computed by Compute_Trace), or nearly so (if computed
+     by Local_Alignment).  
+
+     Optionally, a Path can have additional information about the exact nature of the
+     aligned substrings if the field 'trace' is not NULL.  Trace points to either an
+     array of integers (if computed by a Compute_Trace routine), or an array of unsigned
+     short integers (if computed by Local_Alignment).
+
+     If computed by Local_Alignment 'trace' points at a list of 'tlen' (always even) short
+     values:
+
+            d_0, b_0, d_1, b_1, ... d_n-1, b_n-1, d_n, b_n
+
+     to be interpreted as follows.  The alignment from (abpos,bbpos) to (aepos,bepos)
+     passes through the n trace points for i in [1,n]:
+
+            (a_i,b_i) where a_i = floor(abpos/TS)*TS + i*TS
+                        and b_i = bbpos + (b_0 + b_1 + b_i-1)
+
+     where also let a_0,b_0 = abpos,bbpos and a_(n+1),b_(n+1) = aepos,bepos.  That is, the
+     interior (i.e. i != 0 and i != n+1) trace points pass through every TS'th position of
+     the aread where TS is the "trace spacing" employed when finding the alignment (see
+     New_Align_Spec).  Typically TS is 100.  Then d_i is the number of differences in the
+     portion of the alignment between (a_i,b_i) and (a_i+1,b_i+1).  These trace points allow
+     the Compute_Trace routines to efficiently compute the exact alignment between the two
+     reads by efficiently computing exact alignments between consecutive pairs of trace points.
+     Moreover, the diff values give one an idea of the quality of the alignment along every
+     segment of TS symbols of the aread.
+
+     If computed by a Compute_Trace routine, 'trace' points at a list of 'tlen' integers
+     < i1, i2, ... in > that encodes an exact alignment as follows.  A negative number j
+     indicates that a dash should be placed before A[-j] and a positive number k indicates
+     that a dash should be placed before B[k], where A and B are the two sequences of the
+     overlap.  The indels occur in the trace in the order in which they occur along the
+     alignment.  For a good example of how to "decode" a trace into an alignment, see the
+     code for the routine Print_Alignment.
+
+***/
+
+typedef struct
+  { void     *trace;
+    int       tlen;
+    int       diffs;
+    int       abpos, bbpos;
+    int       aepos, bepos;
+  } Path;
+
+
+/*** ALIGNMENT ABSTRACTION:
+
+     An alignment is modeled by an Alignment record, which in addition to a *pointer* to a
+     'path', gives pointers to the A and B sequences, their lengths, and indicates whether
+     the B-sequence needs to be complemented ('comp' non-zero if so).  The 'trace' pointer
+     of the 'path' subrecord can be either NULL, a list of pass-through points, or an exact
+     trace depending on what routines have been called on the record.
+
+     One can (1) compute a trace, with Compute_Trace, either from scratch if 'path.trace' = NULL,
+     or using the sequence of pass-through points in trace, (2) print an ASCII representation
+     of an alignment, or (3) reverse the roles of A and B, and (4) complement a sequence
+     (which is a reversible process).
+
+     If the alignment record shows the B sequence as complemented, *** THEN IT IS THE
+     RESPONSIBILITY OF THE CALLER *** to make sure that bseq points at a complement of
+     the sequence before calling Compute_Trace or Print_Alignment.  Complement_Seq complements
+     the sequence a of length n.  The operation does the complementation/reversal in place.
+     Calling it a second time on a given fragment restores it to its original state.
+***/
+
+#define COMP(x)  ((x) & 0x1)
+
+#define COMP_FLAG 0x1
+
+typedef struct
+  { Path   *path;
+    uint32  flags;        /* Pipeline status and complementation flags          */
+    char   *aseq;         /* Pointer to A sequence                              */
+    char   *bseq;         /* Pointer to B sequence                              */
+    int     alen;         /* Length of A sequence                               */
+    int     blen;         /* Length of B sequence                               */
+  } Alignment;
+
+void Complement_Seq(char *a, int n);
+
+  /* Many routines like Local_Alignment, Compute_Trace, and Print_Alignment need working
+     storage that is more efficiently reused with each call, rather than being allocated anew
+     with each call.  Each *thread* can create a Work_Data object with New_Work_Data and this
+     object holds and retains the working storage for routines of this module between calls
+     to the routines.  Free_Work_Data frees a Work_Data object and all working storage
+     held by it.
+  */
+
+  typedef void Work_Data;
+
+  Work_Data *New_Work_Data();
+
+  void       Free_Work_Data(Work_Data *work);
+
+  /* Local_Alignment seeks local alignments of a quality determined by a number of parameters.
+     These are coded in an Align_Spec object that can be created with New_Align_Spec and
+     freed with Free_Align_Spec when no longer needed.  There are 4 essential parameters:
+
+     ave_corr:    the average correlation (1 - 2*error_rate) for the sought alignments.  For Pacbio
+                    data we set this to .70 assuming an average of 15% error in each read.
+     trace_space: the spacing interval for keeping trace points and segment differences (see
+                    description of 'trace' for Paths above)
+     freq[4]:     a 4-element vector where afreq[0] = frequency of A, f(A), freq[1] = f(C),
+                    freq[2] = f(G), and freq[3] = f(T).  This vector is part of the header
+                    of every HITS database (see db.h).
+
+     If an alignment cannot reach the boundary of the d.p. matrix with this condition (i.e.
+     overlap), then the last/first 30 columns of the alignment are guaranteed to be
+     suffix/prefix positive at correlation ave_corr * g(freq) where g is an empirically
+     measured function that increases from 1 as the entropy of freq decreases.
+
+     You can get back the original parameters used to create an Align_Spec with the simple
+     utility functions below.
+  */
+
+  typedef void Align_Spec;
+
+  Align_Spec *New_Align_Spec(double ave_corr, int trace_space, float *freq);
+
+  void        Free_Align_Spec(Align_Spec *spec);
+
+  int    Trace_Spacing      (Align_Spec *spec);
+  double Average_Correlation(Align_Spec *spec);
+  float *Base_Frequencies   (Align_Spec *spec);
+
+  /* Local_Alignment finds the longest significant local alignment between the sequences in
+     'align' subject to the alignment criterion given by the Align_Spec 'spec' that passes
+     through one of the points '(xlow-xhgh,y)' within the underlying dynamic programming matrix.
+     The path record of 'align' has its 'trace' filled from the point of view of an overlap between
+     the aread and the bread.  In addition a Path record from the point of view of the bread
+     versus the aread is returned by the function, with this Path's 'trace' filled in
+     appropriately.  The space for the returned path and the two 'trace's are in the
+     working storage supplied by the Work_Data packet and this space is reused with each call,
+     so if one wants to retain the bread-path and the two trace point sequences, then they
+     must be copied to user-allocated storage before calling the routine again.
+  */
+
+  Path *Local_Alignment(Alignment *align, Work_Data *work, Align_Spec *spec,
+                        int xlow, int xhgh, int y);
+
+  /* Given a legitimate Alignment object, Compute_Trace_X computes an exact trace for the alignment.
+     If 'path.trace' is non-NULL, then it is assumed to be a sequence of pass-through points
+     and diff levels computed by Local_Alignment.  In either case 'path.trace' is set
+     to point at an integer array within the storage of the Work_Data packet encoding an
+     exact optimal trace from the start to end points.  If the trace is needed beyond the
+     next call to a routine that sets it, then it should be copied to an array allocated
+     and managed by the caller.
+
+     Compute_Trace_ALL does not require a sequence of pass-through points, as it computes the
+     best alignment between (path->abpos,path->bbpos) and (path->aepos,path->bepos) in the
+     edit graph between the sequences.  Compute_Trace_PTS computes a trace by computing the
+     trace between successive pass through points.  It is much, much faster than Compute_Trace_ALL
+     but at the tradeoff of not necessarily being optimal as pass-through points are not all
+     perfect.  Compute_Trace_MID computes a trace by computing the trace between the mid-points
+     of alignments between two adjacent pairs of pass through points.  It is generally twice as
+     slow as Compute_Trace_PTS, but it produces nearer optimal alignments.
+  */
+
+  void Compute_Trace_ALL(Alignment *align, Work_Data *work);
+  void Compute_Trace_PTS(Alignment *align, Work_Data *work, int trace_spacing);
+  void Compute_Trace_MID(Alignment *align, Work_Data *work, int trace_spacing);
+
+  /* Alignment_Cartoon prints an ASCII representation of the overlap relationhip between the
+     two reads of 'align' to the given 'file' indented by 'indent' space.  Coord controls
+     the display width of numbers, it must be not less than the width of any number to be
+     displayed.
+
+     If the alignment trace is an exact trace, then one can ask Print_Alignment to print an
+     ASCII representation of the alignment 'align' to the file 'file'.  Indent the display
+     by "indent" spaces and put "width" columns per line in the display.  Show "border"
+     characters of sequence on each side of the aligned region.  If upper is non-zero then
+     display bases in upper case.  If coord is greater than 0, then the positions of the
+     first character in A and B in the given row is displayed with a field width given by
+     coord's value.
+
+     Print_Reference is like Print_Alignment but rather than printing exaclty "width" columns
+     per segment, it prints "block" characters of the A sequence in each segment.  This results
+     in segments of different lengths, but is convenient when looking at two alignments involving
+     A as segments are guaranteed to cover the same interval of A in a segment.
+
+     Flip_Alignment modifies align so the roles of A and B are reversed.  If full is off then
+     the trace is ignored, otherwise the trace must be to a full alignment trace and this trace
+     is also appropriately inverted.
+  */
+
+  void Alignment_Cartoon(FILE *file, Alignment *align, int indent, int coord);
+
+  void Print_Alignment(FILE *file, Alignment *align, Work_Data *work,
+                       int indent, int width, int border, int upper, int coord);
+
+  void Print_Reference(FILE *file, Alignment *align, Work_Data *work,
+                       int indent, int block, int border, int upper, int coord);
+
+  void Flip_Alignment(Alignment *align, int full);
+
+
+/*** OVERLAP ABSTRACTION:
+
+     Externally, between modules an Alignment is modeled by an "Overlap" record, which
+     (a) replaces the pointers to the two sequences with their ID's in the HITS data bases,
+     (b) does not contain the length of the 2 sequences (must fetch from DB), and
+     (c) contains its path as a subrecord rather than as a pointer (indeed, typically the
+     corresponding Alignment record points at the Overlap's path sub-record).  The trace pointer
+     is always to a sequence of trace points and can be either compressed (uint8) or
+     uncompressed (uint16).  One can read and write binary records of an "Overlap".
+***/
+
+typedef struct {
+  Path    path;         /* Path: begin- and end-point of alignment + diffs    */
+  uint32  flags;        /* Pipeline status and complementation flags          */
+  int     aread;        /* Id # of A sequence                                 */
+  int     bread;        /* Id # of B sequence                                 */
+} Overlap;
+
+
+  /* Read_Overlap reads the next Overlap record from stream 'input', not including the trace
+     (if any), and without modifying 'ovl's trace pointer.  Read_Trace reads the ensuing trace
+     into the memory pointed at by the trace field of 'ovl'.  It is assumed to be big enough to
+     accommodate the trace where each value take 'tbytes' bytes (1 if uint8 or 2 if uint16).
+
+     Write_Overlap write 'ovl' to stream 'output' followed by its trace vector (if any) that
+     occupies 'tbytes' bytes per value.  
+
+     Print_Overlap prints an ASCII version of the contents of 'ovl' to stream 'output'
+     where the trace occupes 'tbytes' per value and the print out is indented from the left
+     margin by 'indent' spaces.
+
+     Compress_TraceTo8 converts a trace fo 16-bit values to 8-bit values in place, and
+     Decompress_TraceTo16 does the reverse conversion.
+
+     Check_Trace_Points checks that the number of trace points is correct and that the sum
+     of the b-read displacements equals the b-read alignment interval, assuming the trace
+     spacing is 'tspace'.  It reports an error message if there is a problem and 'verbose'
+     is non-zero.  The 'ovl' came from the file names 'fname'.
+  */
+
+  int Read_Overlap(FILE *input, Overlap *ovl);
+  int Read_Trace(FILE *innput, Overlap *ovl, int tbytes);
+
+  void Write_Overlap(FILE *output, Overlap *ovl, int tbytes);
+  void Print_Overlap(FILE *output, Overlap *ovl, int tbytes, int indent);
+
+  void Compress_TraceTo8(Overlap *ovl);
+  void Decompress_TraceTo16(Overlap *ovl);
+
+  int  Check_Trace_Points(Overlap *ovl, int tspace, int verbose, char *fname);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // _A_MODULE
diff --git a/src/cpp/boost.mk b/src/cpp/boost.mk
new file mode 100644
index 0000000..aebea4d
--- /dev/null
+++ b/src/cpp/boost.mk
@@ -0,0 +1,18 @@
+# A URI location for a copy of boost headers only
+URI := https://www.dropbox.com/s/g22iayi83p5gbbq/boost_1_58_0-headersonly.tbz2\?dl\=0
+# Obtain the boost URI and extract it into PWD
+GET_BOOST := curl -L $(URI) | tar xjf -
+
+MYPATH__ := $(dir $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
+
+# Three ways to boost: 1) Internal PBI repot, 2) URI, 3) User specified.
+
+ifdef boost
+BOOST_HEADERS := $(boost)
+else
+BOOST_HEADERS := $(MYPATH__)third-party/boost_1_58_0-headersonly
+endif
+
+ifneq ($(wildcard $(PREBUILT)/boost/boost_1_58_0-headersonly/*),)
+BOOST_HEADERS := $(PREBUILT)/boost/boost_1_58_0-headersonly
+endif
diff --git a/src/cpp/dazcon.cpp b/src/cpp/dazcon.cpp
new file mode 100644
index 0000000..2f70ba6
--- /dev/null
+++ b/src/cpp/dazcon.cpp
@@ -0,0 +1,257 @@
+
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+#include <cstdint>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <thread>
+#include <boost/format.hpp>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/graphviz.hpp>
+
+#define ELPP_CUSTOM_COUT std::cerr
+#define ELPP_THREAD_SAFE 1
+#include "easylogging++.h"
+#include "tclap/CmdLine.h"
+#include "ProgramOpts.hpp"
+#include "Alignment.hpp"
+#include "AlnGraphBoost.hpp"
+#include "DazAlnProvider.hpp"
+#include "BoundedBuffer.hpp"
+
+INITIALIZE_EASYLOGGINGPP
+
+ProgramOpts popts;
+
+typedef std::vector<dagcon::Alignment> AlnVec;
+
+struct TargetData {
+    std::string targSeq;
+    AlnVec alns;
+};
+
+typedef BoundedBuffer<TargetData> TrgBuf;
+typedef BoundedBuffer<std::string> CnsBuf;
+
+void Reader(TrgBuf& trgBuf, AlnProvider* ap) {
+    try {
+        TargetData td;
+        bool hasNext = true;
+        do {
+            hasNext = ap->nextTarget(td.targSeq, td.alns);
+            //for (auto& aln : td.alns)
+            //    std::cerr << aln;
+            if (! td.alns.empty())
+                trgBuf.push(td);
+        } while (hasNext);
+    } 
+    catch (PacBio::DagCon::IOException& e) {
+        std::cerr << e.what();
+        exit(1);
+    }
+
+    // write out sentinals, one per consensus thread
+    TargetData sentinel;
+    for (int i=0; i < popts.threads; i++)
+        trgBuf.push(sentinel);
+}
+
+void Consensus(int id, TrgBuf& trgBuf, CnsBuf& cnsBuf) {
+    TargetData td;
+    trgBuf.pop(&td);
+    std::vector<CnsResult> seqs;
+    el::Loggers::getLogger("Consensus");
+
+    while (td.alns.size() > 0) {
+        if (td.alns.size() < popts.minCov) {
+            trgBuf.pop(&td);
+            continue;
+        }
+        boost::format msg("(%d) calling: %s Alignments: %d");
+        CLOG(INFO, "Consensus") << msg % id % td.alns[0].id % td.alns.size();
+
+        AlnGraphBoost ag(td.targSeq);
+        AlnVec alns = td.alns; 
+        for (auto it = alns.begin(); it != alns.end(); ++it) {
+            if (it->qstr.length() < popts.minLen) continue;
+            dagcon::Alignment aln = normalizeGaps(*it);
+            // XXX: Shouldn't be needed for dazcon, but causes some infinite 
+            // loops in the current consensus code.
+            trimAln(aln, popts.trim);
+            ag.addAln(aln);
+        }
+        CVLOG(3, "Consensus") << "Merging nodes";
+        ag.mergeNodes();
+        CVLOG(3, "Consensus") << "Generating consensus";
+        ag.consensus(seqs, popts.minCov, popts.minLen);
+        for (auto it = seqs.begin(); it != seqs.end(); ++it) {
+            CnsResult result = *it;
+            boost::format fasta(">%s/%d_%d\n%s\n");
+            fasta % alns[0].id % result.range[0] % result.range[1];
+            fasta % result.seq;
+            cnsBuf.push(fasta.str()); 
+        }
+        trgBuf.pop(&td);
+    }
+    boost::format msg("(%d) ending ...");
+    CLOG(INFO, "Consensus") << msg % id;
+    // write out a sentinal
+    cnsBuf.push("");
+}
+
+void Writer(CnsBuf& cnsBuf) {
+    std::string cns;
+    cnsBuf.pop(&cns);
+    int sentinelCount = 0;
+    while (true) {
+        std::cout << cns;
+        if (cns == "" && ++sentinelCount == popts.threads) 
+            break;
+
+        cnsBuf.pop(&cns);
+    }
+}
+
+void parseArgs(int argc, char **argv) {
+
+    try {
+        TCLAP::CmdLine cmd("PBI consensus module", ' ', "0.3");
+        TCLAP::ValueArg<int> threadArg(
+            "j","threads",                 // short, long name
+            "Number of consensus threads", // description
+             false, 4,                     // required, default
+             "int", cmd);
+
+        TCLAP::ValueArg<unsigned int> minCovArg(
+            "c","min-coverage",
+            "Minimum coverage for correction",
+            false, 6, "uint", cmd);
+
+        TCLAP::ValueArg<unsigned int> minLenArg(
+            "l","min-len",
+            "Minimum length for correction",
+            false, 500, "uint", cmd);
+
+        TCLAP::ValueArg<unsigned int> trimArg(
+            "t","trim",
+            "Trim alignments on either size",
+            false, 10, "uint", cmd);
+
+        TCLAP::ValueArg<std::string> alnFileArg(
+            "a","align-file",
+            "Path to the alignments file",
+            true,"","string", cmd);
+
+        TCLAP::ValueArg<std::string> seqFileArg(
+            "s","seq-file",
+            "Path to the sequences file",
+            true,"","string", cmd);
+
+        TCLAP::ValueArg<unsigned int> maxHitArg(
+            "m","max-hit",
+            "Maximum number of hits to pass to consensus",
+            false,85,"uint", cmd);
+
+        TCLAP::SwitchArg sortCovArg("x","coverage-sort",
+            "Sort hits by coverage", cmd, false);
+
+        TCLAP::SwitchArg properOvlArg("o","only-proper-overlaps",
+            "Use only 'proper overlaps', i.e., align to the ends", cmd, false);
+
+        TCLAP::SwitchArg verboseArg("v","verbose",
+            "Turns on verbose logging", cmd, false);
+
+        TCLAP::UnlabeledMultiArg<int> targetArgs(
+            "targets", "Limit consensus to list of target ids", 
+            false, "list of ints", cmd);
+
+        cmd.parse(argc, argv);
+    
+        popts.minCov     = minCovArg.getValue();
+        popts.minLen     = minLenArg.getValue();
+        popts.trim       = trimArg.getValue();
+        popts.alnFile    = alnFileArg.getValue();
+        popts.seqFile    = seqFileArg.getValue();
+        popts.threads    = threadArg.getValue();
+        popts.maxHits    = maxHitArg.getValue();
+        popts.sortCov    = sortCovArg.getValue();
+        popts.properOvls = properOvlArg.getValue();
+        std::vector<int> tgs = targetArgs.getValue();
+        popts.targets.insert(tgs.begin(), tgs.end());
+    } catch (TCLAP::ArgException& e) {
+        std::cerr << "Error " << e.argId() << ": " << e.error() << std::endl;
+        exit(1);
+    }
+}
+
+int main(int argc, char* argv[]) {
+
+    START_EASYLOGGINGPP(argc, argv);
+    parseArgs(argc, argv);
+
+    LOG(INFO) << "Initializing alignment provider";
+    DazAlnProvider* ap;
+    ap = new DazAlnProvider(popts);
+    TrgBuf trgBuf(20);
+    CnsBuf cnsBuf(10);
+
+    std::thread writerThread(Writer, std::ref(cnsBuf));
+
+    std::vector<std::thread> cnsThreads;
+    for (int i=0; i < popts.threads; i++) {
+        std::thread ct(Consensus, i, std::ref(trgBuf), std::ref(cnsBuf));
+        cnsThreads.push_back(std::move(ct));
+    }
+   
+    std::thread readerThread(Reader, std::ref(trgBuf), ap);
+
+    writerThread.join();
+
+    std::vector<std::thread>::iterator it;
+    for (it = cnsThreads.begin(); it != cnsThreads.end(); ++it)
+        it->join();
+
+    readerThread.join();
+
+    delete ap;
+
+    return 0;
+}
diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp
new file mode 100644
index 0000000..d64de00
--- /dev/null
+++ b/src/cpp/main.cpp
@@ -0,0 +1,314 @@
+// Copyright (c) 2011-2015, Pacific Biosciences of California, Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted (subject to the limitations in the
+// disclaimer below) provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// * Neither the name of Pacific Biosciences nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+// GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+// BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+// SUCH DAMAGE.
+
+
+#include <cstdint>
+#include <cassert>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+#include <boost/format.hpp>
+#include <thread>
+#include <boost/graph/graph_traits.hpp>
+#include <boost/graph/adjacency_list.hpp>
+#include <boost/graph/graphviz.hpp>
+
+#define ELPP_CUSTOM_COUT std::cerr
+#define ELPP_THREAD_SAFE 1
+#include "easylogging++.h"
+#include "tclap/CmdLine.h"
+#include "Alignment.hpp"
+#include "AlnGraphBoost.hpp"
+#include "BlasrM5AlnProvider.hpp"
+#include "BoundedBuffer.hpp"
+#include "tuples/TupleMetrics.hpp"
+#include "SimpleAligner.hpp"
+#include "ProgramOpts.hpp"
+
+INITIALIZE_EASYLOGGINGPP
+
+ProgramOpts popts;
+
+bool AlignFirst = false;
+
+typedef std::vector<dagcon::Alignment> AlnVec;
+typedef BoundedBuffer<AlnVec> AlnBuf;
+typedef BoundedBuffer<std::string> CnsBuf;
+
+class Reader {
+    AlnBuf* alnBuf_;
+    const std::string fpath_;
+    size_t minCov_;
+    int nCnsThreads_;
+public:
+    Reader(AlnBuf* b, const std::string fpath, size_t minCov) : 
+        alnBuf_(b), 
+        fpath_(fpath),
+        minCov_(minCov)
+    { }
+
+    void setNumCnsThreads(int n) {
+        nCnsThreads_ = n;
+    }
+
+    void operator()() {
+        el::Logger* logger = el::Loggers::getLogger("Reader");
+        try {
+            AlnProvider* ap;
+            if (fpath_ == "-") { 
+                ap = new BlasrM5AlnProvider(&std::cin);
+            } else {
+                ap = new BlasrM5AlnProvider(fpath_);
+            }
+            AlnVec alns;
+            bool hasNext = true;
+            while (hasNext) {
+                hasNext = ap->nextTarget(alns);
+                size_t cov = alns.size();
+                if (cov == 0) continue;
+                if (cov < minCov_) {
+                    logger->debug("Coverage requirement not met for %v, coverage: %v", 
+                        alns[0].id, alns.size());
+                    continue;
+                }
+                boost::format msg("Consensus candidate: %s");
+                msg % alns[0].id;
+                logger->debug(msg.str());
+                alnBuf_->push(alns);
+            }
+        } 
+        catch (M5Exception::FileOpenError) {
+            logger->error("Error opening file: %s", fpath_);
+        }
+        catch (M5Exception::FormatError err) {
+            logger->error("Format error. Input: %s, Error: %s", 
+                fpath_, err.msg);
+        }
+        catch (M5Exception::SortError err) {
+            logger->error("Input file is not sorted by either target or query.");
+        }
+
+        // write out sentinals, one per consensus thread
+        AlnVec sentinel;
+        for (int i=0; i < nCnsThreads_; i++)
+            alnBuf_->push(sentinel);
+    }
+};
+
+class Consensus {
+    AlnBuf* alnBuf_;
+    CnsBuf* cnsBuf_;
+    size_t minLen_;
+    int minWeight_;
+    SimpleAligner aligner;
+public:
+    Consensus(AlnBuf* ab, CnsBuf* cb, size_t minLen, int minWeight) : 
+        alnBuf_(ab), 
+        cnsBuf_(cb),
+        minLen_(minLen),
+        minWeight_(minWeight)
+    { }
+
+    void operator()() {
+        el::Logger* logger = el::Loggers::getLogger("Consensus");
+        AlnVec alns;
+        alnBuf_->pop(&alns);
+        std::vector<CnsResult> seqs;
+
+        while (alns.size() > 0) {
+            if (alns.size() < popts.minCov) {
+                alnBuf_->pop(&alns);
+                continue;
+            }
+            boost::format msg("Consensus calling: %s Alignments: %d");
+            msg % alns[0].id;
+            msg % alns.size();
+            logger->info(msg.str());
+
+            if (AlignFirst) 
+                for_each(alns.begin(), alns.end(), aligner); 
+
+            AlnGraphBoost ag(alns[0].tlen);
+            for (auto it = alns.begin(); it != alns.end(); ++it) {
+                if (it->qstr.length() < minLen_) continue;
+                dagcon::Alignment aln = normalizeGaps(*it);
+                trimAln(aln, popts.trim);
+                ag.addAln(aln);
+            }
+            ag.mergeNodes();
+            ag.consensus(seqs, minWeight_, minLen_);
+            for (auto it = seqs.begin(); it != seqs.end(); ++it) {
+                CnsResult result = *it;
+                boost::format fasta(">%s/%d_%d\n%s\n");
+                fasta % alns[0].id % result.range[0] % result.range[1];
+                fasta % result.seq;
+                cnsBuf_->push(fasta.str()); 
+            }
+
+            alnBuf_->pop(&alns);
+        }
+        // write out a sentinal
+        cnsBuf_->push("");
+    }
+};
+
+class Writer {
+    CnsBuf* cnsBuf_;
+    int nCnsThreads_;
+public:
+    Writer(CnsBuf* cb) : cnsBuf_(cb) {}
+    
+    void setNumCnsThreads(int n) {
+        nCnsThreads_ = n;
+    }
+
+    void operator()() {
+        std::string cns;
+        cnsBuf_->pop(&cns);
+        int sentinelCount = 0;
+        while (true) {
+            std::cout << cns;
+            if (cns == "" && ++sentinelCount == nCnsThreads_) 
+                break;
+
+            cnsBuf_->pop(&cns);
+        }
+    }
+};
+
+void parseArgs(int argc, char **argv) {
+
+    try {
+        TCLAP::CmdLine cmd("PBI consensus module", ' ', "0.3");
+        TCLAP::ValueArg<int> threadArg(
+            "j","threads",                 // short, long name
+            "Number of consensus threads", // description
+             false, 4,                     // required, default
+             "int", cmd);
+
+        TCLAP::ValueArg<unsigned int> minCovArg(
+            "c","min-coverage",
+            "Minimum coverage for correction",
+            false, 6, "uint", cmd);
+
+        TCLAP::ValueArg<unsigned int> minLenArg(
+            "m","min-length",
+            "Minimum length for correction",
+            false, 500, "uint", cmd);
+
+        TCLAP::ValueArg<unsigned int> trimArg(
+            "t","trim",
+            "Trim alignments on either size",
+            false, 50, "uint", cmd);
+
+        TCLAP::SwitchArg alignArg("a","align",
+            "Align sequences before adding to consensus", cmd, false);
+
+        TCLAP::SwitchArg verboseArg("v","verbose",
+            "Turns on verbose logging", cmd, false);
+
+        TCLAP::UnlabeledValueArg<std::string> inputArg(
+            "input", "Input data", 
+            true, "-","either file path or stdin", cmd);
+
+        cmd.parse(argc, argv);
+    
+        popts.minCov  = minCovArg.getValue();
+        popts.minLen  = minLenArg.getValue();
+        popts.trim    = trimArg.getValue();
+        popts.threads = threadArg.getValue();
+        popts.align   = alignArg.getValue();
+        popts.input   = inputArg.getValue();
+    } catch (TCLAP::ArgException& e) {
+        std::cerr << "Error " << e.argId() << ": " << e.error() << std::endl;
+        exit(1);
+    }
+}
+
+int main(int argc, char* argv[]) {
+    START_EASYLOGGINGPP(argc, argv);
+    parseArgs(argc, argv);
+
+    el::Logger* logger = el::Loggers::getLogger("default");
+
+    if (popts.align) {
+        dagcon::Alignment::parse = parsePre;
+        AlignFirst = true;
+    }
+
+    AlnBuf alnBuf(30);
+    CnsBuf cnsBuf(30);
+
+    if (popts.threads > 1) {
+        logger->info("Multi-threaded. Input: %v, Threads: %v", 
+            popts.input, popts.threads);
+    
+        Writer writer(&cnsBuf);
+        writer.setNumCnsThreads(popts.threads);
+        std::thread writerThread(writer);
+
+        std::vector<std::thread> cnsThreads;
+        for (int i=0; i < popts.threads; i++) {
+            Consensus c(&alnBuf, &cnsBuf, popts.minLen, popts.minCov);
+            cnsThreads.push_back(std::thread(c));
+        }
+
+        Reader reader(&alnBuf, popts.input, popts.minCov);
+        reader.setNumCnsThreads(popts.threads);
+        std::thread readerThread(reader);
+
+        writerThread.join();
+        std::vector<std::thread>::iterator it;
+        for (it = cnsThreads.begin(); it != cnsThreads.end(); ++it)
+            it->join();
+    
+        readerThread.join();
+    } else {
+        logger->info("Single-threaded. Input: %v", popts.input);
+        Reader reader(&alnBuf, popts.input, popts.minCov);
+        reader.setNumCnsThreads(1);
+        Consensus cns(&alnBuf, &cnsBuf, popts.minLen, popts.minCov);
+        Writer writer(&cnsBuf);
+        writer.setNumCnsThreads(1);
+        reader();
+        cns();
+        writer();
+    }
+        
+    return 0;
+}
diff --git a/src/cpp/pbdagcon_wf.sh b/src/cpp/pbdagcon_wf.sh
new file mode 100755
index 0000000..76912da
--- /dev/null
+++ b/src/cpp/pbdagcon_wf.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+#################################################################################$$
+# Copyright (c) 2011-2013, Pacific Biosciences of California, Inc.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted (subject to the limitations in the
+# disclaimer below) provided that the following conditions are met:
+#
+#  * Redistributions of source code must retain the above copyright
+#  notice, this list of conditions and the following disclaimer.
+#
+#  * Redistributions in binary form must reproduce the above
+#  copyright notice, this list of conditions and the following
+#  disclaimer in the documentation and/or other materials provided
+#  with the distribution.
+#
+#  * Neither the name of Pacific Biosciences nor the names of its
+#  contributors may be used to endorse or promote products derived
+#  from this software without specific prior written permission.
+#
+# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+# GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+# BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#################################################################################$$
+
+
+# Simple pbdagcon workflow script.  Written for the benefit of running via
+# smrtpipe so I can communicate pipe errors to the task.  We're overcoming
+# the limitation of smrtpipe forcing tasks to run serially, enabling a new
+# level of pipelining that's extremely efficient in an imperfect world ...
+# However, direct file I/O is faster by default.
+
+tmp=${tmp-"/tmp"}
+
+trap "rm -f $tmp/aln.$$.pre" EXIT SIGINT
+
+echo "Generating pre-alignments"
+echo "m4topre.py $mym4 $allm4 $subreads ${bestn-24} > $tmp/aln.$$.pre"
+
+# generate pre-alignments to a tmp directory
+m4topre.py $mym4 $allm4 $subreads ${bestn-24} > $tmp/aln.$$.pre || exit $?
+
+echo "Correcting reads"
+# pipe it to consensus and generate fasta
+pbdagcon -c ${cov-8} -a -j ${nproc-15} $tmp/aln.$$.pre | tee ${fasta-"corrected.fa"} | \
+# generate a fastq
+awk '{if($0~/>/){sub(/>/,"@",$0);print;}else{l=length($0);q="";while(l--){q=q "9"}printf("%s\n+\n%s\n",$0,q)}}' > ${fastq-"corrected.fq"}
+
+
+# check the status of each piped command and exit non-zero if found
+for exitval in ${PIPESTATUS[*]}
+do
+    if [ $exitval -gt 0 ]
+    then
+        exit $exitval
+    fi
+done
+
+
+exit 0;
diff --git a/src/cpp/pbi.mk b/src/cpp/pbi.mk
new file mode 100644
index 0000000..db3a232
--- /dev/null
+++ b/src/cpp/pbi.mk
@@ -0,0 +1,22 @@
+# Darwin/Clang is unhappy with -L for a non-existent directory, so we
+# cannot use this file to build on OSX.
+# Instead of a bunch of ifdefs, we can conditionally include this file.
+
+# Defines some variables specific to the PBI build env using relative paths
+BASEDIR  ?= ../../../../../..
+PREBUILT := $(BASEDIR)/smrtanalysis/prebuilt.out
+BIFX     := $(BASEDIR)/smrtanalysis/bioinformatics
+BLASR    ?= $(BIFX)/lib/cpp/alignment
+PBDATA   ?= $(BIFX)/lib/cpp/pbdata
+PBBAM    := $(BIFX)/staging/PostPrimary/pbbam
+ZLIB     := $(PREBUILT)/zlib/zlib-1.2.5/$(OS_STRING2)/lib
+
+ifneq ($(wildcard $(PBBAM)/*),)
+EXTRA_INCDIRS := -I$(PBBAM)/include -I$(PBBAM)/third-party/htslib
+EXTRA_LDFLAGS := -L$(PBBAM)/lib -L$(PBBAM)/third-party/htslib -L$(ZLIB)
+
+# We are moving to BAM, which requires extra lib support when compiling against
+# libblasr.  This conditional allows backward compatable compilations with
+# PacificBiosciences/blasr_libcpp.
+EXTRA_LDLIBS = -lpbbam -lhts -lz -lpthread
+endif
diff --git a/src/cpp/third-party/easylogging++.h b/src/cpp/third-party/easylogging++.h
new file mode 100755
index 0000000..f9a67fe
--- /dev/null
+++ b/src/cpp/third-party/easylogging++.h
@@ -0,0 +1,6663 @@
+//
+//  Easylogging++ v9.80
+//  Single-header only, cross-platform logging library for C++ applications
+//
+//  Copyright (c) 2015 muflihun.com
+//
+//  This library is released under the MIT Licence.
+//  http://easylogging.muflihun.com/licence.php
+//
+//  easylogging at muflihun.com
+//
+//  https://github.com/easylogging/easyloggingpp
+//  http://easylogging.muflihun.com
+//  http://muflihun.com
+//
+#ifndef EASYLOGGINGPP_H
+#define EASYLOGGINGPP_H
+// Compilers and C++0x/C++11 Evaluation
+#if defined(__GNUC__)
+#   define ELPP_COMPILER_GCC 1
+#   define ELPP_GCC_VERSION (__GNUC__ * 10000 \
+                               + __GNUC_MINOR__ * 100 \
+                               + __GNUC_PATCHLEVEL__)
+#   if defined(__GXX_EXPERIMENTAL_CXX0X__)
+#      define ELPP_CXX0X 1
+#   elif(ELPP_GCC_VERSION >= 40801)
+#      define ELPP_CXX11 1
+#   endif  // defined(__GXX_EXPERIMENTAL_CXX0X__)
+#endif  // defined(__GNUC__)
+// Visual C++
+#if defined(_MSC_VER)
+#   define ELPP_COMPILER_MSVC 1
+#   define ELPP_CRT_DBG_WARNINGS 1
+#   if (_MSC_VER == 1600)
+#      define ELPP_CXX0X 1
+#   elif(_MSC_VER >= 1700)
+#      define ELPP_CXX11 1
+#   endif  // (_MSC_VER == 1600)
+#endif  // defined(_MSC_VER)
+// Clang++
+#if defined(__clang__) && (__clang__ == 1)
+#   define ELPP_COMPILER_CLANG 1
+#   define ELPP_CLANG_VERSION (__clang_major__ * 10000 \
+                                + __clang_minor__ * 100 \
+                                + __clang_patchlevel__)
+#   if (ELPP_CLANG_VERSION >= 30300)
+#      define ELPP_CXX11 1
+#   endif  // (ELPP_CLANG_VERSION >= 30300)
+#endif  // defined(__clang__) && (__clang__ == 1)
+// MinGW
+#if defined(__MINGW32__) || defined(__MINGW64__)
+#   define ELPP_MINGW 1
+#endif  // defined(__MINGW32__) || defined(__MINGW64__)
+// Cygwin
+#if defined(__CYGWIN__) && (__CYGWIN__ == 1)
+#   define ELPP_CYGWIN 1
+#endif  // defined(__CYGWIN__) && (__CYGWIN__ == 1)
+// Intel C++
+#if defined(__INTEL_COMPILER)
+#   define ELPP_COMPILER_INTEL 1
+#endif
+// Operating System Evaluation
+// Windows
+#if defined(_WIN32) || defined(_WIN64)
+#   define ELPP_OS_WINDOWS 1
+#endif  // defined(_WIN32) || defined(_WIN64)
+// Linux
+#if (defined(__linux) || defined(__linux__))
+#   define ELPP_OS_LINUX 1
+#endif  // (defined(__linux) || defined(__linux__))
+// Mac
+#if defined(__APPLE__)
+#   define ELPP_OS_MAC 1
+#endif  // defined(__APPLE__)
+// FreeBSD
+#if defined(__FreeBSD__)
+#   define ELPP_OS_FREEBSD 1
+#endif
+// Solaris
+#if defined(__sun)
+#   define ELPP_OS_SOLARIS 1
+#endif
+// Unix
+#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS))
+#   define ELPP_OS_UNIX 1
+#endif  // ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS))
+// Android
+#if defined(__ANDROID__)
+#   define ELPP_OS_ANDROID 1
+#endif  // defined(__ANDROID__)
+// Evaluating Cygwin as *nix OS
+#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN
+#   undef ELPP_OS_UNIX
+#   undef ELPP_OS_LINUX
+#   define ELPP_OS_UNIX 1
+#   define ELPP_OS_LINUX 1
+#endif //  !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN
+#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO)
+#   define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout
+#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT)
+#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR)
+#   define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr
+#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT)
+#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL)
+#   define ELPP_INTERNAL_DEBUGGING_ENDL std::endl
+#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT)
+#if !defined(ELPP_INTERNAL_DEBUGGING_MSG)
+#   define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg
+#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT)
+// Internal Assertions and errors
+#if !defined(ELPP_DISABLE_ASSERT)
+#   if (defined(ELPP_DEBUG_ASSERT_FAILURE))
+#      define ELPP_ASSERT(expr, msg) if (!(expr)) { \
+          std::stringstream internalInfoStream; internalInfoStream << msg; \
+          ELPP_INTERNAL_DEBUGGING_OUT_ERROR \
+              << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \
+              << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \
+                  "ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); }
+#   else
+#      define ELPP_ASSERT(expr, msg) if (!(expr)) { \
+          std::stringstream internalInfoStream; internalInfoStream << msg; \
+          ELPP_INTERNAL_DEBUGGING_OUT_ERROR\
+             << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \
+             << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \
+             << ELPP_INTERNAL_DEBUGGING_ENDL; }
+#   endif  // (defined(ELPP_DEBUG_ASSERT_FAILURE))
+#else
+#   define ELPP_ASSERT(x, y)
+#endif  //(!defined(ELPP_DISABLE_ASSERT)
+#if ELPP_COMPILER_MSVC
+#   define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \
+       { char buff[256]; strerror_s(buff, 256, errno); \
+       ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0
+#else
+#   define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \
+        ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0
+#endif  // ELPP_COMPILER_MSVC
+#if defined(ELPP_DEBUG_ERRORS)
+#   if !defined(ELPP_INTERNAL_ERROR)
+#      define ELPP_INTERNAL_ERROR(msg, pe) { \
+          std::stringstream internalInfoStream; internalInfoStream << "<ERROR> " << msg; \
+          ELPP_INTERNAL_DEBUGGING_OUT_ERROR \
+          << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \
+          << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \
+          if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << "    "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0
+#   endif
+#else
+#   undef ELPP_INTERNAL_INFO
+#   define ELPP_INTERNAL_ERROR(msg, pe)
+#endif  // defined(ELPP_DEBUG_ERRORS)
+#if (defined(ELPP_DEBUG_INFO))
+#   if !(defined(ELPP_INTERNAL_INFO_LEVEL))
+#      define ELPP_INTERNAL_INFO_LEVEL 9
+#   endif  // !(defined(ELPP_INTERNAL_INFO_LEVEL))
+#   if !defined(ELPP_INTERNAL_INFO)
+#      define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \
+          std::stringstream internalInfoStream; internalInfoStream << "<INFO> " << msg; \
+          ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \
+             << ELPP_INTERNAL_DEBUGGING_ENDL; }}
+#   endif
+#else
+#   undef ELPP_INTERNAL_INFO
+#   define ELPP_INTERNAL_INFO(lvl, msg)
+#endif  // (defined(ELPP_DEBUG_INFO))
+#if defined(ELPP_STACKTRACE_ON_CRASH)
+#   if (ELPP_COMPILER_GCC && !ELPP_MINGW)
+#      define ELPP_STACKTRACE 1
+#   else
+#      if ELPP_COMPILER_MSVC
+#         pragma message("Stack trace not available for this compiler")
+#      else
+#         warning "Stack trace not available for this compiler";
+#      endif  // ELPP_COMPILER_MSVC
+#   endif  // ELPP_COMPILER_GCC
+#endif  // (defined(ELPP_STACKTRACE_ON_CRASH))
+// Miscellaneous macros
+#define ELPP_UNUSED(x) (void)x
+#if ELPP_OS_UNIX
+// Log file permissions for unix-based systems
+#   define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH
+#endif  // ELPP_OS_UNIX
+#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC
+#   if defined(ELPP_EXPORT_SYMBOLS)
+#      define ELPP_EXPORT __declspec(dllexport)
+#   else
+#      define ELPP_EXPORT __declspec(dllimport)
+#   endif  // defined(ELPP_EXPORT_SYMBOLS)
+#else
+#   define ELPP_EXPORT
+#endif  // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC
+// Some special functions that are VC++ specific
+#undef STRTOK
+#undef STRERROR
+#undef STRCAT
+#undef STRCPY
+#if ELPP_CRT_DBG_WARNINGS
+#   define STRTOK(a, b, c) strtok_s(a, b, c)
+#   define STRERROR(a, b, c) strerror_s(a, b, c)
+#   define STRCAT(a, b, len) strcat_s(a, len, b)
+#   define STRCPY(a, b, len) strcpy_s(a, len, b)
+#else
+#   define STRTOK(a, b, c) strtok(a, b)
+#   define STRERROR(a, b, c) strerror(c)
+#   define STRCAT(a, b, len) strcat(a, b)
+#   define STRCPY(a, b, len) strcpy(a, b)
+#endif
+// Compiler specific support evaluations
+#if (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD)
+#   define ELPP_USE_STD_THREADING 1
+#endif  // (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD)
+#undef ELPP_FINAL
+#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702)
+#   define ELPP_FINAL
+#else
+#   define ELPP_FINAL final
+#endif  // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702)
+#if defined(ELPP_EXPERIMENTAL_ASYNC)
+#   define ELPP_ASYNC_LOGGING 1
+#else
+#   define ELPP_ASYNC_LOGGING 0
+#endif  // defined(ELPP_EXPERIMENTAL_ASYNC)
+#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING
+#   define ELPP_THREADING_ENABLED 1
+#endif  // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING
+// Function macro ELPP_FUNC
+#undef ELPP_FUNC
+#if ELPP_COMPILER_MSVC  // Visual C++
+#   define ELPP_FUNC __FUNCSIG__
+#elif ELPP_COMPILER_GCC  // GCC
+#   define ELPP_FUNC __PRETTY_FUNCTION__
+#elif ELPP_COMPILER_INTEL  // Intel C++
+#   define ELPP_FUNC __PRETTY_FUNCTION__
+#elif ELPP_COMPILER_CLANG  // Clang++
+#   define ELPP_FUNC __PRETTY_FUNCTION__
+#else
+#   if defined(__func__)
+#      define ELPP_FUNC __func__
+#   else
+#      define ELPP_FUNC ""
+#   endif  // defined(__func__)
+#endif  // defined(_MSC_VER)
+#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED
+// Keep following line commented until features are fixed
+#if ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)
+#   define ELPP_VARIADIC_TEMPLATES_SUPPORTED 1
+#endif  // ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)
+// Logging Enable/Disable macros
+#if (!defined(ELPP_DISABLE_LOGS))
+#   define ELPP_LOGGING_ENABLED 1
+#endif  // (!defined(ELPP_DISABLE_LOGS))
+#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG))))
+#   define ELPP_DEBUG_LOG 1
+#else
+#   define ELPP_DEBUG_LOG 0
+#endif  // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG))))
+#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED))
+#   define ELPP_INFO_LOG 1
+#else
+#   define ELPP_INFO_LOG 0
+#endif  // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED))
+#   define ELPP_WARNING_LOG 1
+#else
+#   define ELPP_WARNING_LOG 0
+#endif  // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED))
+#   define ELPP_ERROR_LOG 1
+#else
+#   define ELPP_ERROR_LOG 0
+#endif  // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED))
+#   define ELPP_FATAL_LOG 1
+#else
+#   define ELPP_FATAL_LOG 0
+#endif  // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED))
+#   define ELPP_TRACE_LOG 1
+#else
+#   define ELPP_TRACE_LOG 0
+#endif  // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED))
+#   define ELPP_VERBOSE_LOG 1
+#else
+#   define ELPP_VERBOSE_LOG 0
+#endif  // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED))
+#if (!(ELPP_CXX0X || ELPP_CXX11))
+#   error "Easylogging++ 9.0+ is only compatible with C++0x (or higher) compliant compiler"
+#endif  // (!(ELPP_CXX0X || ELPP_CXX11))
+// Headers
+#if defined(ELPP_SYSLOG)
+#   include <syslog.h>
+#endif  // defined(ELPP_SYSLOG)
+#include <ctime>
+#include <cstring>
+#include <cstdlib>
+#include <cctype>
+#include <cwchar>
+#include <csignal>
+#include <cerrno>
+#include <cstdarg>
+#if defined(ELPP_UNICODE)
+#   include <locale>
+#   if ELPP_OS_WINDOWS
+#      include <codecvt>
+#   endif // ELPP_OS_WINDOWS
+#endif  // defined(ELPP_UNICODE)
+#if ELPP_STACKTRACE
+#   include <cxxabi.h>
+#   include <execinfo.h>
+#endif  // ELPP_STACKTRACE
+#if ELPP_OS_ANDROID
+#   include <sys/system_properties.h>
+#endif  // ELPP_OS_ANDROID
+#if ELPP_OS_UNIX
+#   include <sys/stat.h>
+#   include <sys/time.h>
+#elif ELPP_OS_WINDOWS
+#   include <direct.h>
+#   include <windows.h>
+#   if defined(WIN32_LEAN_AND_MEAN)
+#      if defined(ELPP_WINSOCK2)
+#         include <winsock2.h>
+#	   else
+#         include <winsock.h>
+#      endif // defined(ELPP_WINSOCK2)
+#   endif // defined(WIN32_LEAN_AND_MEAN)
+#endif  // ELPP_OS_UNIX
+#include <string>
+#include <vector>
+#include <map>
+#include <utility>
+#include <functional>
+#include <algorithm>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <memory>
+#include <type_traits>
+#if ELPP_THREADING_ENABLED
+#   if ELPP_USE_STD_THREADING
+#      include <mutex>
+#      include <thread>
+#   else
+#      if ELPP_OS_UNIX
+#         include <pthread.h>
+#      endif  // ELPP_OS_UNIX
+#   endif  // ELPP_USE_STD_THREADING
+#endif  // ELPP_THREADING_ENABLED
+#if ELPP_ASYNC_LOGGING
+#   include <thread>
+#   include <queue>
+#   include <condition_variable>
+#endif  // ELPP_ASYNC_LOGGING 
+#if defined(ELPP_STL_LOGGING)
+// For logging STL based templates
+#   include <list>
+#   include <queue>
+#   include <deque>
+#   include <set>
+#   include <bitset>
+#   include <stack>
+#   if defined(ELPP_LOG_STD_ARRAY)
+#      include <array>
+#   endif  // defined(ELPP_LOG_STD_ARRAY)
+#   if defined(ELPP_LOG_UNORDERED_MAP)
+#      include <unordered_map>
+#   endif  // defined(ELPP_LOG_UNORDERED_MAP)
+#   if defined(ELPP_LOG_UNORDERED_SET)
+#      include <unordered_set>
+#   endif  // defined(ELPP_UNORDERED_SET)
+#endif  // defined(ELPP_STL_LOGGING)
+#if defined(ELPP_QT_LOGGING)
+// For logging Qt based classes & templates
+#   include <QString>
+#   include <QByteArray>
+#   include <QVector>
+#   include <QList>
+#   include <QPair>
+#   include <QMap>
+#   include <QQueue>
+#   include <QSet>
+#   include <QLinkedList>
+#   include <QHash>
+#   include <QMultiHash>
+#   include <QStack>
+#endif  // defined(ELPP_QT_LOGGING)
+#if defined(ELPP_BOOST_LOGGING)
+// For logging boost based classes & templates
+#   include <boost/container/vector.hpp>
+#   include <boost/container/stable_vector.hpp>
+#   include <boost/container/list.hpp>
+#   include <boost/container/deque.hpp>
+#   include <boost/container/map.hpp>
+#   include <boost/container/flat_map.hpp>
+#   include <boost/container/set.hpp>
+#   include <boost/container/flat_set.hpp>
+#endif  // defined(ELPP_BOOST_LOGGING)
+#if defined(ELPP_WXWIDGETS_LOGGING)
+// For logging wxWidgets based classes & templates
+#   include <wx/vector.h>
+#endif  // defined(ELPP_WXWIDGETS_LOGGING)
+// Forward declarations
+namespace el {
+class Logger;
+class LogMessage;
+class PerformanceTrackingData;
+class Loggers;
+class Helpers;
+template <typename T> class Callback;
+class LogDispatchCallback;
+class PerformanceTrackingCallback;
+class LogDispatchData;
+namespace base {
+class Storage;
+class RegisteredLoggers;
+class PerformanceTracker;
+class MessageBuilder;
+class Writer;
+class PErrorWriter;
+class LogDispatcher;
+class DefaultLogBuilder;
+class DefaultLogDispatchCallback;
+#if ELPP_ASYNC_LOGGING
+class AsyncLogDispatchCallback;
+class AsyncDispatchWorker;
+#endif // ELPP_ASYNC_LOGGING
+class DefaultPerformanceTrackingCallback;
+}  // namespace base
+}  // namespace el
+/// @brief Easylogging++ entry namespace
+namespace el {
+/// @brief Namespace containing base/internal functionality used by Easylogging++
+namespace base {
+/// @brief Data types used by Easylogging++
+namespace type {
+#undef ELPP_LITERAL
+#undef ELPP_STRLEN
+#undef ELPP_COUT
+#if defined(ELPP_UNICODE)
+#   define ELPP_LITERAL(txt) L##txt
+#   define ELPP_STRLEN wcslen
+#   if defined ELPP_CUSTOM_COUT
+#      define ELPP_COUT ELPP_CUSTOM_COUT
+#   else
+#      define ELPP_COUT std::wcout
+#   endif  // defined ELPP_CUSTOM_COUT
+typedef wchar_t char_t;
+typedef std::wstring string_t;
+typedef std::wstringstream stringstream_t;
+typedef std::wfstream fstream_t;
+typedef std::wostream ostream_t;
+#else
+#   define ELPP_LITERAL(txt) txt
+#   define ELPP_STRLEN strlen
+#   if defined ELPP_CUSTOM_COUT
+#      define ELPP_COUT ELPP_CUSTOM_COUT
+#   else
+#      define ELPP_COUT std::cout
+#   endif  // defined ELPP_CUSTOM_COUT
+typedef char char_t;
+typedef std::string string_t;
+typedef std::stringstream stringstream_t;
+typedef std::fstream fstream_t;
+typedef std::ostream ostream_t;
+#endif  // defined(ELPP_UNICODE)
+#if defined(ELPP_CUSTOM_COUT_LINE)
+#   define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine)
+#else
+#   define ELPP_COUT_LINE(logLine) logLine << std::flush
+#endif // defined(ELPP_CUSTOM_COUT_LINE)
+typedef unsigned short EnumType;
+typedef std::shared_ptr<base::Storage> StoragePointer;
+typedef int VerboseLevel;
+typedef std::shared_ptr<LogDispatchCallback> LogDispatchCallbackPtr;
+typedef std::shared_ptr<PerformanceTrackingCallback> PerformanceTrackingCallbackPtr;
+}  // namespace type
+/// @brief Internal helper class that prevent copy constructor for class
+///
+/// @detail When using this class simply inherit it privately
+class NoCopy {
+protected:
+    NoCopy(void) {}
+private:
+    NoCopy(const NoCopy&);
+    NoCopy& operator=(const NoCopy&);
+};
+/// @brief Internal helper class that makes all default constructors private.
+///
+/// @detail This prevents initializing class making it static unless an explicit constructor is declared.
+/// When using this class simply inherit it privately
+class StaticClass {
+private:
+    StaticClass(void);
+    StaticClass(const StaticClass&);
+    StaticClass& operator=(const StaticClass&);
+};
+}  // namespace base
+/// @brief Represents enumeration for severity level used to determine level of logging
+///
+/// @detail With Easylogging++, developers may disable or enable any level regardless of
+/// what the severity is. Or they can choose to log using hierarchical logging flag
+enum class Level : base::type::EnumType {
+        /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels
+        Global = 1,
+        /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs.
+        Trace = 2,
+        /// @brief Informational events most useful for developers to debug application
+        Debug = 4,
+        /// @brief Severe error information that will presumably abort application
+        Fatal = 8, 
+        /// @brief Information representing errors in application but application will keep running
+        Error = 16,
+        /// @brief Useful when application has potentially harmful situtaions
+        Warning = 32, 
+        /// @brief Information that can be highly useful and vary with verbose logging level.
+        Verbose = 64,
+        /// @brief Mainly useful to represent current progress of application
+        Info = 128, 
+        /// @brief Represents unknown level
+        Unknown = 1010
+};
+/// @brief Static class that contains helper functions for el::Level
+class LevelHelper : base::StaticClass {
+public:
+    /// @brief Represents minimum valid level. Useful when iterating through enum.
+    static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(Level::Trace);
+    /// @brief Represents maximum valid level. This is used internally and you should not need it.
+    static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(Level::Info);
+    /// @brief Casts level to int, useful for iterating through enum.
+    static base::type::EnumType castToInt(Level level) {
+        return static_cast<base::type::EnumType>(level);
+    }
+    /// @brief Casts int(ushort) to level, useful for iterating through enum.
+    static Level castFromInt(base::type::EnumType l) {
+        return static_cast<Level>(l);
+    }
+    /// @brief Converts level to associated const char*
+    /// @return Upper case string based level.
+    static const char* convertToString(Level level) {
+       // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
+        if (level == Level::Global) return "GLOBAL";
+        if (level == Level::Debug) return "DEBUG";
+        if (level == Level::Info) return "INFO";
+        if (level == Level::Warning) return "WARNING";
+        if (level == Level::Error) return "ERROR";
+        if (level == Level::Fatal) return "FATAL";
+        if (level == Level::Verbose) return "VERBOSE";
+        if (level == Level::Trace) return "TRACE";
+        return "UNKNOWN";
+    }
+    /// @brief Converts from levelStr to Level
+    /// @param levelStr Upper case string based level.
+    ///        Lower case is also valid but providing upper case is recommended.
+    static Level convertFromString(const char* levelStr) {
+        if ((strcmp(levelStr, "GLOBAL") == 0) || (strcmp(levelStr, "global") == 0))
+            return Level::Global;
+        if ((strcmp(levelStr, "DEBUG") == 0) || (strcmp(levelStr, "debug") == 0))
+            return Level::Debug;
+        if ((strcmp(levelStr, "INFO") == 0) || (strcmp(levelStr, "info") == 0))
+            return Level::Info;
+        if ((strcmp(levelStr, "WARNING") == 0) || (strcmp(levelStr, "warning") == 0))
+            return Level::Warning;
+        if ((strcmp(levelStr, "ERROR") == 0) || (strcmp(levelStr, "error") == 0))
+            return Level::Error;
+        if ((strcmp(levelStr, "FATAL") == 0) || (strcmp(levelStr, "fatal") == 0))
+            return Level::Fatal;
+        if ((strcmp(levelStr, "VERBOSE") == 0) || (strcmp(levelStr, "verbose") == 0))
+            return Level::Verbose;
+        if ((strcmp(levelStr, "TRACE") == 0) || (strcmp(levelStr, "trace") == 0))
+            return Level::Trace;
+        return Level::Unknown;
+    }
+    /// @brief Applies specified function to each level starting from startIndex
+    /// @param startIndex initial value to start the iteration from. This is passed as pointer and 
+    ///        is left-shifted so this can be used inside function (fn) to represent current level.
+    /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels.
+    static inline void forEachLevel(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
+        base::type::EnumType lIndexMax = LevelHelper::kMaxValid;
+        do {
+            if (fn()) {
+                break;
+            }
+            *startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
+        } while (*startIndex <= lIndexMax);
+    }
+};
+/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect
+/// of logging
+enum class ConfigurationType : base::type::EnumType {
+   /// @brief Determines whether or not corresponding level and logger of logging is enabled
+   /// You may disable all logs by using el::Level::Global
+    Enabled = 1,
+   /// @brief Whether or not to write corresponding log to log file
+    ToFile = 2,
+   /// @brief Whether or not to write corresponding level and logger log to standard output.
+   /// By standard output meaning termnal, command prompt etc
+    ToStandardOutput = 4,
+   /// @brief Determines format of logging corresponding level and logger.
+    Format = 8,
+   /// @brief Determines log file (full path) to write logs to for correponding level and logger
+    Filename = 16,
+   /// @brief Specifies milliseconds width. Width can be within range (1-6)
+    MillisecondsWidth = 32,
+   /// @brief Determines whether or not performance tracking is enabled.
+   ///
+   /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger
+    PerformanceTracking = 64,
+   /// @brief Specifies log file max size.
+   ///
+   /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will 
+   /// be truncated and re-initiated.
+    MaxLogFileSize = 128,
+   /// @brief Specifies number of log entries to hold until we flush pending log data
+    LogFlushThreshold = 256,
+   /// @brief Represents unknown configuration
+    Unknown = 1010
+};
+/// @brief Static class that contains helper functions for el::ConfigurationType
+class ConfigurationTypeHelper : base::StaticClass {
+public:
+    /// @brief Represents minimum valid configuration type. Useful when iterating through enum.
+    static const base::type::EnumType kMinValid = static_cast<base::type::EnumType>(ConfigurationType::Enabled);
+    /// @brief Represents maximum valid configuration type. This is used internally and you should not need it.
+    static const base::type::EnumType kMaxValid = static_cast<base::type::EnumType>(ConfigurationType::MaxLogFileSize);
+    /// @brief Casts configuration type to int, useful for iterating through enum.
+    static base::type::EnumType castToInt(ConfigurationType configurationType) {
+        return static_cast<base::type::EnumType>(configurationType);
+    }
+    /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum.
+    static ConfigurationType castFromInt(base::type::EnumType c) {
+        return static_cast<ConfigurationType>(c);
+    }
+    /// @brief Converts configuration type to associated const char*
+    /// @returns Upper case string based configuration type.
+    static const char* convertToString(ConfigurationType configurationType) {
+        // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
+        if (configurationType == ConfigurationType::Enabled) return "ENABLED";
+        if (configurationType == ConfigurationType::Filename) return "FILENAME";
+        if (configurationType == ConfigurationType::Format) return "FORMAT";
+        if (configurationType == ConfigurationType::ToFile) return "TO_FILE";
+        if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT";
+        if (configurationType == ConfigurationType::MillisecondsWidth) return "MILLISECONDS_WIDTH";
+        if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING";
+        if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE";
+        if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD";
+        return "UNKNOWN";
+    }
+    /// @brief Converts from configStr to ConfigurationType
+    /// @param configStr Upper case string based configuration type.
+    ///        Lower case is also valid but providing upper case is recommended.
+    static ConfigurationType convertFromString(const char* configStr) {
+        if ((strcmp(configStr, "ENABLED") == 0) || (strcmp(configStr, "enabled") == 0))
+            return ConfigurationType::Enabled;
+        if ((strcmp(configStr, "TO_FILE") == 0) || (strcmp(configStr, "to_file") == 0))
+            return ConfigurationType::ToFile;
+        if ((strcmp(configStr, "TO_STANDARD_OUTPUT") == 0) || (strcmp(configStr, "to_standard_output") == 0))
+            return ConfigurationType::ToStandardOutput;
+        if ((strcmp(configStr, "FORMAT") == 0) || (strcmp(configStr, "format") == 0))
+            return ConfigurationType::Format;
+        if ((strcmp(configStr, "FILENAME") == 0) || (strcmp(configStr, "filename") == 0))
+            return ConfigurationType::Filename;
+        if ((strcmp(configStr, "MILLISECONDS_WIDTH") == 0) || (strcmp(configStr, "milliseconds_width") == 0))
+            return ConfigurationType::MillisecondsWidth;
+        if ((strcmp(configStr, "PERFORMANCE_TRACKING") == 0) || (strcmp(configStr, "performance_tracking") == 0))
+            return ConfigurationType::PerformanceTracking;
+        if ((strcmp(configStr, "MAX_LOG_FILE_SIZE") == 0) || (strcmp(configStr, "max_log_file_size") == 0))
+            return ConfigurationType::MaxLogFileSize;
+        if ((strcmp(configStr, "LOG_FLUSH_THRESHOLD") == 0) || (strcmp(configStr, "log_flush_threshold") == 0))
+            return ConfigurationType::LogFlushThreshold;
+        return ConfigurationType::Unknown;
+    }
+    /// @brief Applies specified function to each configuration type starting from startIndex
+    /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted
+    ///        so this can be used inside function (fn) to represent current configuration type.
+    /// @param fn function to apply with each configuration type. 
+    ///        This bool represent whether or not to stop iterating through configurations.
+    static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn) {
+        base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid;
+        do {
+            if (fn()) {
+                break;
+            }
+            *startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
+        } while (*startIndex <= cIndexMax);
+    }
+};
+/// @brief Flags used while writing logs. This flags are set by user
+enum class LoggingFlag : base::type::EnumType {
+    /// @brief Makes sure we have new line for each container log entry
+    NewLineForContainer = 1,
+    /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose
+    /// logging is allowed via that module.
+    AllowVerboseIfModuleNotSpecified = 2,
+    /// @brief When handling crashes by default, detailed crash reason will be logged as well
+    LogDetailedCrashReason = 4,
+    /// @brief Allows to disable application abortion when logged using FATAL level
+    DisableApplicationAbortOnFatalLog = 8,
+    /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default
+    ImmediateFlush = 16,
+    /// @brief Enables strict file rolling
+    StrictLogFileSizeCheck = 32,
+    /// @brief Make terminal output colorful for supported terminals
+    ColoredTerminalOutput = 64,
+    /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network")
+    MultiLoggerSupport = 128,
+    /// @brief Disables comparing performance tracker's checkpoints
+    DisablePerformanceTrackingCheckpointComparison = 256,
+    /// @brief Disable VModules
+    DisableVModules = 512,
+    /// @brief Disable VModules extensions
+    DisableVModulesExtensions = 1024,
+    /// @brief Enables hierarchical logging
+    HierarchicalLogging = 2048,
+    /// @brief Creates logger automatically when not available
+    CreateLoggerAutomatically = 4096,
+    /// @brief Adds spaces b/w logs that separated by left-shift operator
+    AutoSpacing = 8192,
+    /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only)
+    FixedTimeFormat = 16384
+};
+namespace base {
+/// @brief Namespace containing constants used internally.
+namespace consts {
+    // Level log values - These are values that are replaced in place of %level format specifier
+    static const base::type::char_t* kInfoLevelLogValue     =   ELPP_LITERAL("INFO ");
+    static const base::type::char_t* kDebugLevelLogValue    =   ELPP_LITERAL("DEBUG");
+    static const base::type::char_t* kWarningLevelLogValue  =   ELPP_LITERAL("WARN ");
+    static const base::type::char_t* kErrorLevelLogValue    =   ELPP_LITERAL("ERROR");
+    static const base::type::char_t* kFatalLevelLogValue    =   ELPP_LITERAL("FATAL");
+    static const base::type::char_t* kVerboseLevelLogValue  =   ELPP_LITERAL("VER");
+    static const base::type::char_t* kTraceLevelLogValue    =   ELPP_LITERAL("TRACE");
+    static const base::type::char_t* kInfoLevelShortLogValue     =   ELPP_LITERAL("I");
+    static const base::type::char_t* kDebugLevelShortLogValue    =   ELPP_LITERAL("D");
+    static const base::type::char_t* kWarningLevelShortLogValue  =   ELPP_LITERAL("W");
+    static const base::type::char_t* kErrorLevelShortLogValue    =   ELPP_LITERAL("E");
+    static const base::type::char_t* kFatalLevelShortLogValue    =   ELPP_LITERAL("F");
+    static const base::type::char_t* kVerboseLevelShortLogValue  =   ELPP_LITERAL("V");
+    static const base::type::char_t* kTraceLevelShortLogValue    =   ELPP_LITERAL("T");
+    // Format specifiers - These are used to define log format
+    static const base::type::char_t* kAppNameFormatSpecifier          =      ELPP_LITERAL("%app");
+    static const base::type::char_t* kLoggerIdFormatSpecifier         =      ELPP_LITERAL("%logger");
+    static const base::type::char_t* kThreadIdFormatSpecifier         =      ELPP_LITERAL("%thread");
+    static const base::type::char_t* kSeverityLevelFormatSpecifier    =      ELPP_LITERAL("%level");
+    static const base::type::char_t* kSeverityLevelShortFormatSpecifier    =      ELPP_LITERAL("%levshort");
+    static const base::type::char_t* kDateTimeFormatSpecifier         =      ELPP_LITERAL("%datetime");
+    static const base::type::char_t* kLogFileFormatSpecifier          =      ELPP_LITERAL("%file");
+    static const base::type::char_t* kLogFileBaseFormatSpecifier      =      ELPP_LITERAL("%fbase");
+    static const base::type::char_t* kLogLineFormatSpecifier          =      ELPP_LITERAL("%line");
+    static const base::type::char_t* kLogLocationFormatSpecifier      =      ELPP_LITERAL("%loc");
+    static const base::type::char_t* kLogFunctionFormatSpecifier      =      ELPP_LITERAL("%func");
+    static const base::type::char_t* kCurrentUserFormatSpecifier      =      ELPP_LITERAL("%user");
+    static const base::type::char_t* kCurrentHostFormatSpecifier      =      ELPP_LITERAL("%host");
+    static const base::type::char_t* kMessageFormatSpecifier          =      ELPP_LITERAL("%msg");
+    static const base::type::char_t* kVerboseLevelFormatSpecifier     =      ELPP_LITERAL("%vlevel");
+    static const char* kDateTimeFormatSpecifierForFilename            =      "%datetime";
+    // Date/time
+    static const char* kDays[7]                         =      { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
+    static const char* kDaysAbbrev[7]                   =      { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+    static const char* kMonths[12]                      =      { "January", "February", "March", "Apri", "May", "June", "July", "August",
+            "September", "October", "November", "December" };
+    static const char* kMonthsAbbrev[12]                =      { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+    static const char* kDefaultDateTimeFormat           =      "%Y-%M-%d %H:%m:%s,%g";
+    static const char* kDefaultDateTimeFormatInFilename =      "%Y-%M-%d_%H-%m";
+    static const int kYearBase                          =      1900;
+    static const char* kAm                              =      "AM";
+    static const char* kPm                              =      "PM";
+    // Miscellaneous constants
+    static const char* kDefaultLoggerId                        =      "default";
+    static const char* kPerformanceLoggerId                    =      "performance";
+    static const char* kSysLogLoggerId                         =      "syslog";
+    static const char* kNullPointer                            =      "nullptr";
+    static const char  kFormatSpecifierChar                    =      '%';
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+    static const char  kFormatSpecifierCharValue               =      'v';
+#endif  // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+    static const unsigned int kMaxLogPerContainer              =      100;
+    static const unsigned int kMaxLogPerCounter                =      100000;
+    static const unsigned int  kDefaultMillisecondsWidth       =      3;
+    static const base::type::VerboseLevel kMaxVerboseLevel     =      9;
+    static const char* kUnknownUser                            =      "user";
+    static const char* kUnknownHost                            =      "unknown-host";
+#if defined(ELPP_DEFAULT_LOG_FILE)
+    static const char* kDefaultLogFile                         =      ELPP_DEFAULT_LOG_FILE;
+#else
+#   if ELPP_OS_UNIX
+#      if ELPP_OS_ANDROID
+    static const char* kDefaultLogFile                         =      "logs/myeasylog.log";
+#      else
+    static const char* kDefaultLogFile                         =      "logs/myeasylog.log";
+#      endif  // ELPP_OS_ANDROID
+#   elif ELPP_OS_WINDOWS
+    static const char* kDefaultLogFile                         =      "logs\\myeasylog.log";
+#   endif  // ELPP_OS_UNIX
+#endif  // defined(ELPP_DEFAULT_LOG_FILE)
+#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+    static const char* kDefaultLogFileParam                    =      "--default-log-file";
+#endif  // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+    static const char* kLoggingFlagsParam                      =      "--logging-flags";
+#endif  // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+#if ELPP_OS_WINDOWS
+    static const char* kFilePathSeperator                      =      "\\";
+#else
+    static const char* kFilePathSeperator                      =      "/";
+#endif  // ELPP_OS_WINDOWS
+    static const char* kValidLoggerIdSymbols                   =      "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
+    static const char* kConfigurationComment                   =      "##";
+    static const char* kConfigurationLevel                     =      "*";
+    static const char* kConfigurationLoggerId                  =      "--";
+    static const std::size_t kSourceFilenameMaxLength          =      100;
+    static const std::size_t kSourceLineMaxLength              =      10;
+    static const Level kPerformanceTrackerDefaultLevel         =      Level::Info;
+    const struct {
+        double value;
+        const base::type::char_t* unit;
+    } kTimeFormats[] = {
+       { 1000.0f, ELPP_LITERAL("mis") },
+       { 1000.0f, ELPP_LITERAL("ms") },
+       { 60.0f, ELPP_LITERAL("seconds") },
+       { 60.0f, ELPP_LITERAL("minutes") },
+       { 24.0f, ELPP_LITERAL("hours") },
+       { 7.0f, ELPP_LITERAL("days") }
+    };
+    static const int kTimeFormatsCount                           =      sizeof(kTimeFormats) / sizeof(kTimeFormats[0]);
+    const struct {
+        int numb;
+        const char* name;
+        const char* brief;
+        const char* detail;
+    } kCrashSignals[] = {
+        // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..)
+        { SIGABRT, "SIGABRT", "Abnormal termination",
+                "Program was abnormally terminated." },
+        { SIGFPE, "SIGFPE", "Erroneous arithmetic operation",
+                "Arithemetic operation issue such as division by zero or operation resulting in overflow." },
+        { SIGILL, "SIGILL", "Illegal instruction",
+                "Generally due to a corruption in the code or to an attempt to execute data."},
+        { SIGSEGV, "SIGSEGV", "Invalid access to memory",
+                "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." },
+        { SIGINT, "SIGINT", "Interactive attention signal",
+                 "Interruption generated (generally) by user or operating system." },
+    };
+    static const int kCrashSignalsCount                          =      sizeof(kCrashSignals) / sizeof(kCrashSignals[0]);
+}  // namespace consts
+}  // namespace base
+typedef std::function<void(const char*, std::size_t)> PreRollOutCallback;
+namespace base {
+static inline void defaultPreRollOutCallback(const char*, std::size_t) {}
+/// @brief Enum to represent timestamp unit
+enum class TimestampUnit : base::type::EnumType {
+    Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5
+};
+/// @brief Format flags used to determine specifiers that are active for performance improvements.
+enum class FormatFlags : base::type::EnumType {
+    DateTime = 1<<1, LoggerId = 1<<2, File = 1<<3, Line = 1<<4, Location = 1<<5, Function = 1<<6,
+    User = 1<<7, Host = 1<<8, LogMessage = 1<<9, VerboseLevel = 1<<10, AppName = 1<<11, ThreadId = 1<<12,
+    Level = 1<<13, FileBase = 1<<14, LevelShort = 1<<15
+};
+/// @brief A milliseconds width class containing actual width and offset for date/time
+class MillisecondsWidth {
+public:
+    MillisecondsWidth(void) { init(base::consts::kDefaultMillisecondsWidth); }
+    explicit MillisecondsWidth(int width) { init(width); }
+    bool operator==(const MillisecondsWidth& msWidth) { return m_width == msWidth.m_width && m_offset == msWidth.m_offset; }
+    int m_width; unsigned int m_offset;
+private:
+    void init(int width) {
+        if (width < 1 || width > 6) {
+            width = base::consts::kDefaultMillisecondsWidth;
+        }
+        m_width = width;
+        switch (m_width) {
+        case 3: m_offset = 1000; break;
+        case 4: m_offset = 100; break;
+        case 5: m_offset = 10; break;
+        case 6: m_offset = 1; break;
+        default: m_offset = 1000; break;
+        }
+    }
+};
+/// @brief Namespace containing utility functions/static classes used internally
+namespace utils {
+/// @brief Deletes memory safely and points to null
+template <typename T>
+static inline
+typename std::enable_if<std::is_pointer<T*>::value, void>::type
+safeDelete(T*& pointer) {
+    if (pointer == nullptr)
+        return;
+    delete pointer;
+    pointer = nullptr;
+}
+/// @brief Gets value of const char* but if it is nullptr, a string nullptr is returned
+static inline const char* charPtrVal(const char* pointer) {
+    return pointer == nullptr ? base::consts::kNullPointer : pointer;
+}
+/// @brief Aborts application due with user-defined status
+static inline void abort(int status, const std::string& reason = std::string()) {
+    // Both status and reason params are there for debugging with tools like gdb etc
+    ELPP_UNUSED(status);
+    ELPP_UNUSED(reason);
+#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
+   // Ignore msvc critical error dialog - break instead (on debug mode)
+    _asm int 3
+#else
+    ::abort();
+#endif  // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG)
+}
+/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation
+/// Use these function as <pre>flag = bitwise::Or<MyEnum>(MyEnum::val1, flag);</pre>
+namespace bitwise {
+template <typename Enum>
+static inline base::type::EnumType And(Enum e, base::type::EnumType flag) {
+    return static_cast<base::type::EnumType>(flag) & static_cast<base::type::EnumType>(e);
+}
+template <typename Enum>
+static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) {
+    return static_cast<base::type::EnumType>(flag) & ~(static_cast<base::type::EnumType>(e));
+}
+template <typename Enum>
+static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) {
+    return static_cast<base::type::EnumType>(flag) | static_cast<base::type::EnumType>(e);
+}
+}  // namespace bitwise
+template <typename Enum>
+static inline void addFlag(Enum e, base::type::EnumType* flag) {
+    *flag = base::utils::bitwise::Or<Enum>(e, *flag);
+}
+template <typename Enum>
+static inline void removeFlag(Enum e, base::type::EnumType* flag) {
+    *flag = base::utils::bitwise::Not<Enum>(e, *flag);
+}
+template <typename Enum>
+static inline bool hasFlag(Enum e, base::type::EnumType flag) {
+    return base::utils::bitwise::And<Enum>(e, flag) > 0x0;
+}
+}  // namespace utils
+namespace threading {
+#if ELPP_THREADING_ENABLED
+#   if !ELPP_USE_STD_THREADING
+namespace internal {
+/// @brief A mutex wrapper for compiler that dont yet support std::mutex
+class Mutex : base::NoCopy {
+public:
+    Mutex(void) {
+#   if ELPP_OS_UNIX
+        pthread_mutex_init(&m_underlyingMutex, nullptr);
+#   elif ELPP_OS_WINDOWS
+        InitializeCriticalSection(&m_underlyingMutex);
+#   endif  // ELPP_OS_UNIX
+    }
+
+    virtual ~Mutex(void) {
+#   if ELPP_OS_UNIX
+        pthread_mutex_destroy(&m_underlyingMutex);
+#   elif ELPP_OS_WINDOWS
+        DeleteCriticalSection(&m_underlyingMutex);
+#   endif  // ELPP_OS_UNIX
+    }
+
+    inline void lock(void) {
+#   if ELPP_OS_UNIX
+        pthread_mutex_lock(&m_underlyingMutex);
+#   elif ELPP_OS_WINDOWS
+        EnterCriticalSection(&m_underlyingMutex);
+#   endif  // ELPP_OS_UNIX
+    }
+
+    inline bool try_lock(void) {
+#   if ELPP_OS_UNIX
+        return (pthread_mutex_trylock(&m_underlyingMutex) == 0);
+#   elif ELPP_OS_WINDOWS
+        return TryEnterCriticalSection(&m_underlyingMutex);
+#   endif  // ELPP_OS_UNIX
+    }
+
+    inline void unlock(void) {
+#   if ELPP_OS_UNIX
+        pthread_mutex_unlock(&m_underlyingMutex);
+#   elif ELPP_OS_WINDOWS
+        LeaveCriticalSection(&m_underlyingMutex);
+#   endif  // ELPP_OS_UNIX
+    }
+
+private:
+#   if ELPP_OS_UNIX
+    pthread_mutex_t m_underlyingMutex;
+#   elif ELPP_OS_WINDOWS
+    CRITICAL_SECTION m_underlyingMutex;
+#   endif  // ELPP_OS_UNIX
+};
+/// @brief Scoped lock for compiler that dont yet support std::lock_guard
+template <typename M>
+class ScopedLock : base::NoCopy {
+public:
+    explicit ScopedLock(M& mutex) {
+        m_mutex = &mutex;
+        m_mutex->lock();
+    }
+
+    virtual ~ScopedLock(void) {
+        m_mutex->unlock();
+    }
+private:
+    M* m_mutex;
+    ScopedLock(void);
+};
+} // namespace internal
+/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned.
+static inline std::string getCurrentThreadId(void) {
+    std::stringstream ss;
+#      if (ELPP_OS_WINDOWS)
+    ss << GetCurrentThreadId();
+#      endif  // (ELPP_OS_WINDOWS)
+    return ss.str();
+}
+static inline void msleep(int) {
+    // No implementation for non std::thread version
+}
+typedef base::threading::internal::Mutex Mutex;
+typedef base::threading::internal::ScopedLock<base::threading::Mutex> ScopedLock;
+#   else
+/// @brief Gets ID of currently running threading using std::this_thread::get_id()
+static inline std::string getCurrentThreadId(void) {
+    std::stringstream ss;
+    ss << std::this_thread::get_id();
+    return ss.str();
+}
+static inline void msleep(int ms) {
+    // Only when async logging enabled - this is because async is strict on compiler
+#if ELPP_ASYNC_LOGGING
+    std::this_thread::sleep_for(std::chrono::milliseconds(ms));
+#endif  // ELPP_ASYNC_LOGGING
+}
+typedef std::mutex Mutex;
+typedef std::lock_guard<std::mutex> ScopedLock;
+#   endif  // !ELPP_USE_STD_THREADING
+#else
+namespace internal {
+/// @brief Mutex wrapper used when multi-threading is disabled.
+class NoMutex : base::NoCopy {
+public:
+    NoMutex(void) {}
+    inline void lock(void) {}
+    inline bool try_lock(void) { return true; }
+    inline void unlock(void) {}
+};
+/// @brief Lock guard wrapper used when multi-threading is disabled.
+template <typename Mutex>
+class NoScopedLock : base::NoCopy {
+public:
+    explicit NoScopedLock(Mutex&) {
+    }
+    virtual ~NoScopedLock(void) {
+    }
+private:
+    NoScopedLock(void);
+};
+}  // namespace internal
+static inline std::string getCurrentThreadId(void) {
+    return std::string();
+}
+static inline void msleep(int) {
+    // No custom implementation
+}
+typedef base::threading::internal::NoMutex Mutex;
+typedef base::threading::internal::NoScopedLock<base::threading::Mutex> ScopedLock;
+#endif  // ELPP_THREADING_ENABLED
+/// @brief Base of thread safe class, this class is inheritable-only
+class ThreadSafe {
+public:
+    virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); }
+    virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); }
+    virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; }
+protected:
+    ThreadSafe(void) {}
+    virtual ~ThreadSafe(void) {}
+private:
+    base::threading::Mutex m_mutex;
+};
+}  // namespace threading
+namespace utils {
+class File : base::StaticClass {
+public:
+    /// @brief Creates new out file stream for specified filename.
+    /// @return Pointer to newly created fstream or nullptr
+    static base::type::fstream_t* newFileStream(const std::string& filename) {
+        base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), 
+            base::type::fstream_t::out | base::type::fstream_t::app);
+#if defined(ELPP_UNICODE)
+        std::locale elppUnicodeLocale("");
+#if ELPP_OS_WINDOWS
+        std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16<wchar_t>);
+        elppUnicodeLocale = elppUnicodeLocaleWindows;
+#endif
+        fs->imbue(elppUnicodeLocale);
+#endif  // defined(ELPP_UNICODE)
+        if (fs->is_open()) {
+            fs->flush();
+        } else {
+            base::utils::safeDelete(fs);
+            ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true);
+        }
+        return fs;
+    }
+
+    /// @brief Gets size of file provided in stream
+    static std::size_t getSizeOfFile(base::type::fstream_t* fs) {
+        if (fs == nullptr) {
+            return 0;
+        }
+        std::streampos currPos = fs->tellg();
+        fs->seekg(0, fs->end);
+        std::size_t size = static_cast<std::size_t>(fs->tellg());
+        fs->seekg(currPos);
+        return size;
+    }
+
+    /// @brief Determines whether or not provided path exist in current file system
+    static inline bool pathExists(const char* path, bool considerFile = false) {
+        if (path == nullptr) {
+            return false;
+        }
+#if ELPP_OS_UNIX
+        ELPP_UNUSED(considerFile);
+        struct stat st;
+        return (stat(path, &st) == 0);
+#elif ELPP_OS_WINDOWS
+        DWORD fileType = GetFileAttributesA(path);
+        if (fileType == INVALID_FILE_ATTRIBUTES) {
+            return false;
+        }
+        return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true);
+#endif  // ELPP_OS_UNIX
+    }
+
+    /// @brief Creates specified path on file system
+    /// @param path Path to create.
+    static bool createPath(const std::string& path) {
+        if (path.empty()) {
+            return false;
+        }
+        if (base::utils::File::pathExists(path.c_str())) {
+            return true;
+        }
+        int status = -1;
+
+        char* currPath = const_cast<char*>(path.c_str());
+        std::string builtPath = std::string();
+#if ELPP_OS_UNIX
+        if (path[0] == '/') {
+            builtPath = "/";
+        }
+        currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0);
+#elif ELPP_OS_WINDOWS
+        // Use secure functions API
+        char* nextTok_ = nullptr;
+        currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_);
+        ELPP_UNUSED(nextTok_);
+#endif  // ELPP_OS_UNIX
+        while (currPath != nullptr) {
+            builtPath.append(currPath);
+            builtPath.append(base::consts::kFilePathSeperator);
+#if ELPP_OS_UNIX
+            status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS);
+            currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0);
+#elif ELPP_OS_WINDOWS
+            status = _mkdir(builtPath.c_str());
+            currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_);
+#endif  // ELPP_OS_UNIX
+        }
+        if (status == -1) {
+            ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true);
+            return false;
+        }
+        return true;
+    }
+    /// @brief Extracts path of filename with leading slash
+    static std::string extractPathFromFilename(const std::string& fullPath,
+            const char* seperator = base::consts::kFilePathSeperator) {
+        if ((fullPath == "") || (fullPath.find(seperator) == std::string::npos)) {
+            return fullPath;
+        }
+        std::size_t lastSlashAt = fullPath.find_last_of(seperator);
+        if (lastSlashAt == 0) {
+            return std::string(seperator);
+        }
+        return fullPath.substr(0, lastSlashAt + 1);
+    }
+    /// @brief builds stripped filename and puts it in buff
+    static void buildStrippedFilename(const char* filename, char buff[], 
+            std::size_t limit = base::consts::kSourceFilenameMaxLength) {
+        std::size_t sizeOfFilename = strlen(filename);
+        if (sizeOfFilename >= limit) {
+            filename += (sizeOfFilename - limit);
+            if (filename[0] != '.' && filename[1] != '.') {  // prepend if not already
+                filename += 3;  // 3 = '..'
+                STRCAT(buff, "..", limit);
+            }
+        }
+        STRCAT(buff, filename, limit);
+    }
+    /// @brief builds base filename and puts it in buff
+    static void buildBaseFilename(const std::string& fullPath, char buff[], 
+            std::size_t limit = base::consts::kSourceFilenameMaxLength,
+            const char* seperator = base::consts::kFilePathSeperator) {
+        const char *filename = fullPath.c_str();
+        std::size_t lastSlashAt = fullPath.find_last_of(seperator);
+        filename += lastSlashAt ? lastSlashAt+1 : 0;
+        std::size_t sizeOfFilename = strlen(filename);
+        if (sizeOfFilename >= limit) {
+            filename += (sizeOfFilename - limit);
+            if (filename[0] != '.' && filename[1] != '.') {  // prepend if not already
+                filename += 3;  // 3 = '..'
+                STRCAT(buff, "..", limit);
+            }
+        }
+        STRCAT(buff, filename, limit);
+    }
+};
+/// @brief String utilities helper class used internally. You should not use it.
+class Str : base::StaticClass {
+public:
+    /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues.
+    static inline bool isDigit(char c) {
+        return c >= '0' && c <= '9';
+    }
+
+    /// @brief Matches wildcards, '*' and '?' only supported.
+    static bool wildCardMatch(const char* str, const char* pattern) {
+        while (*pattern) {
+            switch (*pattern) {
+            case '?':
+                if (!*str)
+                    return false;
+                ++str;
+                ++pattern;
+                break;
+            case '*':
+                if (wildCardMatch(str, pattern + 1))
+                    return true;
+                if (*str && wildCardMatch(str + 1, pattern))
+                    return true;
+                return false;
+                break;
+            default:
+                if (*str++ != *pattern++)
+                    return false;
+                break;
+            }
+        }
+        return !*str && !*pattern;
+    }
+
+    /// @brief Trims string from start
+    /// @param [in,out] str String to trim
+    static inline std::string& ltrim(std::string& str) {
+        str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun<int, int>(&std::isspace))));
+        return str;
+    }
+
+    /// @brief Trim string from end
+    /// @param [in,out] str String to trim
+    static inline std::string& rtrim(std::string& str) {
+        str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun<int, int>(&std::isspace))).base(), str.end());
+        return str;
+    }
+
+    /// @brief Trims string from left and right
+    /// @param [in,out] str String to trim
+    static inline std::string& trim(std::string& str) {
+        return ltrim(rtrim(str));
+    }
+
+    /// @brief Determines whether or not str starts with specified string
+    /// @param str String to check
+    /// @param start String to check against
+    /// @return Returns true if starts with specified string, false otherwise
+    static inline bool startsWith(const std::string& str, const std::string& start) {
+        return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0);
+    }
+
+    /// @brief Determines whether or not str ends with specified string
+    /// @param str String to check
+    /// @param end String to check against
+    /// @return Returns true if ends with specified string, false otherwise
+    static inline bool endsWith(const std::string& str, const std::string& end) {
+        return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0);
+    }
+
+    /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance.
+    /// @param [in,out] str String to replace from
+    /// @param replaceWhat Character to replace
+    /// @param replaceWith Character to replace with
+    /// @return Modified version of str
+    static inline std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith) {
+        std::replace(str.begin(), str.end(), replaceWhat, replaceWith);
+        return str;
+    }
+
+    /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place
+    /// @param str String to replace from
+    /// @param replaceWhat Character to replace
+    /// @param replaceWith Character to replace with
+    /// @return Modified (original) str
+    static inline std::string& replaceAll(std::string& str, const std::string& replaceWhat, // NOLINT
+            const std::string& replaceWith) {
+        if (replaceWhat == replaceWith)
+            return str;
+        std::size_t foundAt = std::string::npos;
+        while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) {
+            str.replace(foundAt, replaceWhat.length(), replaceWith);
+        }
+        return str;
+    }
+
+    static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT
+            const base::type::string_t& replaceWith) {
+        std::size_t foundAt = base::type::string_t::npos;
+        while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) {
+            if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) {
+                str.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
+                ++foundAt;
+            } else {
+                str.replace(foundAt, replaceWhat.length(), replaceWith);
+                return;
+            }
+        }
+    }
+#if defined(ELPP_UNICODE)
+    static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT
+            const std::string& replaceWith) {
+        replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end()));
+    }
+#endif  // defined(ELPP_UNICODE)
+    /// @brief Converts string to uppercase
+    /// @param str String to convert
+    /// @return Uppercase string
+    static inline std::string& toUpper(std::string& str) {
+        std::transform(str.begin(), str.end(), str.begin(), ::toupper);
+        return str;
+    }
+
+    /// @brief Compares cstring equality - uses strcmp
+    static inline bool cStringEq(const char* s1, const char* s2) {
+        if (s1 == nullptr && s2 == nullptr) return true;
+        if (s1 == nullptr || s2 == nullptr) return false;
+        return strcmp(s1, s2) == 0;
+    }
+
+    /// @brief Compares cstring equality (case-insensitive) - uses toupper(char)
+    /// Dont use strcasecmp because of CRT (VC++)
+    static bool cStringCaseEq(const char* s1, const char* s2) {
+        if (s1 == nullptr && s2 == nullptr) return true;
+        if (s1 == nullptr || s2 == nullptr) return false;
+        if (strlen(s1) != strlen(s2)) return false;
+        while (*s1 != '\0' && *s2 != '\0') {
+            if (::toupper(*s1) != ::toupper(*s2)) return false;
+            ++s1;
+            ++s2;
+        }
+        return true;
+    }
+
+    /// @brief Returns true if c exist in str
+    static inline bool contains(const char* str, char c) {
+        for (; *str; ++str) {
+            if (*str == c)
+                return true;
+        }
+        return false;
+    }
+
+    static inline char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true) {
+        char localBuff[10] = "";
+        char* p = localBuff + sizeof(localBuff) - 2;
+        if (n > 0) {
+            for (; n > 0 && p > localBuff && len > 0; n /= 10, --len)
+                *--p = static_cast<char>(n % 10 + '0');
+        } else {
+            *--p = '0';
+            --len;
+        }
+        if (zeroPadded)
+            while (p > localBuff && len-- > 0) *--p = static_cast<char>('0');
+        return addToBuff(p, buf, bufLim);
+    }
+
+    static inline char* addToBuff(const char* str, char* buf, const char* bufLim) {
+        while ((buf < bufLim) && ((*buf = *str++) != '\0'))
+            ++buf;
+        return buf;
+    }
+
+    static inline char* clearBuff(char buff[], std::size_t lim) {
+        STRCPY(buff, "", lim);
+        ELPP_UNUSED(lim);  // For *nix we dont have anything using lim in above STRCPY macro
+        return buff;
+    }
+
+    /// @brief Converst wchar* to char*
+    ///        NOTE: Need to free return value after use!
+    static char* wcharPtrToCharPtr(const wchar_t* line) {
+        std::size_t len_ = wcslen(line) + 1;
+        char* buff_ = static_cast<char*>(malloc(len_ + 1));
+#      if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
+        std::wcstombs(buff_, line, len_);
+#      elif ELPP_OS_WINDOWS
+        std::size_t convCount_ = 0;
+        mbstate_t mbState_;
+        ::memset(static_cast<void*>(&mbState_), 0, sizeof(mbState_));
+        wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_);
+#      endif  // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS)
+       return buff_;
+    }
+};
+/// @brief Operating System helper static class used internally. You should not use it.
+class OS : base::StaticClass {
+public:
+#if ELPP_OS_WINDOWS
+    /// @brief Gets environment variables for Windows based OS. 
+    ///        We are not using <code>getenv(const char*)</code> because of CRT deprecation
+    /// @param varname Variable name to get environment variable value for
+    /// @return If variable exist the value of it otherwise nullptr
+    static const char* getWindowsEnvironmentVariable(const char* varname) {
+        const DWORD bufferLen = 50;
+        static char buffer[bufferLen];
+        if (GetEnvironmentVariableA(varname, buffer, bufferLen)) {
+            return buffer;
+        }
+        return nullptr;
+    }
+#endif  // ELPP_OS_WINDOWS
+#if ELPP_OS_ANDROID
+    /// @brief Reads android property value
+    static inline std::string getProperty(const char* prop) {
+        char propVal[PROP_VALUE_MAX + 1];
+        int ret = __system_property_get(prop, propVal);
+        return ret == 0 ? std::string() : std::string(propVal);
+    }
+
+    /// @brief Reads android device name
+    static std::string getDeviceName(void) {
+        std::stringstream ss;
+        std::string manufacturer = getProperty("ro.product.manufacturer");
+        std::string model = getProperty("ro.product.model");
+        if (manufacturer.empty() || model.empty()) {
+            return std::string();
+        }
+        ss << manufacturer << "-" << model;
+        return ss.str();
+    }
+#endif  // ELPP_OS_ANDROID
+
+    /// @brief Runs command on terminal and returns the output.
+    ///
+    /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned.
+    /// @param command Bash command
+    /// @return Result of bash output or empty string if no result found.
+    static const std::string getBashOutput(const char* command) {
+#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
+        if (command == nullptr) {
+            return std::string();
+        }
+        FILE* proc = nullptr;
+        if ((proc = popen(command, "r")) == nullptr) {
+            ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true);
+            return std::string();
+        }
+        char hBuff[4096];
+        if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) {
+            pclose(proc);
+            if (hBuff[strlen(hBuff) - 1] == '\n') {
+                hBuff[strlen(hBuff) - 1] = '\0';
+            }
+            return std::string(hBuff);
+        }
+        return std::string();
+#else
+        ELPP_UNUSED(command);
+        return std::string();
+#endif  // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN)
+    }
+
+    /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++)
+    /// @param variableName Environment variable name
+    /// @param defaultVal If no environment variable or value found the value to return by default
+    /// @param alternativeBashCommand If environment variable not found what would be alternative bash command
+    ///        in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami'
+    static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand = nullptr) {
+#if ELPP_OS_UNIX
+        const char* val = getenv(variableName);
+#elif ELPP_OS_WINDOWS
+        const char* val = getWindowsEnvironmentVariable(variableName);
+#endif  // ELPP_OS_UNIX
+        if ((val == nullptr) || ((strcmp(val, "") == 0))) {
+#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
+           // Try harder on unix-based systems
+            std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand);
+            if (valBash.empty()) {
+                return std::string(defaultVal);
+            } else {
+                return valBash;
+            }
+#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX
+            ELPP_UNUSED(alternativeBashCommand);
+            return std::string(defaultVal);
+#endif  // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH)
+        }
+        return std::string(val);
+    }
+   /// @brief Gets current username.
+    static inline std::string currentUser(void) {
+#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
+        return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami");
+#elif ELPP_OS_WINDOWS
+        return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser);
+#elif ELPP_OS_ANDROID
+        ELPP_UNUSED(base::consts::kUnknownUser);
+        return std::string("android");
+#else
+        return std::string();
+#endif  // ELPP_OS_UNIX && !ELPP_OS_ANDROID
+    }
+
+    /// @brief Gets current host name or computer name.
+    ///
+    /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen
+    static inline std::string currentHost(void) {
+#if ELPP_OS_UNIX && !ELPP_OS_ANDROID
+        return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname");
+#elif ELPP_OS_WINDOWS
+        return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost);
+#elif ELPP_OS_ANDROID
+        ELPP_UNUSED(base::consts::kUnknownHost);
+        return getDeviceName();
+#else
+        return std::string();
+#endif  // ELPP_OS_UNIX && !ELPP_OS_ANDROID
+    }
+    /// @brief Whether or not terminal supports colors
+    static inline bool termSupportsColor(void) {
+        std::string term = getEnvironmentVariable("TERM", "");
+        return term == "xterm" || term == "xterm-color" || term == "xterm-256color" ||
+                              term == "screen" || term == "linux" || term == "cygwin";
+    }
+};
+extern std::string s_currentUser;
+extern std::string s_currentHost;
+extern bool s_termSupportsColor;
+#define ELPP_INITI_BASIC_DECLR \
+    namespace el {\
+        namespace base {\
+            namespace utils {\
+                std::string s_currentUser = el::base::utils::OS::currentUser(); \
+                std::string s_currentHost = el::base::utils::OS::currentHost(); \
+                bool s_termSupportsColor = el::base::utils::OS::termSupportsColor(); \
+            }\
+        }\
+   }
+/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str
+class DateTime : base::StaticClass {
+public:
+    /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current millisecond.
+    ///
+    /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided
+    /// @param [in,out] tv Pointer that gets updated
+    static void gettimeofday(struct timeval* tv) {
+#if ELPP_OS_WINDOWS
+        if (tv != nullptr) {
+#   if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
+            const unsigned __int64 delta_ = 11644473600000000Ui64;
+#   else
+            const unsigned __int64 delta_ = 11644473600000000ULL;
+#   endif  // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS)
+            const double secOffSet = 0.000001;
+            const unsigned long usecOffSet = 1000000;
+            FILETIME fileTime;
+            GetSystemTimeAsFileTime(&fileTime);
+            unsigned __int64 present = 0;
+            present |= fileTime.dwHighDateTime;
+            present = present << 32;
+            present |= fileTime.dwLowDateTime;
+            present /= 10;  // mic-sec
+           // Subtract the difference
+            present -= delta_;
+            tv->tv_sec = static_cast<long>(present * secOffSet);
+            tv->tv_usec = static_cast<long>(present % usecOffSet);
+        }
+#else
+        ::gettimeofday(tv, nullptr);
+#endif  // ELPP_OS_WINDOWS
+    }
+
+    /// @brief Gets current date and time with milliseconds.
+    /// @param format User provided date/time format
+    /// @param msWidth A pointer to base::MillisecondsWidth from configuration (non-null)
+    /// @returns string based date time in specified format.
+    static inline std::string getDateTime(const char* format, const base::MillisecondsWidth* msWidth) {
+        struct timeval currTime;
+        gettimeofday(&currTime);
+        struct ::tm timeInfo;
+        buildTimeInfo(&currTime, &timeInfo);
+        const int kBuffSize = 30;
+        char buff_[kBuffSize] = "";
+        parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast<std::size_t>(currTime.tv_usec / msWidth->m_offset), msWidth);
+        return std::string(buff_);
+    }
+
+    /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc
+    static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit) {
+        double result = static_cast<double>(time);
+        base::type::EnumType start = static_cast<base::type::EnumType>(timestampUnit);
+        const base::type::char_t* unit = base::consts::kTimeFormats[start].unit;
+        for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) {
+            if (result <= base::consts::kTimeFormats[i].value) {
+                break;
+            }
+            result /= base::consts::kTimeFormats[i].value;
+            unit = base::consts::kTimeFormats[i + 1].unit;
+        }
+        base::type::stringstream_t ss;
+        ss << result << " " << unit;
+        return ss.str();
+    }
+
+    /// @brief Gets time difference in milli/micro second depending on timestampUnit
+    static inline unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, base::TimestampUnit timestampUnit) {
+        if (timestampUnit == base::TimestampUnit::Microsecond) {
+            return static_cast<unsigned long long>(static_cast<unsigned long long>(1000000 * endTime.tv_sec + endTime.tv_usec) -
+                    static_cast<unsigned long long>(1000000 * startTime.tv_sec + startTime.tv_usec));
+        } else {
+            return static_cast<unsigned long long>((((endTime.tv_sec - startTime.tv_sec) * 1000000) + (endTime.tv_usec - startTime.tv_usec)) / 1000);
+        }
+    }
+
+private:
+    static inline struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) {
+#if ELPP_OS_UNIX
+        time_t rawTime = currTime->tv_sec;
+        ::localtime_r(&rawTime, timeInfo);
+        return timeInfo;
+#else
+#   if ELPP_COMPILER_MSVC
+        ELPP_UNUSED(currTime);
+        time_t t;
+        _time64(&t);
+        localtime_s(timeInfo, &t);
+        return timeInfo;
+#   else
+        // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method
+        time_t rawTime = currTime->tv_sec;
+        struct tm* tmInf = localtime(&rawTime);
+        *timeInfo = *tmInf;
+        return timeInfo;
+#   endif  // ELPP_COMPILER_MSVC
+#endif  // ELPP_OS_UNIX
+    }
+    static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo,
+            std::size_t msec, const base::MillisecondsWidth* msWidth) {
+        const char* bufLim = buf + bufSz;
+        for (; *format; ++format) {
+            if (*format == base::consts::kFormatSpecifierChar) {
+                switch (*++format) {
+                case base::consts::kFormatSpecifierChar:  // Escape
+                    break;
+                case '\0':  // End
+                    --format;
+                    break;
+                case 'd':  // Day
+                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim);
+                    continue;
+                case 'a':  // Day of week (short)
+                    buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim);
+                    continue;
+                case 'A':  // Day of week (long)
+                    buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim);
+                    continue;
+                case 'M':  // month
+                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim);
+                    continue;
+                case 'b':  // month (short)
+                    buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim);
+                    continue;
+                case 'B':  // month (long)
+                    buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim);
+                    continue;
+                case 'y':  // year (two digits)
+                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim);
+                    continue;
+                case 'Y':  // year (four digits)
+                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim);
+                    continue;
+                case 'h':  // hour (12-hour)
+                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim);
+                    continue;
+                case 'H':  // hour (24-hour)
+                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim);
+                    continue;
+                case 'm':  // minute
+                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim);
+                    continue;
+                case 's':  // second
+                    buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim);
+                    continue;
+                case 'z':  // milliseconds
+                case 'g':
+                    buf = base::utils::Str::convertAndAddToBuff(msec, msWidth->m_width, buf, bufLim);
+                    continue;
+                case 'F':  // AM/PM
+                    buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim);
+                    continue;
+                default:
+                    continue;
+                }
+            }
+            if (buf == bufLim) break;
+            *buf++ = *format;
+        }
+        return buf;
+    }
+};
+/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..)
+class CommandLineArgs {
+public:
+    CommandLineArgs(void) {
+        setArgs(0, static_cast<char**>(nullptr));
+    }
+    CommandLineArgs(int argc, const char** argv) {
+        setArgs(argc, argv);
+    }
+    CommandLineArgs(int argc, char** argv) {
+        setArgs(argc, argv);
+    }
+    virtual ~CommandLineArgs(void) {}
+    /// @brief Sets arguments and parses them
+    inline void setArgs(int argc, const char** argv) {
+        setArgs(argc, const_cast<char**>(argv));
+    }
+    /// @brief Sets arguments and parses them
+    inline void setArgs(int argc, char** argv) {
+        m_params.clear();
+        m_paramsWithValue.clear();
+        if (argc == 0 || argv == nullptr) {
+            return;
+        }
+        m_argc = argc;
+        m_argv = argv;
+        for (int i = 1; i < m_argc; ++i) {
+            const char* v = (strstr(m_argv[i], "="));
+            if (v != nullptr && strlen(v) > 0) {
+                std::string key = std::string(m_argv[i]);
+                key = key.substr(0, key.find_first_of('='));
+                if (hasParamWithValue(key.c_str())) {
+                    ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" 
+                        << getParamValue(key.c_str()) << "]");
+                } else {
+                    m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1)));
+                }
+            }
+            if (v == nullptr) {
+                if (hasParam(m_argv[i])) {
+                    ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists");
+                } else {
+                    m_params.push_back(std::string(m_argv[i]));
+                }
+            }
+        }
+    }
+    /// @brief Returns true if arguments contain paramKey with a value (seperated by '=')
+    inline bool hasParamWithValue(const char* paramKey) const {
+        return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end();
+    }
+    /// @brief Returns value of arguments
+    /// @see hasParamWithValue(const char*)
+    inline const char* getParamValue(const char* paramKey) const {
+        return m_paramsWithValue.find(std::string(paramKey))->second.c_str();
+    }
+    /// @brief Return true if arguments has a param (not having a value) i,e without '='
+    inline bool hasParam(const char* paramKey) const {
+        return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end();
+    }
+    /// @brief Returns true if no params available. This exclude argv[0]
+    inline bool empty(void) const {
+        return m_params.empty() && m_paramsWithValue.empty();
+    }
+    /// @brief Returns total number of arguments. This exclude argv[0]
+    inline std::size_t size(void) const {
+        return m_params.size() + m_paramsWithValue.size();
+    }
+    inline friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) {
+        for (int i = 1; i < c.m_argc; ++i) {
+            os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]");
+            if (i < c.m_argc - 1) {
+                os << ELPP_LITERAL(" ");
+            }
+        }
+        return os;
+    }
+
+private:
+    int m_argc;
+    char** m_argv;
+    std::map<std::string, std::string> m_paramsWithValue;
+    std::vector<std::string> m_params;
+};
+/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type.
+///
+/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement
+/// unregisterAll() and deepCopy(const AbstractRegistry<T_Ptr, Container>&) and write registerNew() method according to container
+/// and few more methods; get() to find element, unregister() to unregister single entry.
+/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation.
+template <typename T_Ptr, typename Container>
+class AbstractRegistry : public base::threading::ThreadSafe {
+public:
+    typedef typename Container::iterator iterator;
+    typedef typename Container::const_iterator const_iterator;
+
+    /// @brief Default constructor
+    AbstractRegistry(void) {}
+
+    /// @brief Move constructor that is useful for base classes
+    AbstractRegistry(AbstractRegistry&& sr) {
+        if (this == &sr) {
+            return;
+        }
+        unregisterAll();
+        m_list = std::move(sr.m_list);
+    }
+
+    bool operator==(const AbstractRegistry<T_Ptr, Container>& other) {
+        if (size() != other.size()) {
+            return false;
+        }
+        for (std::size_t i = 0; i < m_list.size(); ++i) {
+            if (m_list.at(i) != other.m_list.at(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    bool operator!=(const AbstractRegistry<T_Ptr, Container>& other) {
+        if (size() != other.size()) {
+            return true;
+        }
+        for (std::size_t i = 0; i < m_list.size(); ++i) {
+            if (m_list.at(i) != other.m_list.at(i)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /// @brief Assignment move operator
+    AbstractRegistry& operator=(AbstractRegistry&& sr) {
+        if (this == &sr) {
+            return *this;
+        }
+        unregisterAll();
+        m_list = std::move(sr.m_list);
+        return *this;
+    }
+
+    virtual ~AbstractRegistry(void) {
+    }
+
+    /// @return Iterator pointer from start of repository
+    virtual inline iterator begin(void) ELPP_FINAL {
+        return m_list.begin();
+    }
+
+    /// @return Iterator pointer from end of repository
+    virtual inline iterator end(void) ELPP_FINAL {
+        return m_list.end();
+    }
+
+
+    /// @return Constant iterator pointer from start of repository
+    virtual inline const_iterator cbegin(void) const ELPP_FINAL {
+        return m_list.cbegin();
+    }
+
+    /// @return End of repository
+    virtual inline const_iterator cend(void) const ELPP_FINAL {
+        return m_list.cend();
+    }
+
+    /// @return Whether or not repository is empty
+    virtual inline bool empty(void) const ELPP_FINAL {
+        return m_list.empty();
+    }
+
+    /// @return Size of repository
+    virtual inline std::size_t size(void) const ELPP_FINAL {
+        return m_list.size();
+    }
+
+    /// @brief Returns underlying container by reference
+    virtual inline Container& list(void) ELPP_FINAL {
+        return m_list;
+    }
+
+    /// @brief Returns underlying container by constant reference.
+    virtual inline const Container& list(void) const ELPP_FINAL {
+        return m_list;
+    }
+
+    /// @brief Unregisters all the pointers from current repository.
+    virtual void unregisterAll(void) = 0;
+
+protected:
+    virtual void deepCopy(const AbstractRegistry<T_Ptr, Container>&) = 0;
+    void reinitDeepCopy(const AbstractRegistry<T_Ptr, Container>& sr) {
+        unregisterAll();
+        deepCopy(sr);
+    }
+
+private:
+    Container m_list;
+};
+
+/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version)
+///
+/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions)
+///         of AbstractRegistry<T_Ptr, Container>. Any implementation of this class should be  
+///         explicitly (by using lock functions)
+template <typename T_Ptr, typename T_Key = const char*>
+class Registry : public AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>> {
+public:
+    typedef typename Registry<T_Ptr, T_Key>::iterator iterator;
+    typedef typename Registry<T_Ptr, T_Key>::const_iterator const_iterator;
+
+    Registry(void) {}
+
+    /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor.
+    Registry(const Registry& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() {
+        if (this == &sr) {
+            return;
+        }
+        this->reinitDeepCopy(sr);
+    }
+
+    /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element
+    /// @see unregisterAll()
+    /// @see deepCopy(const AbstractRegistry&)
+    Registry& operator=(const Registry& sr) {
+        if (this == &sr) {
+            return *this;
+        }
+        this->reinitDeepCopy(sr);
+        return *this;
+    }
+
+    virtual ~Registry(void) {
+        unregisterAll();
+    }
+
+protected:
+    virtual inline void unregisterAll(void) ELPP_FINAL {
+        if (!this->empty()) {
+            for (auto&& curr : this->list()) {
+                base::utils::safeDelete(curr.second);
+            }
+            this->list().clear();
+        }
+    }
+
+    /// @brief Registers new registry to repository.
+    virtual inline void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL {
+        unregister(uniqKey);
+        this->list().insert(std::make_pair(uniqKey, ptr));
+    }
+
+    /// @brief Unregisters single entry mapped to specified unique key
+    inline void unregister(const T_Key& uniqKey) {
+        T_Ptr* existing = get(uniqKey);
+        if (existing != nullptr) {
+            base::utils::safeDelete(existing);
+            this->list().erase(uniqKey);
+        }
+    }
+
+    /// @brief Gets pointer from repository. If none found, nullptr is returned.
+    inline T_Ptr* get(const T_Key& uniqKey) {
+        iterator it = this->list().find(uniqKey);
+        return it == this->list().end()
+                ? nullptr
+                : it->second;
+    }
+
+private:
+    virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::map<T_Key, T_Ptr*>>& sr) ELPP_FINAL {
+        for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) {
+            registerNew(it->first, new T_Ptr(*it->second));
+        }
+    }
+};
+
+/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version)
+///
+/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry<T_Ptr, Container>. Any implementation of this class
+/// should be made thread-safe explicitly
+template <typename T_Ptr, typename Pred>
+class RegistryWithPred : public AbstractRegistry<T_Ptr, std::vector<T_Ptr*>> {
+public:
+    typedef typename RegistryWithPred<T_Ptr, Pred>::iterator iterator;
+    typedef typename RegistryWithPred<T_Ptr, Pred>::const_iterator const_iterator;
+
+    RegistryWithPred(void) {
+    }
+
+    virtual ~RegistryWithPred(void) {
+        unregisterAll();
+    }
+
+    /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor.
+    RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>() {
+        if (this == &sr) {
+            return;
+        }
+        this->reinitDeepCopy(sr);
+    }
+
+    /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element
+    /// @see unregisterAll()
+    /// @see deepCopy(const AbstractRegistry&)
+    RegistryWithPred& operator=(const RegistryWithPred& sr) {
+        if (this == &sr) {
+            return *this;
+        }
+        this->reinitDeepCopy(sr);
+        return *this;
+    }
+
+    friend inline base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) {
+        for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) {
+            os << ELPP_LITERAL("    ") << **it << ELPP_LITERAL("\n");
+        }
+        return os;
+    }
+
+protected:
+    virtual inline void unregisterAll(void) ELPP_FINAL {
+        if (!this->empty()) {
+            for (auto&& curr : this->list()) {
+                base::utils::safeDelete(curr);
+            }
+            this->list().clear();
+        }
+    }
+
+    virtual void unregister(T_Ptr*& ptr) ELPP_FINAL {
+        if (ptr) {
+            iterator iter = this->begin();
+            for (; iter != this->end(); ++iter) {
+                if (ptr == *iter) {
+                    break;
+                }
+            }
+            if (iter != this->end() && *iter != nullptr) {
+                this->list().erase(iter);
+                base::utils::safeDelete(*iter);
+            }
+        }
+    }
+
+    virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL {
+        this->list().push_back(ptr);
+    }
+
+    /// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate
+    /// in order to validate pointer.
+    template <typename T, typename T2>
+    inline T_Ptr* get(const T& arg1, const T2 arg2) {
+        iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2));
+        if (iter != this->list().end() && *iter != nullptr) {
+            return *iter;
+        }
+        return nullptr;
+    }
+
+private:
+    virtual inline void deepCopy(const AbstractRegistry<T_Ptr, std::vector<T_Ptr*>>& sr) {
+        for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) {
+            registerNew(new T_Ptr(**it));
+        }
+    }
+};
+
+}  // namespace utils
+} // namespace base
+/// @brief Base of Easylogging++ friendly class
+///
+/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const`
+class Loggable {
+public:
+    virtual ~Loggable(void) {}
+    virtual void log(el::base::type::ostream_t&) const = 0;
+private:
+    friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) {
+        loggable.log(os);
+        return os;
+    }
+};
+namespace base {
+/// @brief Represents log format containing flags and date format. This is used internally to start initial log
+class LogFormat : public Loggable {
+public:
+    LogFormat(void) :
+        m_level(Level::Unknown),
+        m_userFormat(base::type::string_t()),
+        m_format(base::type::string_t()),
+        m_dateTimeFormat(std::string()),
+        m_flags(0x0) {
+    }
+
+    LogFormat(Level level, const base::type::string_t& format)
+            : m_level(level), m_userFormat(format) {
+        parseFromFormat(m_userFormat);
+    }
+
+    LogFormat(const LogFormat& logFormat) {
+        m_level = logFormat.m_level;
+        m_userFormat = logFormat.m_userFormat;
+        m_format = logFormat.m_format;
+        m_dateTimeFormat = logFormat.m_dateTimeFormat;
+        m_flags = logFormat.m_flags;
+    }
+
+    LogFormat(LogFormat&& logFormat) {
+        m_level = std::move(logFormat.m_level);
+        m_userFormat = std::move(logFormat.m_userFormat);
+        m_format = std::move(logFormat.m_format);
+        m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat);
+        m_flags = std::move(logFormat.m_flags);
+    }
+
+    LogFormat& operator=(const LogFormat& logFormat) {
+        m_level = logFormat.m_level;
+        m_userFormat = logFormat.m_userFormat;
+        m_dateTimeFormat = logFormat.m_dateTimeFormat;
+        m_flags = logFormat.m_flags;
+        return *this;
+    }
+
+    virtual ~LogFormat(void) {
+    }
+
+    inline bool operator==(const LogFormat& other) {
+        return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format &&
+                m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags;
+    }
+
+    /// @brief Updates format to be used while logging.
+    /// @param userFormat User provided format
+    void parseFromFormat(const base::type::string_t& userFormat) {
+        // We make copy because we will be changing the format
+        // i.e, removing user provided date format from original format
+        // and then storing it.
+        base::type::string_t formatCopy = userFormat;
+        m_flags = 0x0;
+        auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) {
+            std::size_t foundAt = base::type::string_t::npos;
+            while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos){
+                if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) {
+                    if (hasFlag(flag)) {
+                        // If we already have flag we remove the escape chars so that '%%' is turned to '%'
+                        // even after specifier resolution - this is because we only replaceFirst specifier
+                        formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1);
+                        ++foundAt;
+                    }
+                } else {
+                    if (!hasFlag(flag)) addFlag(flag);
+                }
+            }
+        };
+        conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName);
+        conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level);
+        conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort);
+        conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId);
+        conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId);
+        conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File);
+        conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase);
+        conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line);
+        conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location);
+        conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function);
+        conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User);
+        conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host);
+        conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage);
+        conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel);
+        // For date/time we need to extract user's date format first
+        std::size_t dateIndex = std::string::npos;
+        if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) {
+            while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
+                dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1);
+            }
+            if (dateIndex != std::string::npos) {
+                addFlag(base::FormatFlags::DateTime);
+                updateDateFormat(dateIndex, formatCopy);
+            }
+        }
+        m_format = formatCopy;
+        updateFormatSpec();
+    }
+
+    inline Level level(void) const {
+        return m_level;
+    }
+
+    inline const base::type::string_t& userFormat(void) const {
+        return m_userFormat;
+    }
+
+    inline const base::type::string_t& format(void) const {
+       return m_format;
+    }
+
+    inline const std::string& dateTimeFormat(void) const {
+       return m_dateTimeFormat;
+    }
+
+    inline base::type::EnumType flags(void) const {
+       return m_flags;
+    }
+
+    inline bool hasFlag(base::FormatFlags flag) const {
+        return base::utils::hasFlag(flag, m_flags);
+    }
+
+    virtual void log(el::base::type::ostream_t& os) const {
+        os << m_format;
+    }
+
+protected:
+    /// @brief Updates date time format if available in currFormat.
+    /// @param index Index where %datetime, %date or %time was found
+    /// @param [in,out] currFormat current format that is being used to format
+    virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL {
+        if (hasFlag(base::FormatFlags::DateTime)) {
+            index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier);
+        }
+        const base::type::char_t* ptr = currFormat.c_str() + index;
+        if ((currFormat.size() > index) && (ptr[0] == '{')) {
+            // User has provided format for date/time
+            ++ptr;
+            int count = 1;  // Start by 1 in order to remove starting brace
+            std::stringstream ss;
+            for (; *ptr; ++ptr, ++count) {
+                if (*ptr == '}') {
+                    ++count;  // In order to remove ending brace
+                    break;
+                }
+                ss << *ptr;
+            }
+            currFormat.erase(index, count);
+            m_dateTimeFormat = ss.str();
+        } else {
+            // No format provided, use default
+            if (hasFlag(base::FormatFlags::DateTime)) {
+                m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat);
+            }
+        }
+    }
+
+    /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level
+    virtual void updateFormatSpec(void) ELPP_FINAL {
+        // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet.
+        if (m_level == Level::Debug) {
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+                    base::consts::kDebugLevelLogValue);
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+                    base::consts::kDebugLevelShortLogValue);
+        } else if (m_level == Level::Info) {
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+                    base::consts::kInfoLevelLogValue);
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+                    base::consts::kInfoLevelShortLogValue);
+        } else if (m_level == Level::Warning) {
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+                    base::consts::kWarningLevelLogValue);
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+                    base::consts::kWarningLevelShortLogValue);
+        } else if (m_level == Level::Error) {
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+                    base::consts::kErrorLevelLogValue);
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+                    base::consts::kErrorLevelShortLogValue);
+        } else if (m_level == Level::Fatal) {
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+                    base::consts::kFatalLevelLogValue);
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+                    base::consts::kFatalLevelShortLogValue);
+        } else if (m_level == Level::Verbose) {
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+                    base::consts::kVerboseLevelLogValue);
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+                    base::consts::kVerboseLevelShortLogValue);
+        } else if (m_level == Level::Trace) {
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier,
+                    base::consts::kTraceLevelLogValue);
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier,
+                    base::consts::kTraceLevelShortLogValue);
+        }
+        if (hasFlag(base::FormatFlags::User)) {
+            std::string s = base::utils::s_currentUser;
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier,
+                    base::utils::s_currentUser);
+        }
+        if (hasFlag(base::FormatFlags::Host)) {
+            base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier,
+                    base::utils::s_currentHost);
+        }
+        // Ignore Level::Global and Level::Unknown
+    }
+
+    inline void addFlag(base::FormatFlags flag) {
+        base::utils::addFlag(flag, &m_flags);
+    }
+
+private:
+    Level m_level;
+    base::type::string_t m_userFormat;
+    base::type::string_t m_format;
+    std::string m_dateTimeFormat;
+    base::type::EnumType m_flags;
+    friend class el::Logger;  // To resolve loggerId format specifier easily
+};
+}  // namespace base
+/// @brief Resolving function for format specifier
+typedef std::function<const char*(void)> FormatSpecifierValueResolver;
+/// @brief User-provided custom format specifier
+/// @see el::Helpers::installCustomFormatSpecifier
+/// @see FormatSpecifierValueResolver
+class CustomFormatSpecifier {
+public:
+    CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) :
+        m_formatSpecifier(formatSpecifier), m_resolver(resolver) {}
+    inline const char* formatSpecifier(void) const { return m_formatSpecifier; }
+    inline const FormatSpecifierValueResolver& resolver(void) const { return m_resolver; }
+    inline bool operator==(const char* formatSpecifier) {
+        return strcmp(m_formatSpecifier, formatSpecifier) == 0;
+    }
+
+private:
+    const char* m_formatSpecifier;
+    FormatSpecifierValueResolver m_resolver;
+};
+/// @brief Represents single configuration that has representing level, configuration type and a string based value.
+///
+/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes
+/// and will be parsed later.
+///
+/// Consider some examples below:
+///   * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true");
+///   * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048");
+///   * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log");
+class Configuration : public Loggable {
+public:
+    Configuration(const Configuration& c) :
+            m_level(c.m_level),
+            m_configurationType(c.m_configurationType),
+            m_value(c.m_value) {
+    }
+
+    Configuration& operator=(const Configuration& c) {
+        m_level = c.m_level;
+        m_configurationType = c.m_configurationType;
+        m_value = c.m_value;
+        return *this;
+    }
+
+    virtual ~Configuration(void) {
+    }
+
+    /// @brief Full constructor used to sets value of configuration
+    Configuration(Level level, ConfigurationType configurationType, const std::string& value) :
+        m_level(level),
+        m_configurationType(configurationType),
+        m_value(value) {
+    }
+
+    /// @brief Gets level of current configuration
+    inline Level level(void) const {
+        return m_level;
+    }
+
+    /// @brief Gets configuration type of current configuration
+    inline ConfigurationType configurationType(void) const {
+        return m_configurationType;
+    }
+
+    /// @brief Gets string based configuration value
+    inline const std::string& value(void) const {
+        return m_value;
+    }
+
+    /// @brief Set string based configuration value
+    /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values
+    ///        use them in quotes. They will be parsed when configuring
+    inline void setValue(const std::string& value) {
+        m_value = value;
+    }
+
+    virtual inline void log(el::base::type::ostream_t& os) const {
+        os << LevelHelper::convertToString(m_level)
+            << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType)
+            << ELPP_LITERAL(" = ") << m_value.c_str();
+    }
+
+    /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it.
+    class Predicate {
+    public:
+        Predicate(Level level, ConfigurationType configurationType) :
+            m_level(level),
+            m_configurationType(configurationType) {
+        }
+
+        inline bool operator()(const Configuration* conf) const {
+            return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType));
+        }
+
+    private:
+        Level m_level;
+        ConfigurationType m_configurationType;
+    };
+
+private:
+    Level m_level;
+    ConfigurationType m_configurationType;
+    std::string m_value;
+};
+
+/// @brief Thread-safe Configuration repository
+///
+/// @detail This repository represents configurations for all the levels and configuration type mapped to a value.
+class Configurations : public base::utils::RegistryWithPred<Configuration, Configuration::Predicate> {
+public:
+    /// @brief Default constructor with empty repository
+    Configurations(void) :
+            m_configurationFile(std::string()),
+            m_isFromFile(false) {
+    }
+
+    /// @brief Constructor used to set configurations using configuration file.
+    /// @param configurationFile Full path to configuration file
+    /// @param useDefaultsForRemaining Lets you set the remaining configurations to default.
+    /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to.
+    /// @see parseFromFile(const std::string&, Configurations* base)
+    /// @see setRemainingToDefault()
+    Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, Configurations* base = nullptr) :
+            m_configurationFile(configurationFile),
+            m_isFromFile(false) {
+        parseFromFile(configurationFile, base);
+        if (useDefaultsForRemaining) {
+            setRemainingToDefault();
+        }
+    }
+
+    virtual ~Configurations(void) {
+    }
+
+    /// @brief Parses configuration from file.
+    /// @param configurationFile Full path to configuration file
+    /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+    ///        existing Configurations to base all the values and then set rest of configuration via configuration file.
+    /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you
+    ///         do not proceed without successful parse.
+    inline bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr) {
+        // We initial assertion with true because if we have assertion diabled, we want to pass this
+        // check and if assertion is enabled we will have values re-assigned any way.
+        bool assertionPassed = true;
+        ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)),
+                "Configuration file [" << configurationFile << "] does not exist!");
+        if (!assertionPassed) {
+            return false;
+        }
+        bool success = Parser::parseFromFile(configurationFile, this, base);
+        m_isFromFile = success;
+        return success;
+    }
+
+    /// @brief Parse configurations from configuration string.
+    ///
+    /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary
+    /// new line characters are provided.
+    /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+    ///        existing Configurations to base all the values and then set rest of configuration via configuration text.
+    /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you
+    ///         do not proceed without successful parse.
+    inline bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr) {
+        bool success = Parser::parseFromText(configurationsString, this, base);
+        if (success) {
+            m_isFromFile = false;
+        }
+        return success;
+    }
+
+    /// @brief Sets configuration based-off an existing configurations.
+    /// @param base Pointer to existing configurations.
+    inline void setFromBase(Configurations* base) {
+        if (base == nullptr || base == this) {
+            return;
+        }
+        base::threading::ScopedLock scopedLock(base->lock());
+        for (Configuration*& conf : base->list()) {
+            set(conf);
+        }
+    }
+
+    /// @brief Determines whether or not specified configuration type exists in the repository.
+    ///
+    /// @detail Returns as soon as first level is found.
+    /// @param configurationType Type of configuration to check existence for.
+    bool hasConfiguration(ConfigurationType configurationType) {
+        base::type::EnumType lIndex = LevelHelper::kMinValid;
+        bool result = false;
+        LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+            if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) {
+                result = true;
+            }
+            return result;
+        });
+        return result;
+    }
+
+    /// @brief Determines whether or not specified configuration type exists for specified level
+    /// @param level Level to check
+    /// @param configurationType Type of configuration to check existence for.
+    inline bool hasConfiguration(Level level, ConfigurationType configurationType) {
+        base::threading::ScopedLock scopedLock(lock());
+#if ELPP_COMPILER_INTEL
+        // We cant specify template types here, Intel C++ throws compilation error
+        // "error: type name is not allowed"
+        return RegistryWithPred::get(level, configurationType) != nullptr;
+#else
+        return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType) != nullptr;
+#endif  // ELPP_COMPILER_INTEL
+    }
+
+    /// @brief Sets value of configuration for specified level.
+    ///
+    /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types
+    /// ConfigurationType::MillisecondsWidth and ConfigurationType::PerformanceTracking will be ignored if not set for
+    /// Level::Global because these configurations are not dependant on level.
+    /// @param level Level to set configuration for (el::Level).
+    /// @param configurationType Type of configuration (el::ConfigurationType)
+    /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string
+    /// from users' point of view. This is then parsed later to be used internally.
+    /// @see Configuration::setValue(const std::string& value)
+    /// @see el::Level
+    /// @see el::ConfigurationType
+    inline void set(Level level, ConfigurationType configurationType, const std::string& value) {
+        base::threading::ScopedLock scopedLock(lock());
+        unsafeSet(level, configurationType, value);  // This is not unsafe anymore as we have locked mutex
+        if (level == Level::Global) {
+            unsafeSetGlobally(configurationType, value, false);  // Again this is not unsafe either
+        }
+    }
+
+    /// @brief Sets single configuration based on other single configuration.
+    /// @see set(Level level, ConfigurationType configurationType, const std::string& value)
+    inline void set(Configuration* conf) {
+        if (conf == nullptr) {
+            return;
+        }
+        set(conf->level(), conf->configurationType(), conf->value());
+    }
+
+    inline Configuration* get(Level level, ConfigurationType configurationType) {
+        base::threading::ScopedLock scopedLock(lock());
+        return RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
+    }
+
+    /// @brief Sets configuration for all levels.
+    /// @param configurationType Type of configuration
+    /// @param value String based value
+    /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value)
+    inline void setGlobally(ConfigurationType configurationType, const std::string& value) {
+        setGlobally(configurationType, value, false);
+    }
+
+    /// @brief Clears repository so that all the configurations are unset
+    inline void clear(void) {
+        base::threading::ScopedLock scopedLock(lock());
+        unregisterAll();
+    }
+
+    /// @brief Gets configuration file used in parsing this configurations.
+    ///
+    /// @detail If this repository was set manually or by text this returns empty string.
+    inline const std::string& configurationFile(void) const {
+        return m_configurationFile;
+    }
+
+    /// @brief Sets configurations to "factory based" configurations.
+    void setToDefault(void) {
+        setGlobally(ConfigurationType::Enabled, std::string("true"), true);
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+        setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true);
+#else
+        ELPP_UNUSED(base::consts::kDefaultLogFile);
+#endif  // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+        setGlobally(ConfigurationType::ToFile, std::string("true"), true);
+        setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true);
+        setGlobally(ConfigurationType::MillisecondsWidth, std::string("3"), true);
+        setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true);
+        setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true);
+        setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true);
+
+        setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true);
+        set(Level::Debug, ConfigurationType::Format, std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
+        // INFO and WARNING are set to default by Level::Global
+        set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+        set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+        set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
+        set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
+    }
+
+    /// @brief Lets you set the remaining configurations to default.
+    ///
+    /// @detail By remaining, it means that the level/type a configuration does not exist for.
+    /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets
+    /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e,
+    /// true. If you dont do this explicitley (either by calling this function or by using second param in Constructor
+    /// and try to access a value, an error is thrown
+    void setRemainingToDefault(void) {
+        base::threading::ScopedLock scopedLock(lock());
+        unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true"));
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+        unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile));
+#endif  // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+        unsafeSetIfNotExist(Level::Global, ConfigurationType::ToFile, std::string("true"));
+        unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true"));
+        unsafeSetIfNotExist(Level::Global, ConfigurationType::MillisecondsWidth, std::string("3"));
+        unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true"));
+        unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0"));
+        unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+        unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, 
+            std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg"));
+        // INFO and WARNING are set to default by Level::Global
+        unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+        unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"));
+        unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg"));
+        unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg"));
+    }
+
+    /// @brief Parser used internally to parse configurations from file or text.
+    ///
+    /// @detail This class makes use of base::utils::Str.
+    /// You should not need this unless you are working on some tool for Easylogging++
+    class Parser : base::StaticClass {
+    public:
+        /// @brief Parses configuration from file.
+        /// @param configurationFile Full path to configuration file
+        /// @param sender Sender configurations pointer. Usually 'this' is used from calling class
+        /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+        ///        existing Configurations to base all the values and then set rest of configuration via configuration file.
+        /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you
+        ///         do not proceed without successful parse.
+        static bool parseFromFile(const std::string& configurationFile, Configurations* sender, Configurations* base = nullptr) {
+            sender->setFromBase(base);
+            std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in);
+            ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing.");
+            bool parsedSuccessfully = false;
+            std::string line = std::string();
+            Level currLevel = Level::Unknown;
+            std::string currConfigStr = std::string();
+            std::string currLevelStr = std::string();
+            while (fileStream_.good()) {
+                std::getline(fileStream_, line);
+                parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
+                ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
+            }
+            return parsedSuccessfully;
+        }
+
+        /// @brief Parse configurations from configuration string.
+        ///
+        /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary
+        /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you
+        /// do not proceed without successful parse (This is recommended)
+        /// @param configurationsString
+        /// @param sender Sender configurations pointer. Usually 'this' is used from calling class
+        /// @param base Configurations to base new configuration repository off. This value is used when you want to use
+        ///        existing Configurations to base all the values and then set rest of configuration via configuration text.
+        /// @return True if successfully parsed, false otherwise.
+        static bool parseFromText(const std::string& configurationsString, Configurations* sender, Configurations* base = nullptr) {
+            sender->setFromBase(base);
+            bool parsedSuccessfully = false;
+            std::stringstream ss(configurationsString);
+            std::string line = std::string();
+            Level currLevel = Level::Unknown;
+            std::string currConfigStr = std::string();
+            std::string currLevelStr = std::string();
+            while (std::getline(ss, line)) {
+                parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender);
+                ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line);
+            }
+            return parsedSuccessfully;
+        }
+
+    private:
+        friend class el::Loggers;
+        static void ignoreComments(std::string* line) {
+            std::size_t foundAt = 0;
+            std::size_t quotesStart = line->find("\"");
+            std::size_t quotesEnd = std::string::npos;
+            if (quotesStart != std::string::npos) {
+                quotesEnd = line->find("\"", quotesStart + 1);
+                while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') {
+                    // Do not erase slash yet - we will erase it in parseLine(..) while loop
+                    quotesEnd = line->find("\"", quotesEnd + 2);
+                }
+            }
+            if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) {
+                if (foundAt < quotesEnd) {
+                    foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1);
+                }
+                *line = line->substr(0, foundAt);
+            }
+        }
+        static inline bool isLevel(const std::string& line) {
+            return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel));
+        }
+
+        static inline bool isComment(const std::string& line) {
+            return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment));
+        }
+
+        static inline bool isConfig(const std::string& line) {
+            std::size_t assignment = line.find('=');
+            return line != "" &&
+                    (line[0] >= 65 || line[0] <= 90 || line[0] >= 97 || line[0] <= 122) &&
+                    (assignment != std::string::npos) &&
+                    (line.size() > assignment);
+        }
+
+        static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, Configurations* conf) {
+            ConfigurationType currConfig = ConfigurationType::Unknown;
+            std::string currValue = std::string();
+            *line = base::utils::Str::trim(*line);
+            if (isComment(*line)) return true;
+            ignoreComments(line);
+            *line = base::utils::Str::trim(*line);
+            if (line->empty()) {
+                // Comment ignored
+                return true;
+            }
+            if (isLevel(*line)) {
+                if (line->size() <= 2) {
+                    return true;
+                }
+                *currLevelStr = line->substr(1, line->size() - 2);
+                *currLevelStr = base::utils::Str::toUpper(*currLevelStr);
+                *currLevelStr = base::utils::Str::trim(*currLevelStr);
+                *currLevel = LevelHelper::convertFromString(currLevelStr->c_str());
+                return true;
+            }
+            if (isConfig(*line)) {
+                std::size_t assignment = line->find('=');
+                *currConfigStr = line->substr(0, assignment);
+                *currConfigStr = base::utils::Str::toUpper(*currConfigStr);
+                *currConfigStr = base::utils::Str::trim(*currConfigStr);
+                currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str());
+                currValue = line->substr(assignment + 1);
+                currValue = base::utils::Str::trim(currValue);
+                std::size_t quotesStart = currValue.find("\"", 0);
+                std::size_t quotesEnd = std::string::npos;
+                if (quotesStart != std::string::npos) {
+                    quotesEnd = currValue.find("\"", quotesStart + 1);
+                    while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') {
+                        currValue = currValue.erase(quotesEnd - 1, 1);
+                        quotesEnd = currValue.find("\"", quotesEnd + 2);
+                    }
+                }
+                if (quotesStart != std::string::npos && quotesEnd != std::string::npos) {
+                    // Quote provided - check and strip if valid
+                    ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" 
+                        << currConfigStr << "]");
+                    ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]");
+                    if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) {
+                        // Explicit check in case if assertion is disabled
+                        currValue = currValue.substr(quotesStart + 1, quotesEnd - 1);
+                    }
+                }
+            }
+            ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]");
+            ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]");
+            if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) {
+                return false;  // unrecognizable level or config
+            }
+            conf->set(*currLevel, currConfig, currValue);
+            return true;
+        }
+    };
+
+private:
+    std::string m_configurationFile;
+    bool m_isFromFile;
+    friend class el::Loggers;
+
+    /// @brief Unsafely sets configuration if does not already exist
+    void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) {
+        Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
+        if (conf == nullptr) {
+            unsafeSet(level, configurationType, value);
+        }
+    }
+
+    /// @brief Thread unsafe set
+    void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) {
+        Configuration* conf = RegistryWithPred<Configuration, Configuration::Predicate>::get(level, configurationType);
+        if (conf == nullptr) {
+            registerNew(new Configuration(level, configurationType, value));
+        } else {
+            conf->setValue(value);
+        }
+        if (level == Level::Global) {
+            unsafeSetGlobally(configurationType, value, false);
+        }
+    }
+
+    /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true
+    /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value)
+    void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) {
+        if (includeGlobalLevel) {
+            set(Level::Global, configurationType, value);
+        }
+        base::type::EnumType lIndex = LevelHelper::kMinValid;
+        LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+            set(LevelHelper::castFromInt(lIndex), configurationType, value);
+            return false;  // Do not break lambda function yet as we need to set all levels regardless
+        });
+    }
+
+    /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true
+    /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value)
+    void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) {
+        if (includeGlobalLevel) {
+            unsafeSet(Level::Global, configurationType, value);
+        }
+        base::type::EnumType lIndex = LevelHelper::kMinValid;
+        LevelHelper::forEachLevel(&lIndex, [&](void) -> bool  {
+            unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value);
+            return false;  // Do not break lambda function yet as we need to set all levels regardless
+        });
+    }
+};
+
+namespace base {
+typedef std::shared_ptr<base::type::fstream_t> FileStreamPtr;
+typedef std::map<std::string, FileStreamPtr> LogStreamsReferenceMap;
+/// @brief Configurations with data types.
+///
+/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations.
+/// This is to perform faster while writing logs using correct configurations.
+///
+/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class)
+class TypedConfigurations : public base::threading::ThreadSafe {
+public:
+    /// @brief Constructor to initialize (construct) the object off el::Configurations
+    /// @param configurations Configurations pointer/reference to base this typed configurations off.
+    /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference()
+    TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference) {
+        m_configurations = configurations;
+        m_logStreamsReference = logStreamsReference;
+        build(m_configurations);
+    }
+
+    TypedConfigurations(const TypedConfigurations& other) {
+        this->m_configurations = other.m_configurations;
+        this->m_logStreamsReference = other.m_logStreamsReference;
+        build(m_configurations);
+    }
+
+    virtual ~TypedConfigurations(void) {
+    }
+
+    const Configurations* configurations(void) const {
+        return m_configurations;
+    }
+
+    inline bool enabled(Level level) {
+        return getConfigByVal<bool>(level, &m_enabledMap, "enabled");
+    }
+
+    inline bool toFile(Level level) {
+        return getConfigByVal<bool>(level, &m_toFileMap, "toFile");
+    }
+
+    inline const std::string& filename(Level level) {
+        return getConfigByRef<std::string>(level, &m_filenameMap, "filename");
+    }
+
+    inline bool toStandardOutput(Level level) {
+        return getConfigByVal<bool>(level, &m_toStandardOutputMap, "toStandardOutput");
+    }
+
+    inline const base::LogFormat& logFormat(Level level) {
+        return getConfigByRef<base::LogFormat>(level, &m_logFormatMap, "logFormat");
+    }
+
+    inline const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global) {
+        return getConfigByRef<base::MillisecondsWidth>(level, &m_millisecondsWidthMap, "millisecondsWidth");
+    }
+
+    inline bool performanceTracking(Level level = Level::Global) {
+        return getConfigByVal<bool>(level, &m_performanceTrackingMap, "performanceTracking");
+    }
+
+    inline base::type::fstream_t* fileStream(Level level) {
+        return getConfigByRef<base::FileStreamPtr>(level, &m_fileStreamMap, "fileStream").get();
+    }
+
+    inline std::size_t maxLogFileSize(Level level) {
+        return getConfigByVal<std::size_t>(level, &m_maxLogFileSizeMap, "maxLogFileSize");
+    }
+
+    inline std::size_t logFlushThreshold(Level level) {
+        return getConfigByVal<std::size_t>(level, &m_logFlushThresholdMap, "logFlushThreshold");
+    }
+
+private:
+    Configurations* m_configurations;
+    std::map<Level, bool> m_enabledMap;
+    std::map<Level, bool> m_toFileMap;
+    std::map<Level, std::string> m_filenameMap;
+    std::map<Level, bool> m_toStandardOutputMap;
+    std::map<Level, base::LogFormat> m_logFormatMap;
+    std::map<Level, base::MillisecondsWidth> m_millisecondsWidthMap;
+    std::map<Level, bool> m_performanceTrackingMap;
+    std::map<Level, base::FileStreamPtr> m_fileStreamMap;
+    std::map<Level, std::size_t> m_maxLogFileSizeMap;
+    std::map<Level, std::size_t> m_logFlushThresholdMap;
+    base::LogStreamsReferenceMap* m_logStreamsReference;
+
+    friend class el::Helpers;
+    friend class el::base::MessageBuilder;
+    friend class el::base::Writer;
+    friend class el::base::DefaultLogDispatchCallback;
+    friend class el::base::LogDispatcher;
+
+    template <typename Conf_T>
+    inline Conf_T getConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) {
+        base::threading::ScopedLock scopedLock(lock());
+        return unsafeGetConfigByVal(level, confMap, confName);  // This is not unsafe anymore - mutex locked in scope
+    }
+
+    template <typename Conf_T>
+    inline Conf_T& getConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) {
+        base::threading::ScopedLock scopedLock(lock());
+        return unsafeGetConfigByRef(level, confMap, confName);  // This is not unsafe anymore - mutex locked in scope
+    }
+
+    template <typename Conf_T>
+    inline Conf_T unsafeGetConfigByVal(Level level, const std::map<Level, Conf_T>* confMap, const char* confName) {
+        ELPP_UNUSED(confName);
+        typename std::map<Level, Conf_T>::const_iterator it = confMap->find(level);
+        if (it == confMap->end()) {
+            try {
+                return confMap->at(Level::Global);
+            } catch (...) {
+                ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" 
+                    << LevelHelper::convertToString(level) << "]"
+                        << std::endl << "Please ensure you have properly configured logger.", false);
+                return Conf_T();
+            }
+        }
+        return it->second;
+    }
+
+    template <typename Conf_T>
+    inline Conf_T& unsafeGetConfigByRef(Level level, std::map<Level, Conf_T>* confMap, const char* confName) {
+        ELPP_UNUSED(confName);
+        typename std::map<Level, Conf_T>::iterator it = confMap->find(level);
+        if (it == confMap->end()) {
+            try {
+                return confMap->at(Level::Global);
+            } catch (...) {
+                ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" 
+                    << LevelHelper::convertToString(level) << "]"
+                        << std::endl << "Please ensure you have properly configured logger.", false);
+            }
+        }
+        return it->second;
+    }
+
+    template <typename Conf_T>
+    void setValue(Level level, const Conf_T& value, std::map<Level, Conf_T>* confMap, bool includeGlobalLevel = true) {
+        // If map is empty and we are allowed to add into generic level (Level::Global), do it!
+        if (confMap->empty() && includeGlobalLevel) {
+            confMap->insert(std::make_pair(Level::Global, value));
+            return;
+        }
+        // If same value exist in generic level already, dont add it to explicit level
+        typename std::map<Level, Conf_T>::iterator it = confMap->find(Level::Global);
+        if (it != confMap->end() && it->second == value) {
+            return;
+        }
+        // Now make sure we dont double up values if we really need to add it to explicit level
+        it = confMap->find(level);
+        if (it == confMap->end()) {
+            // Value not found for level, add new
+            confMap->insert(std::make_pair(level, value));
+        } else {
+            // Value found, just update value
+            confMap->at(level) = value;
+        }
+    }
+
+    void build(Configurations* configurations) {
+        base::threading::ScopedLock scopedLock(lock());
+        auto getBool = [] (std::string boolStr) -> bool {  // Pass by value for trimming
+            base::utils::Str::trim(boolStr);
+            return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1");
+        };
+        std::vector<Configuration*> withFileSizeLimit;
+        for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
+            Configuration* conf = *it;
+            // We cannot use switch on strong enums because Intel C++ dont support them yet
+            if (conf->configurationType() == ConfigurationType::Enabled) {
+                setValue(conf->level(), getBool(conf->value()), &m_enabledMap);
+            } else if (conf->configurationType() == ConfigurationType::ToFile) {
+                setValue(conf->level(), getBool(conf->value()), &m_toFileMap);
+            } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) {
+                setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap);
+            } else if (conf->configurationType() == ConfigurationType::Filename) {
+            // We do not yet configure filename but we will configure in another
+            // loop. This is because if file cannot be created, we will force ToFile
+            // to be false. Because configuring logger is not necessarily performance
+            // sensative operation, we can live with another loop; (by the way this loop
+            // is not very heavy either)
+            } else if (conf->configurationType() == ConfigurationType::Format) {
+                setValue(conf->level(), base::LogFormat(conf->level(), 
+                    base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap);
+            } else if (conf->configurationType() == ConfigurationType::MillisecondsWidth) {
+                setValue(Level::Global, 
+                    base::MillisecondsWidth(static_cast<int>(getULong(conf->value()))), &m_millisecondsWidthMap);
+            } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) {
+                setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap);
+            } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) {
+                setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_maxLogFileSizeMap);
+#if !defined(ELPP_NO_DEFAULT_LOG_FILE)
+                withFileSizeLimit.push_back(conf);
+#endif  // !defined(ELPP_NO_DEFAULT_LOG_FILE)
+            } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) {
+                setValue(conf->level(), static_cast<std::size_t>(getULong(conf->value())), &m_logFlushThresholdMap);
+            }
+        }
+        // As mentioned early, we will now set filename configuration in separate loop to deal with non-existent files
+        for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) {
+            Configuration* conf = *it;
+            if (conf->configurationType() == ConfigurationType::Filename) {
+                insertFile(conf->level(), conf->value());
+            }
+        }
+        for (std::vector<Configuration*>::iterator conf = withFileSizeLimit.begin();
+                conf != withFileSizeLimit.end(); ++conf) {
+                // This is not unsafe as mutex is locked in currect scope
+                unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback);
+        }
+    }
+
+    unsigned long getULong(std::string confVal) {
+        bool valid = true;
+        base::utils::Str::trim(confVal);
+        valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(),
+                [](char c) { return !base::utils::Str::isDigit(c); }) == confVal.end();
+        if (!valid) {
+            valid = false;
+            ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]");
+            return 0;
+        }
+        return atol(confVal.c_str());
+    }
+
+    std::string resolveFilename(const std::string& filename) {
+        std::string resultingFilename = filename;
+        std::size_t dateIndex = std::string::npos;
+        std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename);
+        if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) {
+            while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) {
+                dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1);
+            }
+            if (dateIndex != std::string::npos) {
+                const char* ptr = resultingFilename.c_str() + dateIndex;
+                // Goto end of specifier
+                ptr += dateTimeFormatSpecifierStr.size();
+                std::string fmt;
+                if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) {
+                    // User has provided format for date/time
+                    ++ptr;
+                    int count = 1;  // Start by 1 in order to remove starting brace
+                    std::stringstream ss;
+                    for (; *ptr; ++ptr, ++count) {
+                        if (*ptr == '}') {
+                            ++count;  // In order to remove ending brace
+                            break;
+                        }
+                        ss << *ptr;
+                    }
+                    resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count);
+                    fmt = ss.str();
+                } else {
+                    fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename);
+                }
+                base::MillisecondsWidth msWidth(3);
+                std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &msWidth);
+                base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename
+                base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now);
+            }
+        }
+        return resultingFilename;
+    }
+
+    void insertFile(Level level, const std::string& fullFilename) {
+        std::string resolvedFilename = resolveFilename(fullFilename);
+        if (resolvedFilename.empty()) {
+            std::cerr << "Could not load empty file for logging, please re-check your configurations for level ["
+                    << LevelHelper::convertToString(level) << "]";
+        }
+        std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator);
+        if (filePath.size() < resolvedFilename.size()) {
+            base::utils::File::createPath(filePath);
+        }
+        auto create = [&](Level level) {
+            base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename);
+            base::type::fstream_t* fs = nullptr;
+            if (filestreamIter == m_logStreamsReference->end()) {
+                // We need a completely new stream, nothing to share with
+                fs = base::utils::File::newFileStream(resolvedFilename);
+                m_filenameMap.insert(std::make_pair(level, resolvedFilename));
+                m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs)));
+                m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level))));
+            } else {
+                // Woops! we have an existing one, share it!
+                m_filenameMap.insert(std::make_pair(level, filestreamIter->first));
+                m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second)));
+                fs = filestreamIter->second.get();
+            }
+            if (fs == nullptr) {
+                // We display bad file error from newFileStream()
+                ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" 
+                    << LevelHelper::convertToString(level) << "] to FALSE", false);
+                setValue(level, false, &m_toFileMap);
+            }
+        };
+        // If we dont have file conf for any level, create it for Level::Global first
+        // otherwise create for specified level
+        create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level);
+    }
+
+    bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) {
+        base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get();
+        if (fs == nullptr) {
+            return true;
+        }
+        std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize");
+        std::size_t currFileSize = base::utils::File::getSizeOfFile(fs);
+        if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) {
+            std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename");
+            ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level ["
+                    << LevelHelper::convertToString(level) << "]");
+            fs->close();
+            PreRollOutCallback(fname.c_str(), currFileSize);
+            fs->open(fname, std::fstream::out | std::fstream::trunc);
+            return true;
+        }
+        return false;
+    }
+
+    bool validateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) {
+        base::threading::ScopedLock scopedLock(lock());
+        return unsafeValidateFileRolling(level, PreRollOutCallback);
+    }
+};
+/// @brief Class that keeps record of current line hit for occasional logging
+class HitCounter {
+public:
+    HitCounter(void) :
+        m_filename(""),
+        m_lineNumber(0),
+        m_hitCounts(0) {
+    }
+
+    HitCounter(const char* filename, unsigned long int lineNumber) :
+        m_filename(filename),
+        m_lineNumber(lineNumber),
+        m_hitCounts(0) {
+    }
+
+    HitCounter(const HitCounter& hitCounter) :
+        m_filename(hitCounter.m_filename),
+        m_lineNumber(hitCounter.m_lineNumber),
+        m_hitCounts(hitCounter.m_hitCounts) {
+    }
+
+    HitCounter& operator=(const HitCounter& hitCounter) {
+        m_filename = hitCounter.m_filename;
+        m_lineNumber = hitCounter.m_lineNumber;
+        m_hitCounts = hitCounter.m_hitCounts;
+        return *this;
+    }
+
+    virtual ~HitCounter(void) {
+    }
+
+    /// @brief Resets location of current hit counter
+    inline void resetLocation(const char* filename, unsigned long int lineNumber) {
+        m_filename = filename;
+        m_lineNumber = lineNumber;
+    }
+
+    /// @brief Validates hit counts and resets it if necessary
+    inline void validateHitCounts(std::size_t n) {
+        if (m_hitCounts >= base::consts::kMaxLogPerCounter) {
+            m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0);
+        }
+        ++m_hitCounts;
+    }
+
+    inline const char* filename(void) const {
+        return m_filename;
+    }
+
+    inline unsigned long int lineNumber(void) const {
+        return m_lineNumber;
+    }
+
+    inline std::size_t hitCounts(void) const {
+        return m_hitCounts;
+    }
+
+    inline void increment(void) {
+        ++m_hitCounts;
+    }
+
+    class Predicate {
+    public:
+        Predicate(const char* filename, unsigned long int lineNumber)
+            : m_filename(filename),
+              m_lineNumber(lineNumber) {
+        }
+        inline bool operator()(const HitCounter* counter) {
+            return ((counter != nullptr) &&
+                    (strcmp(counter->m_filename, m_filename) == 0) &&
+                    (counter->m_lineNumber == m_lineNumber));
+        }
+
+    private:
+        const char* m_filename;
+        unsigned long int m_lineNumber;
+    };
+
+private:
+    const char* m_filename;
+    unsigned long int m_lineNumber;
+    std::size_t m_hitCounts;
+};
+/// @brief Repository for hit counters used across the application
+class RegisteredHitCounters : public base::utils::RegistryWithPred<base::HitCounter, base::HitCounter::Predicate> {
+public:
+    /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one
+    /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
+    bool validateEveryN(const char* filename, unsigned long int lineNumber, std::size_t n) {
+        base::threading::ScopedLock scopedLock(lock());
+        base::HitCounter* counter = get(filename, lineNumber);
+        if (counter == nullptr) {
+            registerNew(counter = new base::HitCounter(filename, lineNumber));
+        }
+        counter->validateHitCounts(n);
+        bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0);
+        return result;
+    }
+
+    /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one
+    /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
+    bool validateAfterN(const char* filename, unsigned long int lineNumber, std::size_t n) {
+        base::threading::ScopedLock scopedLock(lock());
+        base::HitCounter* counter = get(filename, lineNumber);
+        if (counter == nullptr) {
+            registerNew(counter = new base::HitCounter(filename, lineNumber));
+        }
+        // Do not use validateHitCounts here since we do not want to reset counter here
+        // Note the >= instead of > because we are incrementing
+        // after this check
+        if (counter->hitCounts() >= n)
+            return true;
+        counter->increment();
+        return false;
+    }
+
+    /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one
+    /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned
+    bool validateNTimes(const char* filename, unsigned long int lineNumber, std::size_t n) {
+        base::threading::ScopedLock scopedLock(lock());
+        base::HitCounter* counter = get(filename, lineNumber);
+        if (counter == nullptr) {
+            registerNew(counter = new base::HitCounter(filename, lineNumber));
+        }
+        counter->increment();
+        // Do not use validateHitCounts here since we do not want to reset counter here
+        if (counter->hitCounts() <= n)
+            return true;
+        return false;
+    }
+
+    /// @brief Gets hit counter registered at specified position
+    inline const base::HitCounter* getCounter(const char* filename, unsigned long int lineNumber) {
+        base::threading::ScopedLock scopedLock(lock());
+        return get(filename, lineNumber);
+    }
+};
+/// @brief Action to be taken for dispatching
+enum class DispatchAction : base::type::EnumType {
+    None = 1, NormalLog = 2, SysLog = 4
+};
+}  // namespace base
+template <typename T>
+class Callback : protected base::threading::ThreadSafe {
+public:
+    Callback(void) : m_enabled(true) {}
+    inline bool enabled(void) const { return m_enabled; }
+    inline void setEnabled(bool enabled) {
+        base::threading::ScopedLock scopedLock(lock());
+        m_enabled = enabled;
+    }
+protected:
+    virtual void handle(const T* handlePtr) = 0;
+private:
+    bool m_enabled;
+};
+class LogDispatchData {
+public:
+    LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {}
+    inline const LogMessage* logMessage(void) const { return m_logMessage; }
+    inline base::DispatchAction dispatchAction(void) const { return m_dispatchAction; }
+private:
+    LogMessage* m_logMessage;
+    base::DispatchAction m_dispatchAction;
+    friend class base::LogDispatcher;
+
+    inline void setLogMessage(LogMessage* logMessage) { m_logMessage = logMessage; }
+    inline void setDispatchAction(base::DispatchAction dispatchAction) { m_dispatchAction = dispatchAction; }
+};
+class LogDispatchCallback : public Callback<LogDispatchData> {
+private:
+    friend class base::LogDispatcher;
+};
+class PerformanceTrackingCallback : public Callback<PerformanceTrackingData> {
+private:
+    friend class base::PerformanceTracker;
+};
+class LogBuilder : base::NoCopy {
+public:
+    virtual ~LogBuilder(void) { ELPP_INTERNAL_INFO(3, "Destroying log builder...")}
+    virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0;
+    void convertToColoredOutput(base::type::string_t* logLine, Level level) {
+        if (!base::utils::s_termSupportsColor) return;
+        const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m");
+        if (level == Level::Error || level == Level::Fatal)
+            *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor;
+        else if (level == Level::Warning)
+            *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor;
+    }
+private:
+    friend class el::base::DefaultLogDispatchCallback;
+};
+typedef std::shared_ptr<LogBuilder> LogBuilderPtr;
+/// @brief Represents a logger holding ID and configurations we need to write logs
+///
+/// @detail This class does not write logs itself instead its used by writer to read configuations from.
+class Logger : public base::threading::ThreadSafe, public Loggable {
+public:
+    Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) :
+            m_id(id),
+            m_typedConfigurations(nullptr),
+            m_parentApplicationName(std::string()),
+            m_isConfigured(false),
+            m_logStreamsReference(logStreamsReference) {
+        initUnflushedCount();
+    }
+
+    Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference) :
+            m_id(id),
+            m_typedConfigurations(nullptr),
+            m_parentApplicationName(std::string()),
+            m_isConfigured(false),
+            m_logStreamsReference(logStreamsReference) {
+        initUnflushedCount();
+        configure(configurations);
+    }
+
+    Logger(const Logger& logger) {
+        base::utils::safeDelete(m_typedConfigurations);
+        m_id = logger.m_id;
+        m_typedConfigurations = logger.m_typedConfigurations;
+        m_parentApplicationName = logger.m_parentApplicationName;
+        m_isConfigured = logger.m_isConfigured;
+        m_configurations = logger.m_configurations;
+        m_unflushedCount = logger.m_unflushedCount;
+        m_logStreamsReference = logger.m_logStreamsReference;
+    }
+
+    Logger& operator=(const Logger& logger) {
+        base::utils::safeDelete(m_typedConfigurations);
+        m_id = logger.m_id;
+        m_typedConfigurations = logger.m_typedConfigurations;
+        m_parentApplicationName = logger.m_parentApplicationName;
+        m_isConfigured = logger.m_isConfigured;
+        m_configurations = logger.m_configurations;
+        m_unflushedCount = logger.m_unflushedCount;
+        m_logStreamsReference = logger.m_logStreamsReference;
+        return *this;
+    }
+
+    virtual ~Logger(void) {
+        base::utils::safeDelete(m_typedConfigurations);
+    }
+
+    virtual inline void log(el::base::type::ostream_t& os) const {
+        os << m_id.c_str();
+    }
+
+    /// @brief Configures the logger using specified configurations.
+    void configure(const Configurations& configurations) {
+        m_isConfigured = false;  // we set it to false in case if we fail
+        initUnflushedCount();
+        if (m_typedConfigurations != nullptr) {
+            Configurations* c = const_cast<Configurations*>(m_typedConfigurations->configurations());
+            if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) {
+                // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE
+                flush();
+            }
+        }
+        base::threading::ScopedLock scopedLock(lock());
+        if (m_configurations != configurations) {
+            m_configurations.setFromBase(const_cast<Configurations*>(&configurations));
+        }
+        base::utils::safeDelete(m_typedConfigurations);
+        m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference);
+        resolveLoggerFormatSpec();
+        m_isConfigured = true;
+    }
+
+    /// @brief Reconfigures logger using existing configurations
+    inline void reconfigure(void) {
+        ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]");
+        configure(m_configurations);
+    }
+
+    inline const std::string& id(void) const {
+        return m_id;
+    }
+
+    inline const std::string& parentApplicationName(void) const {
+        return m_parentApplicationName;
+    }
+
+    inline void setParentApplicationName(const std::string& parentApplicationName) {
+        m_parentApplicationName = parentApplicationName;
+    }
+
+    inline Configurations* configurations(void) {
+        return &m_configurations;
+    }
+
+    inline base::TypedConfigurations* typedConfigurations(void) {
+        return m_typedConfigurations;
+    }
+
+    static inline bool isValidId(const std::string& id) {
+        for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) {
+            if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) {
+                return false;
+            }
+        }
+        return true;
+    }
+    /// @brief Flushes logger to sync all log files for all levels
+    inline void flush(void) {
+        ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels");
+        base::threading::ScopedLock scopedLock(lock());
+        base::type::EnumType lIndex = LevelHelper::kMinValid;
+        LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+            flush(LevelHelper::castFromInt(lIndex), nullptr);
+            return false;
+        });
+    }
+
+    inline void flush(Level level, base::type::fstream_t* fs) {
+        if (fs == nullptr && m_typedConfigurations->toFile(level)) {
+            fs = m_typedConfigurations->fileStream(level);
+        }
+        if (fs != nullptr) {
+            fs->flush();
+            m_unflushedCount.find(level)->second = 0;
+        }
+    }
+
+    inline bool isFlushNeeded(Level level) {
+        return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level);
+    }
+
+    inline LogBuilder* logBuilder(void) const {
+        return m_logBuilder.get();
+    }
+
+    inline void setLogBuilder(const LogBuilderPtr& logBuilder) {
+        m_logBuilder = logBuilder;
+    }
+
+    inline bool enabled(Level level) const {
+        return m_typedConfigurations->enabled(level);
+    }
+    
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+#   define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\
+    template <typename T, typename... Args>\
+    inline void FUNCTION_NAME(const char*, const T&, const Args&...);\
+    template <typename T>\
+    inline void FUNCTION_NAME(const T&);
+
+    template <typename T, typename... Args> 
+    inline void verbose(int, const char*, const T&, const Args&...);
+
+    template <typename T> 
+    inline void verbose(int, const T&);
+
+    LOGGER_LEVEL_WRITERS_SIGNATURES(info)
+    LOGGER_LEVEL_WRITERS_SIGNATURES(debug)
+    LOGGER_LEVEL_WRITERS_SIGNATURES(warn)
+    LOGGER_LEVEL_WRITERS_SIGNATURES(error)
+    LOGGER_LEVEL_WRITERS_SIGNATURES(fatal)
+    LOGGER_LEVEL_WRITERS_SIGNATURES(trace)
+#   undef LOGGER_LEVEL_WRITERS_SIGNATURES
+#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+private:
+    std::string m_id;
+    base::TypedConfigurations* m_typedConfigurations;
+    base::type::stringstream_t m_stream;
+    std::string m_parentApplicationName;
+    bool m_isConfigured;
+    Configurations m_configurations;
+    std::map<Level, unsigned int> m_unflushedCount;
+    base::LogStreamsReferenceMap* m_logStreamsReference;
+    LogBuilderPtr m_logBuilder;
+
+    friend class el::LogMessage;
+    friend class el::Loggers;
+    friend class el::Helpers;
+    friend class el::base::RegisteredLoggers;
+    friend class el::base::DefaultLogDispatchCallback;
+    friend class el::base::MessageBuilder;
+    friend class el::base::Writer;
+    friend class el::base::PErrorWriter;
+    friend class el::base::Storage;
+    friend class el::base::PerformanceTracker;
+    friend class el::base::LogDispatcher;
+
+    Logger(void);
+
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+    template <typename T, typename... Args>
+    void log_(Level, int, const char*, const T&, const Args&...);
+
+    template <typename T>
+    inline void log_(Level, int, const T&);
+
+    template <typename T, typename... Args>
+    void log(Level, const char*, const T&, const Args&...);
+
+    template <typename T>
+    inline void log(Level, const T&);
+#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+
+    void initUnflushedCount(void) {
+        m_unflushedCount.clear();
+        base::type::EnumType lIndex = LevelHelper::kMinValid;
+        LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+            m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0));
+            return false;
+        });
+    }
+
+    inline base::type::stringstream_t& stream(void) {
+        return m_stream;
+    }
+
+    void resolveLoggerFormatSpec(void) const {
+        base::type::EnumType lIndex = LevelHelper::kMinValid;
+        LevelHelper::forEachLevel(&lIndex, [&](void) -> bool {
+            base::LogFormat* logFormat = 
+                const_cast<base::LogFormat*>(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex)));
+            base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id);
+            return false;
+        });
+    }
+};
+namespace base {
+/// @brief Loggers repository
+class RegisteredLoggers : public base::utils::Registry<Logger, std::string> {
+public:
+    explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) :
+        m_defaultLogBuilder(defaultLogBuilder) {
+        m_defaultConfigurations.setToDefault();
+    }
+
+    virtual ~RegisteredLoggers(void) {
+        flushAll();
+    }
+
+    inline void setDefaultConfigurations(const Configurations& configurations) {
+        base::threading::ScopedLock scopedLock(lock());
+        m_defaultConfigurations.setFromBase(const_cast<Configurations*>(&configurations));
+    }
+
+    inline Configurations* defaultConfigurations(void) {
+        return &m_defaultConfigurations;
+    }
+
+    Logger* get(const std::string& id, bool forceCreation = true) {
+        base::threading::ScopedLock scopedLock(lock());
+        Logger* logger_ = base::utils::Registry<Logger, std::string>::get(id);
+        if (logger_ == nullptr && forceCreation) {
+            bool validId = Logger::isValidId(id);
+            if (!validId) {
+                ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger.");
+                return nullptr;
+            }
+            logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference);
+            logger_->m_logBuilder = m_defaultLogBuilder;
+            registerNew(id, logger_);
+        }
+        return logger_;
+    }
+
+    bool remove(const std::string& id) {
+        if (id == "default") {
+            return false;
+        }
+        Logger* logger = base::utils::Registry<Logger, std::string>::get(id);
+        if (logger != nullptr) {
+            unregister(logger);
+        }
+        return true;
+    }
+
+    inline bool has(const std::string& id) {
+        return get(id, false) != nullptr;
+    }
+
+    inline void unregister(Logger*& logger) {
+        base::threading::ScopedLock scopedLock(lock());
+        base::utils::Registry<Logger, std::string>::unregister(logger->id());
+    }
+
+    inline base::LogStreamsReferenceMap* logStreamsReference(void) {
+        return &m_logStreamsReference;
+    }
+
+    inline void flushAll(void) {
+        ELPP_INTERNAL_INFO(1, "Flushing all log files");
+        base::threading::ScopedLock scopedLock(lock());
+        for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin();
+                it != m_logStreamsReference.end(); ++it) {
+            if (it->second.get() == nullptr) continue;
+            it->second->flush();
+        }
+    }
+
+private:
+    LogBuilderPtr m_defaultLogBuilder;
+    Configurations m_defaultConfigurations;
+    base::LogStreamsReferenceMap m_logStreamsReference;
+    friend class el::base::Storage;
+};
+/// @brief Represents registries for verbose logging
+class VRegistry : base::NoCopy, public base::threading::ThreadSafe {
+public:
+    explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) {
+    }
+
+    /// @brief Sets verbose level. Accepted range is 0-9
+    inline void setLevel(base::type::VerboseLevel level) {
+        base::threading::ScopedLock scopedLock(lock());
+        if (level < 0)
+            m_level = 0;
+        else if (level > 9)
+            m_level = base::consts::kMaxVerboseLevel;
+        else
+            m_level = level;
+    }
+
+    inline base::type::VerboseLevel level(void) const {
+        return m_level;
+    }
+
+    inline void clearModules(void) {
+        base::threading::ScopedLock scopedLock(lock());
+        m_modules.clear();
+    }
+
+    void setModules(const char* modules) {
+        base::threading::ScopedLock scopedLock(lock());
+        auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) {
+            if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) {
+                std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev)));
+                ss.str(std::string(""));
+                ss << chr;
+            }
+            if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) {
+                std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx)));
+                ss.str(std::string(""));
+                ss << chr;
+            }
+            ss << sfx;
+        };
+        auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) {
+            if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) {
+                addSuffix(ss, ".h", nullptr);
+                m_modules.insert(std::make_pair(ss.str(), level));
+                addSuffix(ss, ".c", ".h");
+                m_modules.insert(std::make_pair(ss.str(), level));
+                addSuffix(ss, ".cpp", ".c");
+                m_modules.insert(std::make_pair(ss.str(), level));
+                addSuffix(ss, ".cc", ".cpp");
+                m_modules.insert(std::make_pair(ss.str(), level));
+                addSuffix(ss, ".cxx", ".cc");
+                m_modules.insert(std::make_pair(ss.str(), level));
+                addSuffix(ss, ".-inl.h", ".cxx");
+                m_modules.insert(std::make_pair(ss.str(), level));
+                addSuffix(ss, ".hxx", ".-inl.h");
+                m_modules.insert(std::make_pair(ss.str(), level));
+                addSuffix(ss, ".hpp", ".hxx");
+                m_modules.insert(std::make_pair(ss.str(), level));
+                addSuffix(ss, ".hh", ".hpp");
+            }
+            m_modules.insert(std::make_pair(ss.str(), level));
+        };
+        bool isMod = true;
+        bool isLevel = false;
+        std::stringstream ss;
+        int level = -1;
+        for (; *modules; ++modules) {
+            switch (*modules) {
+            case '=':
+                isLevel = true;
+                isMod = false;
+                break;
+            case ',':
+                isLevel = false;
+                isMod = true;
+                if (!ss.str().empty() && level != -1) {
+                    insert(ss, level);
+                    ss.str(std::string(""));
+                    level = -1;
+                }
+                break;
+            default:
+                if (isMod) {
+                    ss << *modules;
+                } else if (isLevel) {
+                    if (isdigit(*modules)) {
+                        level = static_cast<base::type::VerboseLevel>(*modules) - 48;
+                    }
+                }
+                break;
+            }
+        }
+        if (!ss.str().empty() && level != -1) {
+            insert(ss, level);
+        }
+    }
+
+    bool allowed(base::type::VerboseLevel vlevel, const char* file) {
+        base::threading::ScopedLock scopedLock(lock());
+        if (m_modules.empty() || file == nullptr) {
+            return vlevel <= m_level;
+        } else {
+            std::map<std::string, base::type::VerboseLevel>::iterator it = m_modules.begin();
+            for (; it != m_modules.end(); ++it) {
+                if (base::utils::Str::wildCardMatch(file, it->first.c_str())) {
+                    return vlevel <= it->second;
+                }
+            }
+            if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    inline const std::map<std::string, base::type::VerboseLevel>& modules(void) const {
+        return m_modules;
+    }
+
+    void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) {
+        if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") ||
+            commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) {
+            setLevel(base::consts::kMaxVerboseLevel);
+        } else if (commandLineArgs->hasParamWithValue("--v")) {
+            setLevel(atoi(commandLineArgs->getParamValue("--v")));
+        } else if (commandLineArgs->hasParamWithValue("--V")) {
+            setLevel(atoi(commandLineArgs->getParamValue("--V")));
+        } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) {
+            setModules(commandLineArgs->getParamValue("-vmodule"));
+        } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) {
+            setModules(commandLineArgs->getParamValue("-VMODULE"));
+        }
+    }
+    
+    /// @brief Whether or not vModules enabled
+    inline bool vModulesEnabled(void) {
+        return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags);
+    }
+
+private:
+    base::type::VerboseLevel m_level;
+    base::type::EnumType* m_pFlags;
+    std::map<std::string, base::type::VerboseLevel> m_modules;
+};
+}  // namespace base
+class LogMessage {
+public:
+    LogMessage(Level level, const std::string& file, unsigned long int line, const std::string& func,
+                          base::type::VerboseLevel verboseLevel, Logger* logger) :
+                  m_level(level), m_file(file), m_line(line), m_func(func),
+                  m_verboseLevel(verboseLevel), m_logger(logger), m_message(std::move(logger->stream().str())) {
+    }
+    inline Level level(void) const { return m_level; }
+    inline const std::string& file(void) const { return m_file; }
+    inline unsigned long int line(void) const { return m_line; } // NOLINT
+    inline const std::string& func(void) const { return m_func; }
+    inline base::type::VerboseLevel verboseLevel(void) const { return m_verboseLevel; }
+    inline Logger* logger(void) const { return m_logger; }
+    inline const base::type::string_t& message(void) const { return m_message; }
+private:
+    Level m_level;
+    std::string m_file;
+    unsigned long int m_line;
+    std::string m_func;
+    base::type::VerboseLevel m_verboseLevel;
+    Logger* m_logger;
+    base::type::string_t m_message;
+};
+namespace base {
+#if ELPP_ASYNC_LOGGING
+class AsyncLogItem {
+public:
+    explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine)
+        : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {}
+    virtual ~AsyncLogItem() {}
+    inline LogMessage* logMessage(void) { return &m_logMessage; }
+    inline LogDispatchData* data(void) { return &m_dispatchData; }
+    inline base::type::string_t logLine(void) { return m_logLine; }
+private:
+    LogMessage m_logMessage;
+    LogDispatchData m_dispatchData;
+    base::type::string_t m_logLine;
+};
+class AsyncLogQueue : public base::threading::ThreadSafe {
+public:
+    virtual ~AsyncLogQueue() {
+        ELPP_INTERNAL_INFO(6, "~AsyncLogQueue");
+    }
+    
+    inline AsyncLogItem next(void) {
+        base::threading::ScopedLock scopedLock(lock());
+        AsyncLogItem result = m_queue.front();
+        m_queue.pop();
+        return result;
+    }
+    
+    inline void push(const AsyncLogItem& item) {
+        base::threading::ScopedLock scopedLock(lock());
+        m_queue.push(item);
+    }
+    inline void pop(void) {
+        base::threading::ScopedLock scopedLock(lock());
+        m_queue.pop();
+    }
+    inline AsyncLogItem front(void) {
+        base::threading::ScopedLock scopedLock(lock());
+        return m_queue.front();
+    }
+    inline bool empty(void) {
+        base::threading::ScopedLock scopedLock(lock());
+        return m_queue.empty();
+    }
+private:
+    std::queue<AsyncLogItem> m_queue;
+};
+class IWorker {
+public:
+    virtual ~IWorker() {}
+    virtual void start() = 0;
+};
+#endif // ELPP_ASYNC_LOGGING
+/// @brief Easylogging++ management storage
+class Storage : base::NoCopy, public base::threading::ThreadSafe {
+public:
+#if ELPP_ASYNC_LOGGING
+    Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) :
+#else
+    explicit Storage(const LogBuilderPtr& defaultLogBuilder) :
+#endif  // ELPP_ASYNC_LOGGING
+        m_registeredHitCounters(new base::RegisteredHitCounters()),
+        m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)),
+        m_flags(0x0),
+        m_vRegistry(new base::VRegistry(0, &m_flags)),
+#if ELPP_ASYNC_LOGGING
+        m_asyncLogQueue(new base::AsyncLogQueue()),
+        m_asyncDispatchWorker(asyncDispatchWorker),
+#endif  // ELPP_ASYNC_LOGGING
+        m_preRollOutCallback(base::defaultPreRollOutCallback) {
+        // Register default logger
+        m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId));
+        // Register performance logger and reconfigure format
+        Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId));
+        performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg"));
+        performanceLogger->reconfigure();
+#if defined(ELPP_SYSLOG)
+        // Register syslog logger and reconfigure format
+        Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId));
+        sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg"));
+        sysLogLogger->reconfigure();
+#else
+        ELPP_UNUSED(base::consts::kSysLogLoggerId);
+#endif //  defined(ELPP_SYSLOG)
+        addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified);
+#if ELPP_ASYNC_LOGGING
+        installLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback"));
+#else
+        installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback"));
+#endif  // ELPP_ASYNC_LOGGING
+            installPerformanceTrackingCallback<base::DefaultPerformanceTrackingCallback>(std::string("DefaultPerformanceTrackingCallback"));
+            ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized");
+#if ELPP_ASYNC_LOGGING
+        m_asyncDispatchWorker->start();
+#endif  // ELPP_ASYNC_LOGGING
+    }
+
+    virtual ~Storage(void) {
+        ELPP_INTERNAL_INFO(4, "Destroying storage");
+#if ELPP_ASYNC_LOGGING
+        ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous");
+        uninstallLogDispatchCallback<base::AsyncLogDispatchCallback>(std::string("AsyncLogDispatchCallback"));
+        installLogDispatchCallback<base::DefaultLogDispatchCallback>(std::string("DefaultLogDispatchCallback"));
+        ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker");
+        base::utils::safeDelete(m_asyncDispatchWorker);
+        ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue");
+        base::utils::safeDelete(m_asyncLogQueue);
+#endif  // ELPP_ASYNC_LOGGING
+        ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters");
+        base::utils::safeDelete(m_registeredHitCounters);
+        ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers");
+        base::utils::safeDelete(m_registeredLoggers);
+        ELPP_INTERNAL_INFO(5, "Destroying vRegistry");
+        base::utils::safeDelete(m_vRegistry);
+    }
+
+    inline bool validateEveryNCounter(const char* filename, unsigned long int lineNumber, std::size_t occasion) {
+        return hitCounters()->validateEveryN(filename, lineNumber, occasion);
+    }
+
+    inline bool validateAfterNCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT
+        return hitCounters()->validateAfterN(filename, lineNumber, n);
+    }
+
+    inline bool validateNTimesCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT
+        return hitCounters()->validateNTimes(filename, lineNumber, n);
+    }
+
+    inline base::RegisteredHitCounters* hitCounters(void) const {
+        return m_registeredHitCounters;
+    }
+
+    inline base::RegisteredLoggers* registeredLoggers(void) const {
+        return m_registeredLoggers;
+    }
+    
+    inline base::VRegistry* vRegistry(void) const {
+        return m_vRegistry;
+    }
+
+#if ELPP_ASYNC_LOGGING
+    inline base::AsyncLogQueue* asyncLogQueue(void) const {
+        return m_asyncLogQueue;
+    }
+#endif  // ELPP_ASYNC_LOGGING
+
+    inline const base::utils::CommandLineArgs* commandLineArgs(void) const {
+        return &m_commandLineArgs;
+    }
+
+    inline void addFlag(LoggingFlag flag) {
+        base::utils::addFlag(flag, &m_flags);
+    }
+
+    inline void removeFlag(LoggingFlag flag) {
+        base::utils::removeFlag(flag, &m_flags);
+    }
+
+    inline bool hasFlag(LoggingFlag flag) const {
+        return base::utils::hasFlag(flag, m_flags);
+    }
+
+    inline base::type::EnumType flags(void) const {
+        return m_flags;
+    }
+
+    inline void setFlags(base::type::EnumType flags) {
+        m_flags = flags;
+    }
+
+    inline void setPreRollOutCallback(const PreRollOutCallback& callback) {
+        m_preRollOutCallback = callback;
+    }
+
+    inline void unsetPreRollOutCallback(void) {
+        m_preRollOutCallback = base::defaultPreRollOutCallback;
+    }
+
+    inline PreRollOutCallback& preRollOutCallback(void) {
+        return m_preRollOutCallback;
+    }
+
+    inline bool hasCustomFormatSpecifier(const char* formatSpecifier) {
+        base::threading::ScopedLock scopedLock(lock());
+        return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(),
+                formatSpecifier) != m_customFormatSpecifiers.end();
+    }
+
+    inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) {
+        if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) {
+            return;
+        }
+        base::threading::ScopedLock scopedLock(lock());
+        m_customFormatSpecifiers.push_back(customFormatSpecifier);
+    }
+
+    inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) {
+        base::threading::ScopedLock scopedLock(lock());
+        std::vector<CustomFormatSpecifier>::iterator it = std::find(m_customFormatSpecifiers.begin(),
+                m_customFormatSpecifiers.end(), formatSpecifier);
+        if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) {
+            m_customFormatSpecifiers.erase(it);
+            return true;
+        }
+        return false;
+    }
+
+    const std::vector<CustomFormatSpecifier>* customFormatSpecifiers(void) const {
+        return &m_customFormatSpecifiers;
+    }
+
+    inline void setLoggingLevel(Level level) {
+        m_loggingLevel = level;
+    }
+
+    template <typename T>
+    inline bool installLogDispatchCallback(const std::string& id) {
+        return installCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks);
+    }
+
+    template <typename T>
+    inline void uninstallLogDispatchCallback(const std::string& id) {
+        uninstallCallback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks);
+    }
+    template <typename T>
+    inline T* logDispatchCallback(const std::string& id) {
+        return callback<T, base::type::LogDispatchCallbackPtr>(id, &m_logDispatchCallbacks);
+    }
+
+    template <typename T>
+    inline bool installPerformanceTrackingCallback(const std::string& id) {
+        return installCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks);
+    }
+
+    template <typename T>
+    inline void uninstallPerformanceTrackingCallback(const std::string& id) {
+         uninstallCallback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks);
+    }
+
+    template <typename T>
+    inline T* performanceTrackingCallback(const std::string& id) {
+        return callback<T, base::type::PerformanceTrackingCallbackPtr>(id, &m_performanceTrackingCallbacks);
+    }
+private:
+    base::RegisteredHitCounters* m_registeredHitCounters;
+    base::RegisteredLoggers* m_registeredLoggers;
+    base::type::EnumType m_flags;
+    base::VRegistry* m_vRegistry;
+#if ELPP_ASYNC_LOGGING
+    base::AsyncLogQueue* m_asyncLogQueue;
+    base::IWorker* m_asyncDispatchWorker;
+#endif  // ELPP_ASYNC_LOGGING
+    base::utils::CommandLineArgs m_commandLineArgs;
+    PreRollOutCallback m_preRollOutCallback;
+    std::map<std::string, base::type::LogDispatchCallbackPtr> m_logDispatchCallbacks;
+    std::map<std::string, base::type::PerformanceTrackingCallbackPtr> m_performanceTrackingCallbacks;
+    std::vector<CustomFormatSpecifier> m_customFormatSpecifiers;
+    Level m_loggingLevel;
+
+    friend class el::Helpers;
+    friend class el::base::DefaultLogDispatchCallback;
+    friend class el::LogBuilder;
+    friend class el::base::MessageBuilder;
+    friend class el::base::Writer;
+    friend class el::base::PerformanceTracker;
+    friend class el::base::LogDispatcher;
+
+    void setApplicationArguments(int argc, char** argv) {
+        m_commandLineArgs.setArgs(argc, argv);
+        m_vRegistry->setFromArgs(commandLineArgs());
+        // default log file
+#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+        if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) {
+            Configurations c;
+            c.setGlobally(ConfigurationType::Filename, std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam)));
+            registeredLoggers()->setDefaultConfigurations(c);
+            for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin();
+                    it != registeredLoggers()->end(); ++it) {
+                it->second->configure(c);
+            }
+        }
+#endif  // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG)
+#if defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+        if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) {
+            m_flags = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam));
+        }
+#endif  // defined(ELPP_LOGGING_FLAGS_FROM_ARG)
+    }
+
+    inline void setApplicationArguments(int argc, const char** argv) {
+        setApplicationArguments(argc, const_cast<char**>(argv));
+    }
+
+    template <typename T, typename TPtr>
+    inline bool installCallback(const std::string& id, std::map<std::string, TPtr>* mapT) {
+        if (mapT->find(id) == mapT->end()) {
+            mapT->insert(std::make_pair(id, TPtr(new T())));
+            return true;
+        }
+        return false;
+    }
+
+    template <typename T, typename TPtr>
+    inline void uninstallCallback(const std::string& id, std::map<std::string, TPtr>* mapT) {
+        if (mapT->find(id) != mapT->end()) {
+            mapT->erase(id);
+        }
+    }
+
+    template <typename T, typename TPtr>
+    inline T* callback(const std::string& id, std::map<std::string, TPtr>* mapT) {
+        typename std::map<std::string, TPtr>::iterator iter = mapT->find(id);
+        if (iter != mapT->end()) {
+            return static_cast<T*>(iter->second.get());
+        }
+        return nullptr;
+    }
+};
+extern ELPP_EXPORT base::type::StoragePointer elStorage;
+#define ELPP el::base::elStorage
+class DefaultLogDispatchCallback : public LogDispatchCallback {
+protected:
+    void handle(const LogDispatchData* data) {
+        m_data = data;
+        dispatch(std::move(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), 
+            m_data->dispatchAction() == base::DispatchAction::NormalLog)));
+    }
+private:
+    const LogDispatchData* m_data;
+    void dispatch(base::type::string_t&& logLine) {
+        if (m_data->dispatchAction() == base::DispatchAction::NormalLog) {
+            if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) {
+                base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream(m_data->logMessage()->level());
+                if (fs != nullptr) {
+                    fs->write(logLine.c_str(), logLine.size());
+                    if (fs->fail()) {
+                        ELPP_INTERNAL_ERROR("Unable to write log to file ["
+                            << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n"
+                                << "Few possible reasons (could be something else):\n" << "      * Permission denied\n"
+                                << "      * Disk full\n" << "      * Disk is not writable", true);
+                    } else {
+                        if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) {
+                            m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs);
+                        }
+                    }
+                } else {
+                    ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] "
+                        << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " 
+                        << m_data->logMessage()->logger()->id() << "]", false);
+                }
+            }
+            if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) {
+                if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
+                    m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level());
+                ELPP_COUT << ELPP_COUT_LINE(logLine);
+             }
+        }
+#if defined(ELPP_SYSLOG)
+        else if (m_data->dispatchAction() == base::DispatchAction::SysLog) {
+            // Determine syslog priority
+            int sysLogPriority = 0;
+            if (m_data->logMessage()->level() == Level::Fatal)
+                sysLogPriority = LOG_EMERG;
+            else if (m_data->logMessage()->level() == Level::Error)
+                sysLogPriority = LOG_ERR;
+            else if (m_data->logMessage()->level() == Level::Warning)
+                sysLogPriority = LOG_WARNING;
+            else if (m_data->logMessage()->level() == Level::Info)
+                sysLogPriority = LOG_INFO;
+            else if (m_data->logMessage()->level() == Level::Debug)
+                sysLogPriority = LOG_DEBUG;
+            else
+                sysLogPriority = LOG_NOTICE;
+#   if defined(ELPP_UNICODE)
+            char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
+            syslog(sysLogPriority, "%s", line);
+            free(line);
+#   else
+            syslog(sysLogPriority, "%s", logLine.c_str());
+#   endif
+        }
+#endif  // defined(ELPP_SYSLOG)
+    }
+};
+#if ELPP_ASYNC_LOGGING
+class AsyncLogDispatchCallback : public LogDispatchCallback {
+protected:
+    void handle(const LogDispatchData* data) {
+        base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), data->dispatchAction() == base::DispatchAction::NormalLog);
+        if (data->dispatchAction() == base::DispatchAction::NormalLog && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) {
+            if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput))
+                data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level());
+            ELPP_COUT << ELPP_COUT_LINE(logLine);
+        }
+        // Save resources and only queue if we want to write to file otherwise just ignore handler
+        if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) {
+            ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine));
+        }
+    }
+};
+class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe {
+public:
+    AsyncDispatchWorker() {
+        setContinueRunning(false);
+    }
+
+    virtual ~AsyncDispatchWorker() {
+        setContinueRunning(false);
+        ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue");
+        clean();
+        ELPP_INTERNAL_INFO(6, "Log queue cleaned");
+    }
+
+    inline bool clean() {
+        std::mutex m;
+        std::unique_lock<std::mutex> lk(m);
+        cv.wait(lk, []{ return !ELPP->asyncLogQueue()->empty(); });
+        emptyQueue();
+        lk.unlock();
+        cv.notify_one();
+        return ELPP->asyncLogQueue()->empty();
+    }
+
+    inline void emptyQueue() {
+        while (!ELPP->asyncLogQueue()->empty()) {
+            AsyncLogItem data = ELPP->asyncLogQueue()->next();
+            handle(&data);
+            base::threading::msleep(100);
+        }
+    }
+    
+    virtual inline void start() {
+        base::threading::msleep(5000); // Wait extra few seconds
+        setContinueRunning(true);
+        std::thread t1(&AsyncDispatchWorker::runner, this);
+        t1.join();
+    }
+
+    void handle(AsyncLogItem* logItem) {
+        LogDispatchData* data = logItem->data();
+        LogMessage* logMessage = logItem->logMessage();
+        Logger* logger = logMessage->logger();
+        base::TypedConfigurations* conf = logger->typedConfigurations();
+        base::type::string_t logLine = logItem->logLine();
+        if (data->dispatchAction() == base::DispatchAction::NormalLog) {
+            if (conf->toFile(logMessage->level())) {
+                base::type::fstream_t* fs = conf->fileStream(logMessage->level());
+                if (fs != nullptr) {
+                    fs->write(logLine.c_str(), logLine.size());
+                    if (fs->fail()) {
+                        ELPP_INTERNAL_ERROR("Unable to write log to file ["
+                            << conf->filename(logMessage->level()) << "].\n"
+                                << "Few possible reasons (could be something else):\n" << "      * Permission denied\n"
+                                << "      * Disk full\n" << "      * Disk is not writable", true);
+                    } else {
+                        if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) {
+                            logger->flush(logMessage->level(), fs);
+                        }
+                    }
+                } else {
+                    ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] "
+                        << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false);
+                }
+            }
+        }
+#   if defined(ELPP_SYSLOG)
+        else if (data->dispatchAction() == base::DispatchAction::SysLog) {
+            // Determine syslog priority
+            int sysLogPriority = 0;
+            if (logMessage->level() == Level::Fatal)
+                sysLogPriority = LOG_EMERG;
+            else if (logMessage->level() == Level::Error)
+                sysLogPriority = LOG_ERR;
+            else if (logMessage->level() == Level::Warning)
+                sysLogPriority = LOG_WARNING;
+            else if (logMessage->level() == Level::Info)
+                sysLogPriority = LOG_INFO;
+            else if (logMessage->level() == Level::Debug)
+                sysLogPriority = LOG_DEBUG;
+            else
+                sysLogPriority = LOG_NOTICE;
+#      if defined(ELPP_UNICODE)
+            char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str());
+            syslog(sysLogPriority, "%s", line);
+            free(line);
+#      else
+            syslog(sysLogPriority, "%s", logLine.c_str());
+#      endif
+        }
+#   endif  // defined(ELPP_SYSLOG)
+    }
+
+    void run() {
+        while (continueRunning()) {
+            emptyQueue();
+            base::threading::msleep(10); // 10ms
+        }
+    }
+
+    static void* runner(void *context) {
+        static_cast<AsyncDispatchWorker*>(context)->run();
+        return NULL;
+    }
+    
+    void setContinueRunning(bool value) {
+        base::threading::ScopedLock scopedLock(m_continueRunningMutex);
+        m_continueRunning = value;
+    }
+    bool continueRunning(void) {
+        return m_continueRunning;
+    }
+private:
+    std::condition_variable cv;
+    bool m_continueRunning;
+    base::threading::Mutex m_continueRunningMutex;
+};
+#endif  // ELPP_ASYNC_LOGGING
+}  // namespace base
+namespace base {
+class DefaultLogBuilder : public LogBuilder {
+public:
+    base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const {
+        base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations();
+        const base::LogFormat* logFormat = &tc->logFormat(logMessage->level());
+        base::type::string_t logLine = logFormat->format();
+        char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = "";
+        const char* bufLim = buff + sizeof(buff);
+        if (logFormat->hasFlag(base::FormatFlags::AppName)) {
+            // App name
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier,
+                    logMessage->logger()->parentApplicationName());
+        }
+        if (logFormat->hasFlag(base::FormatFlags::ThreadId)) {
+            // Thread ID
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier,
+                    base::threading::getCurrentThreadId());
+        }
+        if (logFormat->hasFlag(base::FormatFlags::DateTime)) {
+            // DateTime
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier,
+                    base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), 
+                        &tc->millisecondsWidth(logMessage->level())));
+        }
+        if (logFormat->hasFlag(base::FormatFlags::Function)) {
+            // Function
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func());
+        }
+        if (logFormat->hasFlag(base::FormatFlags::File)) {
+            // File
+            char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
+            base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff);
+            buf = base::utils::Str::addToBuff(buff, buf, bufLim);
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff));
+        }
+        if (logFormat->hasFlag(base::FormatFlags::FileBase)) {
+            // FileBase
+            char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength);
+            base::utils::File::buildBaseFilename(logMessage->file(), buff);
+            buf = base::utils::Str::addToBuff(buff, buf, bufLim);
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff));
+        }
+        if (logFormat->hasFlag(base::FormatFlags::Line)) {
+            // Line
+            char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength);
+            buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), 
+                base::consts::kSourceLineMaxLength, buf, bufLim, false);
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff));
+        }
+        if (logFormat->hasFlag(base::FormatFlags::Location)) {
+            // Location
+            char* buf = base::utils::Str::clearBuff(buff, 
+                base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength);
+            base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff);
+            buf = base::utils::Str::addToBuff(buff, buf, bufLim);
+            buf = base::utils::Str::addToBuff(":", buf, bufLim);
+            buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), 
+                base::consts::kSourceLineMaxLength, buf, bufLim, false);
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff));
+        }
+        if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) {
+            // Verbose level
+            char* buf = base::utils::Str::clearBuff(buff, 1);
+            buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false);
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff));
+        }
+        if (logFormat->hasFlag(base::FormatFlags::LogMessage)) {
+            // Log message
+            base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message());
+        }
+#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
+        for (std::vector<CustomFormatSpecifier>::const_iterator it = ELPP->customFormatSpecifiers()->begin();
+                it != ELPP->customFormatSpecifiers()->end(); ++it) {
+            std::string fs(it->formatSpecifier());
+            base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end());
+            base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, std::string(it->resolver()()));
+        }
+#endif  // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS)
+        if (appendNewLine) logLine += ELPP_LITERAL("\n");
+        return logLine;
+    }
+};
+/// @brief Dispatches log messages
+class LogDispatcher : base::NoCopy {
+public:
+    LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) :
+        m_proceed(proceed),
+        m_logMessage(std::move(logMessage)),
+        m_dispatchAction(std::move(dispatchAction)) {
+    }
+
+    void dispatch(void) {
+        if (m_proceed && m_dispatchAction == base::DispatchAction::None) {
+            m_proceed = false;
+        }
+        if (!m_proceed) {
+            return;
+        }
+        // We minimize the time of ELPP's lock - this lock is released after log is written
+        base::threading::ScopedLock scopedLock(ELPP->lock());
+        base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations;
+        if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) {
+            tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback());
+        }
+        LogDispatchCallback* callback = nullptr;
+        LogDispatchData data;
+        for (const std::pair<std::string, base::type::LogDispatchCallbackPtr>& h 
+                : ELPP->m_logDispatchCallbacks) {
+            callback = h.second.get();
+            if (callback != nullptr && callback->enabled()) {
+                data.setLogMessage(&m_logMessage);
+                data.setDispatchAction(m_dispatchAction);
+                callback->acquireLock();
+                callback->handle(&data);
+                callback->releaseLock();
+            }
+        }
+    }
+
+private:
+    bool m_proceed;
+    LogMessage m_logMessage;
+    base::DispatchAction m_dispatchAction;
+};
+#if defined(ELPP_STL_LOGGING)
+/// @brief Workarounds to write some STL logs
+///
+/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers
+/// of same type and provide iterator interface and pass it on to writeIterator().
+/// Remember, this is passed by value in constructor so that we dont change original containers.
+/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer))
+namespace workarounds {
+/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T
+template <typename T, typename Container>
+class IterableContainer {
+public:
+    typedef typename Container::iterator iterator;
+    typedef typename Container::const_iterator const_iterator;
+    IterableContainer(void) {}
+    virtual ~IterableContainer(void) {}
+    iterator begin(void) { return getContainer().begin(); }
+    iterator end(void) { return getContainer().end(); }
+private:
+    virtual Container& getContainer(void) = 0;
+};
+/// @brief Implements IterableContainer and provides iterable std::priority_queue class
+template<typename T, typename Container = std::vector<T>, typename Comparator = std::less<typename Container::value_type>>
+class IterablePriorityQueue : public IterableContainer<T, Container>, public std::priority_queue<T, Container, Comparator> {
+public:
+    IterablePriorityQueue(std::priority_queue<T, Container, Comparator> queue_) {
+        std::size_t count_ = 0;
+        while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) {
+            this->push(queue_.top());
+            queue_.pop();
+        }
+    }
+private:
+    inline Container& getContainer(void) {
+        return this->c;
+    }
+};
+/// @brief Implements IterableContainer and provides iterable std::queue class
+template<typename T, typename Container = std::deque<T>>
+class IterableQueue : public IterableContainer<T, Container>, public std::queue<T, Container> {
+public:
+    IterableQueue(std::queue<T, Container> queue_) {
+        std::size_t count_ = 0;
+        while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) {
+            this->push(queue_.front());
+            queue_.pop();
+        }
+    }
+private:
+    inline Container& getContainer(void) {
+        return this->c;
+    }
+};
+/// @brief Implements IterableContainer and provides iterable std::stack class
+template<typename T, typename Container = std::deque<T>>
+class IterableStack : public IterableContainer<T, Container>, public std::stack<T, Container> {
+public:
+    IterableStack(std::stack<T, Container> stack_) {
+        std::size_t count_ = 0;
+        while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) {
+            this->push(stack_.top());
+            stack_.pop();
+        }
+    }
+private:
+    inline Container& getContainer(void) {
+        return this->c;
+    }
+};
+}  // namespace workarounds
+#endif  // defined(ELPP_STL_LOGGING)
+// Log message builder
+class MessageBuilder {
+public:
+    MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {}
+    void initialize(Logger* logger) {
+        m_logger = logger;
+        m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? 
+            ELPP_LITERAL("\n    ") : ELPP_LITERAL(", ");
+    }
+
+#   define ELPP_SIMPLE_LOG(LOG_TYPE)\
+    inline MessageBuilder& operator<<(LOG_TYPE msg) {\
+        m_logger->stream() << msg;\
+        if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\
+            m_logger->stream() << " ";\
+        }\
+        return *this;\
+    }
+
+    inline MessageBuilder& operator<<(const std::string& msg) {
+        return operator<<(msg.c_str());
+    }
+    ELPP_SIMPLE_LOG(char)
+    ELPP_SIMPLE_LOG(bool)
+    ELPP_SIMPLE_LOG(signed short)
+    ELPP_SIMPLE_LOG(unsigned short)
+    ELPP_SIMPLE_LOG(signed int)
+    ELPP_SIMPLE_LOG(unsigned int)
+    ELPP_SIMPLE_LOG(signed long)
+    ELPP_SIMPLE_LOG(unsigned long)
+    ELPP_SIMPLE_LOG(float)
+    ELPP_SIMPLE_LOG(double)
+    ELPP_SIMPLE_LOG(char*)
+    ELPP_SIMPLE_LOG(const char*)
+    ELPP_SIMPLE_LOG(const void*)
+    ELPP_SIMPLE_LOG(long double)
+    inline MessageBuilder& operator<<(const std::wstring& msg) {
+        return operator<<(msg.c_str());
+    }
+    inline MessageBuilder& operator<<(const wchar_t* msg) {
+        if (msg == nullptr) {
+            m_logger->stream() << base::consts::kNullPointer;
+            return *this;
+        }
+#   if defined(ELPP_UNICODE)
+        m_logger->stream() << msg;
+#   else
+        char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg);
+        m_logger->stream() << buff_;
+        free(buff_);
+#   endif
+        if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {
+            m_logger->stream() << " ";
+        }
+        return *this;
+    }
+    // ostream manipulators
+    inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) {
+        m_logger->stream() << OStreamMani;
+        return *this;
+    }
+#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp)                                                    \
+    template <typename T>                                                                            \
+    inline MessageBuilder& operator<<(const temp<T>& template_inst) {                                \
+        return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size());      \
+    }
+#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp)                                                    \
+    template <typename T1, typename T2>                                                              \
+    inline MessageBuilder& operator<<(const temp<T1, T2>& template_inst) {                           \
+        return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size());      \
+    }
+#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp)                                                  \
+    template <typename T1, typename T2, typename T3>                                                 \
+    inline MessageBuilder& operator<<(const temp<T1, T2, T3>& template_inst) {                       \
+        return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size());      \
+    }
+#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp)                                                   \
+    template <typename T1, typename T2, typename T3, typename T4>                                    \
+    inline MessageBuilder& operator<<(const temp<T1, T2, T3, T4>& template_inst) {                   \
+        return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size());      \
+    }
+#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp)                                                   \
+    template <typename T1, typename T2, typename T3, typename T4, typename T5>                       \
+    inline MessageBuilder& operator<<(const temp<T1, T2, T3, T4, T5>& template_inst) {               \
+        return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size());      \
+    }
+
+#if defined(ELPP_STL_LOGGING)
+    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector)
+    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list)
+    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque)
+    ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set)
+    ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset)
+    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map)
+    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap)
+    template <class T, class Container>
+    inline MessageBuilder& operator<<(const std::queue<T, Container>& queue_) {
+        base::workarounds::IterableQueue<T, Container> iterableQueue_ =
+                static_cast<base::workarounds::IterableQueue<T, Container> >(queue_);
+        return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size());
+    }
+    template <class T, class Container>
+    inline MessageBuilder& operator<<(const std::stack<T, Container>& stack_) {
+        base::workarounds::IterableStack<T, Container> iterableStack_ =
+                static_cast<base::workarounds::IterableStack<T, Container> >(stack_);
+        return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size());
+    }
+    template <class T, class Container, class Comparator>
+    inline MessageBuilder& operator<<(const std::priority_queue<T, Container, Comparator>& priorityQueue_) {
+        base::workarounds::IterablePriorityQueue<T, Container, Comparator> iterablePriorityQueue_ =
+                static_cast<base::workarounds::IterablePriorityQueue<T, Container, Comparator> >(priorityQueue_);
+        return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size());
+    }
+    template <class First, class Second>
+    inline MessageBuilder& operator<<(const std::pair<First, Second>& pair_) {
+        m_logger->stream() << ELPP_LITERAL("(");
+        operator << (static_cast<First>(pair_.first));
+        m_logger->stream() << ELPP_LITERAL(", ");
+        operator << (static_cast<Second>(pair_.second));
+        m_logger->stream() << ELPP_LITERAL(")");
+        return *this;
+    }
+    template <std::size_t Size>
+    inline MessageBuilder& operator<<(const std::bitset<Size>& bitset_) {
+        m_logger->stream() << ELPP_LITERAL("[");
+        operator << (bitset_.to_string());
+        m_logger->stream() << ELPP_LITERAL("]");
+        return *this;
+    }
+#   if defined(ELPP_LOG_STD_ARRAY)
+    template <class T, std::size_t Size>
+    inline MessageBuilder& operator<<(const std::array<T, Size>& array) {
+        return writeIterator(array.begin(), array.end(), array.size());
+    }
+#   endif  // defined(ELPP_LOG_STD_ARRAY)
+#   if defined(ELPP_LOG_UNORDERED_MAP)
+    ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map)
+    ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap)
+#   endif  // defined(ELPP_LOG_UNORDERED_MAP)
+#   if defined(ELPP_LOG_UNORDERED_SET)
+    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set)
+    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset)
+#   endif  // defined(ELPP_LOG_UNORDERED_SET)
+#endif  // defined(ELPP_STL_LOGGING)
+#if defined(ELPP_QT_LOGGING)
+    inline MessageBuilder& operator<<(const QString& msg) {
+#   if defined(ELPP_UNICODE)
+        m_logger->stream() << msg.toStdWString();
+#   else
+        m_logger->stream() << msg.toStdString();
+#   endif  // defined(ELPP_UNICODE)
+        return *this;
+    }
+    inline MessageBuilder& operator<<(const QByteArray& msg) {
+        return operator << (QString(msg));
+    }
+    inline MessageBuilder& operator<<(const QStringRef& msg) {
+        return operator<<(msg.toString());
+    }
+    inline MessageBuilder& operator<<(qint64 msg) {
+#   if defined(ELPP_UNICODE)
+        m_logger->stream() << QString::number(msg).toStdWString();
+#   else
+        m_logger->stream() << QString::number(msg).toStdString();
+#   endif  // defined(ELPP_UNICODE)
+        return *this;
+    }
+    inline MessageBuilder& operator<<(quint64 msg) {
+#   if defined(ELPP_UNICODE)
+        m_logger->stream() << QString::number(msg).toStdWString();
+#   else
+        m_logger->stream() << QString::number(msg).toStdString();
+#   endif  // defined(ELPP_UNICODE)
+        return *this;
+    }
+    inline MessageBuilder& operator<<(QChar msg) {
+        m_logger->stream() << msg.toLatin1();
+        return *this;
+    }
+    inline MessageBuilder& operator<<(const QLatin1String& msg) {
+        m_logger->stream() << msg.latin1();
+        return *this;
+    }
+    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList)
+    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector)
+    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue)
+    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet)
+    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList)
+    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack)
+    template <typename First, typename Second>
+    inline MessageBuilder& operator<<(const QPair<First, Second>& pair_) {
+        m_logger->stream() << ELPP_LITERAL("(");
+        operator << (static_cast<First>(pair_.first));
+        m_logger->stream() << ELPP_LITERAL(", ");
+        operator << (static_cast<Second>(pair_.second));
+        m_logger->stream() << ELPP_LITERAL(")");
+        return *this;
+    }
+    template <typename K, typename V>
+    inline MessageBuilder& operator<<(const QMap<K, V>& map_) {
+        m_logger->stream() << ELPP_LITERAL("[");
+        QList<K> keys = map_.keys();
+        typename QList<K>::const_iterator begin = keys.begin();
+        typename QList<K>::const_iterator end = keys.end();
+        int max_ = static_cast<int>(base::consts::kMaxLogPerContainer);  // to prevent warning
+        for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) {
+            m_logger->stream() << ELPP_LITERAL("(");
+            operator << (static_cast<K>(*begin));
+            m_logger->stream() << ELPP_LITERAL(", ");
+            operator << (static_cast<V>(map_.value(*begin)));
+            m_logger->stream() << ELPP_LITERAL(")");
+            m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL(""));
+        }
+        if (begin != end) {
+            m_logger->stream() << ELPP_LITERAL("...");
+        }
+        m_logger->stream() << ELPP_LITERAL("]");
+        return *this;
+    }
+    template <typename K, typename V>
+    inline MessageBuilder& operator<<(const QMultiMap<K, V>& map_) {
+        operator << (static_cast<QMap<K, V>>(map_));
+        return *this;
+    }
+    template <typename K, typename V>
+    inline MessageBuilder& operator<<(const QHash<K, V>& hash_) {
+        m_logger->stream() << ELPP_LITERAL("[");
+        QList<K> keys = hash_.keys();
+        typename QList<K>::const_iterator begin = keys.begin();
+        typename QList<K>::const_iterator end = keys.end();
+        int max_ = static_cast<int>(base::consts::kMaxLogPerContainer);  // prevent type warning
+        for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) {
+            m_logger->stream() << ELPP_LITERAL("(");
+            operator << (static_cast<K>(*begin));
+            m_logger->stream() << ELPP_LITERAL(", ");
+            operator << (static_cast<V>(hash_.value(*begin)));
+            m_logger->stream() << ELPP_LITERAL(")");
+            m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL(""));
+        }
+        if (begin != end) {
+            m_logger->stream() << ELPP_LITERAL("...");
+        }
+        m_logger->stream() << ELPP_LITERAL("]");
+        return *this;
+    }
+    template <typename K, typename V>
+    inline MessageBuilder& operator<<(const QMultiHash<K, V>& multiHash_) {
+        operator << (static_cast<QHash<K, V>>(multiHash_));
+        return *this;
+    }
+#endif  // defined(ELPP_QT_LOGGING)
+#if defined(ELPP_BOOST_LOGGING)
+    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector)
+    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector)
+    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list)
+    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque)
+    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map)
+    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map)
+    ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set)
+    ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set)
+#endif  // defined(ELPP_BOOST_LOGGING)
+
+/// @brief Macro used internally that can be used externally to make containers easylogging++ friendly
+///
+/// @detail This macro expands to write an ostream& operator<< for container. This container is expected to
+///         have begin() and end() methods that return respective iterators
+/// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets
+/// @param SizeMethod Method used to get size of container.
+/// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro
+///        for an example usage
+#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \
+    el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\
+        const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \
+            ELPP_LITERAL("\n    ") : ELPP_LITERAL(", ");\
+        ContainerType::const_iterator elem = container.begin();\
+        ContainerType::const_iterator endElem = container.end();\
+        std::size_t size_ = container.SizeMethod; \
+        ss << ELPP_LITERAL("[");\
+        for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \
+            ss << ElementInstance;\
+            ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\
+        }\
+        if (elem != endElem) {\
+            ss << ELPP_LITERAL("...");\
+        }\
+        ss << ELPP_LITERAL("]");\
+        return ss;\
+    }
+#if defined(ELPP_WXWIDGETS_LOGGING)
+    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector)
+#   define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem))
+#   define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem))
+#   define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \
+        ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")")
+#else
+#   define ELPP_WX_PTR_ENABLED(ContainerType)
+#   define ELPP_WX_ENABLED(ContainerType)
+#   define ELPP_WX_HASH_MAP_ENABLED(ContainerType)
+#endif  // defined(ELPP_WXWIDGETS_LOGGING)
+    // Other classes
+    template <class Class>
+    ELPP_SIMPLE_LOG(const Class&)
+#undef ELPP_SIMPLE_LOG
+#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG
+#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG
+#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG
+#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG
+#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG
+private:
+    Logger* m_logger;
+    const base::type::char_t* m_containerLogSeperator;
+
+    template<class Iterator>
+    inline MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) {
+        m_logger->stream() << ELPP_LITERAL("[");
+        for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) {
+            operator << (*begin_);
+            m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL(""));
+        }
+        if (begin_ != end_) {
+            m_logger->stream() << ELPP_LITERAL("...");
+        }
+        m_logger->stream() << ELPP_LITERAL("]");
+        if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {
+            m_logger->stream() << " ";
+        }
+        return *this;
+    }
+};
+/// @brief Writes nothing - Used when certain log is disabled
+class NullWriter : base::NoCopy {
+public:
+    NullWriter(void) {}
+
+    // Null manipulator
+    inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) {
+        return *this;
+    }
+
+    template <typename T>
+    inline NullWriter& operator<<(const T&) {
+        return *this;
+    }
+};
+/// @brief Main entry point of each logging
+class Writer : base::NoCopy {
+public:
+    Writer(Level level, const char* file, unsigned long int line,
+               const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
+               base::type::VerboseLevel verboseLevel = 0) :
+                   m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel),
+                   m_proceed(false), m_dispatchAction(dispatchAction) {
+    }
+
+    virtual ~Writer(void) {
+        processDispatch();
+    }
+
+    template <typename T>
+    inline Writer& operator<<(const T& log) {
+#if ELPP_LOGGING_ENABLED
+        if (m_proceed) {
+            m_messageBuilder << log;
+        }
+#endif  // ELPP_LOGGING_ENABLED
+        return *this;
+    }
+
+    inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) {
+#if ELPP_LOGGING_ENABLED
+        if (m_proceed) {
+            m_messageBuilder << log;
+        }
+#endif  // ELPP_LOGGING_ENABLED
+        return *this;
+    }
+
+    Writer& construct(Logger* logger, bool needLock = true) {
+        m_logger = logger;
+        initializeLogger(logger->id(), false, needLock);
+        m_messageBuilder.initialize(m_logger);
+        return *this;
+    }
+
+    Writer& construct(int count, const char* loggerIds, ...) {
+        if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
+            va_list loggersList;
+            va_start(loggersList, loggerIds);
+            const char* id = loggerIds;
+            for (int i = 0; i < count; ++i) {
+                m_loggerIds.push_back(std::string(id));
+                id = va_arg(loggersList, const char*);
+            }
+            va_end(loggersList);
+            initializeLogger(m_loggerIds.at(0));
+        } else {
+            initializeLogger(std::string(loggerIds));
+        }
+        m_messageBuilder.initialize(m_logger);
+        return *this;
+    }
+protected:
+    Level m_level;
+    const char* m_file;
+    const unsigned long int m_line;
+    const char* m_func;
+    base::type::VerboseLevel m_verboseLevel;
+    Logger* m_logger;
+    bool m_proceed;
+    base::MessageBuilder m_messageBuilder;
+    base::DispatchAction m_dispatchAction;
+    std::vector<std::string> m_loggerIds;
+    friend class el::Helpers;
+
+    void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true) {
+        if (lookup) {
+            m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically));
+        }
+        if (m_logger == nullptr) {
+            ELPP->acquireLock();
+            if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) {
+                // Somehow default logger has been unregistered. Not good! Register again
+                ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId));
+            }
+            ELPP->releaseLock();  // Need to unlock it for next writer
+            Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
+                    << "Logger [" << loggerId << "] is not registered yet!";
+            m_proceed = false;
+        } else {
+            if (needLock) {
+                m_logger->acquireLock();  // This should not be unlocked by checking m_proceed because
+                                          // m_proceed can be changed by lines below
+            }
+            if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) {
+                m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) :
+                        LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel);
+            } else {
+                m_proceed = m_logger->enabled(m_level);
+            }
+        }
+    }
+    
+    void processDispatch() {
+#if ELPP_LOGGING_ENABLED
+        if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) {
+            bool firstDispatched = false;
+            base::type::string_t logMessage;
+            std::size_t i = 0;
+            do {
+                if (m_proceed) {
+                    if (firstDispatched) {
+                        m_logger->stream() << logMessage;
+                    } else {
+                        firstDispatched = true;
+                        if (m_loggerIds.size() > 1) {
+                            logMessage = m_logger->stream().str();
+                        }
+                    }
+                    triggerDispatch();
+                } else if (m_logger != nullptr) {
+                    m_logger->stream().str(ELPP_LITERAL(""));
+                    m_logger->releaseLock();
+                }
+                if (i + 1 < m_loggerIds.size()) {
+                    initializeLogger(m_loggerIds.at(i + 1));
+                }
+            } while (++i < m_loggerIds.size());
+        } else {
+            if (m_proceed) {
+                triggerDispatch();
+            } else if (m_logger != nullptr) {
+                m_logger->stream().str(ELPP_LITERAL(""));
+                m_logger->releaseLock();
+            }
+        }
+#else
+        if (m_logger != nullptr) {
+            m_logger->stream().str(ELPP_LITERAL(""));
+            m_logger->releaseLock();
+        }
+#endif // ELPP_LOGGING_ENABLED
+    }
+
+    void triggerDispatch(void) {
+        if (m_proceed) {
+            base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel,
+                          m_logger), m_dispatchAction).dispatch();
+        }
+        if (m_logger != nullptr) {
+            m_logger->stream().str(ELPP_LITERAL(""));
+            m_logger->releaseLock();
+        }
+        if (m_proceed && m_level == Level::Fatal
+                && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) {
+            base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId)
+                    << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]";
+            std::stringstream reasonStream;
+            reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]"
+                << " If you wish to disable 'abort on fatal log' please use "
+                << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)";
+            base::utils::abort(1, reasonStream.str());
+        }
+        m_proceed = false;
+    }
+};
+class PErrorWriter : public base::Writer {
+public:
+    PErrorWriter(Level level, const char* file, unsigned long int line,
+               const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog,
+               base::type::VerboseLevel verboseLevel = 0) :
+        base::Writer(level, file, line, func, dispatchAction, verboseLevel) {
+    }
+
+    virtual ~PErrorWriter(void) {
+        if (m_proceed) {
+#if ELPP_COMPILER_MSVC
+            char buff[256];
+            strerror_s(buff, 256, errno);
+            m_logger->stream() << ": " << buff << " [" << errno << "]";
+#else
+            m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]";
+#endif
+        }
+    }
+};
+}  // namespace base
+// Logging from Logger class. Why this is here? Because we have Storage and Writer class available
+#if ELPP_VARIADIC_TEMPLATES_SUPPORTED
+    template <typename T, typename... Args>
+    void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) {
+        base::MessageBuilder b;
+        b.initialize(this);
+        while (*s) {
+            if (*s == base::consts::kFormatSpecifierChar) {
+                if (*(s + 1) == base::consts::kFormatSpecifierChar) {
+                    ++s;
+                } else {
+                    if (*(s + 1) == base::consts::kFormatSpecifierCharValue) {
+                        ++s;
+                        b << value;
+                        log_(level, vlevel, ++s, args...);
+                        return;
+                    }
+                }
+            }
+            b << *s++;
+        }
+        ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false);
+    }
+    template <typename T> 
+    inline void Logger::log_(Level level, int vlevel, const T& log) {
+        if (level == Level::Verbose) {
+            if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) {
+                base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", 
+                    base::DispatchAction::NormalLog, vlevel).construct(this, false) << log;
+            } else {
+                stream().str(ELPP_LITERAL(""));
+            }
+        } else {
+            base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log;
+        }
+    }
+    template <typename T, typename... Args>
+    void Logger::log(Level level, const char* s, const T& value, const Args&... args) {
+        base::threading::ScopedLock scopedLock(lock());
+        log_(level, 0, s, value, args...);
+    }
+    template <typename T> 
+    inline void Logger::log(Level level, const T& log) { 
+        base::threading::ScopedLock scopedLock(lock());
+        log_(level, 0, log);
+    }
+#   if ELPP_VERBOSE_LOG
+    template <typename T, typename... Args>
+    inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) {
+        base::threading::ScopedLock scopedLock(lock());
+        log_(el::Level::Verbose, vlevel, s, value, args...);
+    }
+    template <typename T>
+    inline void Logger::verbose(int vlevel, const T& log) {
+        base::threading::ScopedLock scopedLock(lock());
+        log_(el::Level::Verbose, vlevel, log);
+    }
+#   else
+    template <typename T, typename... Args>
+    inline void Logger::verbose(int, const char*, const T&, const Args&...) {
+        return;
+    }
+    template <typename T>
+    inline void Logger::verbose(int, const T&) {
+        return;
+    }
+#   endif  // ELPP_VERBOSE_LOG
+#   define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\
+    template <typename T, typename... Args>\
+    inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\
+        log(LOG_LEVEL, s, value, args...);\
+    }\
+    template <typename T>\
+    inline void Logger::FUNCTION_NAME(const T& value) {\
+        log(LOG_LEVEL, value);\
+    }
+#   define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\
+    template <typename T, typename... Args>\
+    inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\
+        return;\
+    }\
+    template <typename T>\
+    inline void Logger::FUNCTION_NAME(const T&) {\
+        return;\
+    }
+
+#   if ELPP_INFO_LOG
+    LOGGER_LEVEL_WRITERS(info, Level::Info)
+#   else
+    LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info)
+#   endif // ELPP_INFO_LOG
+#   if ELPP_DEBUG_LOG
+    LOGGER_LEVEL_WRITERS(debug, Level::Debug)
+#   else
+    LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug)
+#   endif // ELPP_DEBUG_LOG
+#   if ELPP_WARNING_LOG
+    LOGGER_LEVEL_WRITERS(warn, Level::Warning)
+#   else
+    LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning)
+#   endif // ELPP_WARNING_LOG
+#   if ELPP_ERROR_LOG
+    LOGGER_LEVEL_WRITERS(error, Level::Error)
+#   else
+    LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error)
+#   endif // ELPP_ERROR_LOG
+#   if ELPP_FATAL_LOG
+    LOGGER_LEVEL_WRITERS(fatal, Level::Fatal)
+#   else
+    LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal)
+#   endif // ELPP_FATAL_LOG
+#   if ELPP_TRACE_LOG
+    LOGGER_LEVEL_WRITERS(trace, Level::Trace)
+#   else
+    LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace)
+#   endif // ELPP_TRACE_LOG
+#   undef LOGGER_LEVEL_WRITERS
+#   undef LOGGER_LEVEL_WRITERS_DISABLED
+#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED
+#if ELPP_COMPILER_MSVC
+#   define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs
+#   define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__))
+#   define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\
+       10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#else
+#   if ELPP_COMPILER_CLANG
+#      define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#   else
+#      define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#   endif // ELPP_COMPILER_CLANG
+#endif // ELPP_COMPILER_MSVC
+#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \
+    writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \
+    writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \
+    if (ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion)) \
+        writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \
+    if (ELPP->validateAfterNCounter(__FILE__, __LINE__, n)) \
+        writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \
+    if (ELPP->validateNTimesCounter(__FILE__, __LINE__, n)) \
+        writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#undef ELPP_CURR_FILE_PERFORMANCE_LOGGER
+#if defined(ELPP_PERFORMANCE_LOGGER)
+#   define ELPP_CURR_FILE_PERFORMANCE_LOGGER ELPP_PERFORMANCE_LOGGER
+#else
+#   define ELPP_CURR_FILE_PERFORMANCE_LOGGER el::base::consts::kPerformanceLoggerId
+#endif
+class PerformanceTrackingData {
+public:
+    enum class DataType : base::type::EnumType {
+        Checkpoint = 1, Complete = 2
+    };
+    // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*)
+    explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), 
+        m_dataType(dataType), m_file(""), m_line(0), m_func("") {}
+    inline const std::string* blockName(void) const;
+    inline const struct timeval* startTime(void) const;
+    inline const struct timeval* endTime(void) const;
+    inline const struct timeval* lastCheckpointTime(void) const;
+    inline const base::PerformanceTracker* performanceTracker(void) const { return m_performanceTracker; }
+    inline PerformanceTrackingData::DataType dataType(void) const { return m_dataType; }
+    inline bool firstCheckpoint(void) const { return m_firstCheckpoint; }
+    inline std::string checkpointId(void) const { return m_checkpointId; }
+    inline const char* file(void) const { return m_file; }
+    inline unsigned long int line(void) const { return m_line; }
+    inline const char* func(void) const { return m_func; }
+    inline const base::type::string_t* formattedTimeTaken() const { return &m_formattedTimeTaken; }
+    inline const std::string& loggerId(void) const;
+private:
+    base::PerformanceTracker* m_performanceTracker;
+    base::type::string_t m_formattedTimeTaken;
+    PerformanceTrackingData::DataType m_dataType;
+    bool m_firstCheckpoint;
+    std::string m_checkpointId;
+    const char* m_file;
+    unsigned long int m_line;
+    const char* m_func;    
+    inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) {
+        m_performanceTracker = performanceTracker;
+        m_firstCheckpoint = firstCheckpoint;
+    }
+
+    friend class el::base::PerformanceTracker;
+};
+namespace base {
+/// @brief Represents performanceTracker block of code that conditionally adds performance status to log
+///        either when goes outside the scope of when checkpoint() is called
+class PerformanceTracker : public base::threading::ThreadSafe, public Loggable {
+public:
+    PerformanceTracker(const std::string& blockName,
+            base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond,
+            const std::string& loggerId = std::string(ELPP_CURR_FILE_PERFORMANCE_LOGGER), 
+            bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel) :
+        m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog),
+        m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) {
+#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+        // We store it locally so that if user happen to change configuration by the end of scope
+        // or before calling checkpoint, we still depend on state of configuraton at time of construction
+        el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false);
+        m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level);
+        if (m_enabled) {
+            base::utils::DateTime::gettimeofday(&m_startTime);
+        }
+#endif  // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+    }
+    /// @brief Copy constructor
+    PerformanceTracker(const PerformanceTracker& t) :
+        m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog),
+        m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled),
+        m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) {
+    }
+    virtual ~PerformanceTracker(void) {
+#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+        if (m_enabled) {
+            base::threading::ScopedLock scopedLock(lock());
+            if (m_scopedLog) {
+                base::utils::DateTime::gettimeofday(&m_endTime);
+                base::type::string_t formattedTime = getFormattedTimeTaken();
+                PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete);
+                data.init(this);
+                data.m_formattedTimeTaken = formattedTime;
+                PerformanceTrackingCallback* callback = nullptr;
+                for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
+                        : ELPP->m_performanceTrackingCallbacks) {
+                    callback = h.second.get();
+                    if (callback != nullptr && callback->enabled()) {
+                        callback->acquireLock();
+                        callback->handle(&data);
+                        callback->releaseLock();
+                    }
+                }
+            }
+        }
+#endif  // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING)
+    }
+    /// @brief A checkpoint for current performanceTracker block.
+    void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, unsigned long int line = __LINE__, const char* func = "") {
+#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+        if (m_enabled) {
+            base::threading::ScopedLock scopedLock(lock());
+            base::utils::DateTime::gettimeofday(&m_endTime);            
+            base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL("");
+            PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint);
+            data.init(this);
+            data.m_checkpointId = id;
+            data.m_file = file;
+            data.m_line = line;
+            data.m_func = func;
+            data.m_formattedTimeTaken = formattedTime;
+            PerformanceTrackingCallback* callback = nullptr;
+            for (const std::pair<std::string, base::type::PerformanceTrackingCallbackPtr>& h
+                    : ELPP->m_performanceTrackingCallbacks) {
+                callback = h.second.get();
+                if (callback != nullptr && callback->enabled()) {
+                    callback->acquireLock();
+                    callback->handle(&data);
+                    callback->releaseLock();
+                }
+            }
+            base::utils::DateTime::gettimeofday(&m_lastCheckpointTime);
+            m_hasChecked = true;
+            m_lastCheckpointId = id;
+        }
+#endif  // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED
+        ELPP_UNUSED(id);
+        ELPP_UNUSED(file);
+        ELPP_UNUSED(line);
+        ELPP_UNUSED(func);
+    }
+    inline Level level(void) const { return m_level; }
+private:
+    std::string m_blockName;
+    base::TimestampUnit m_timestampUnit;
+    std::string m_loggerId;
+    bool m_scopedLog;
+    Level m_level;
+    bool m_hasChecked;
+    std::string m_lastCheckpointId;
+    bool m_enabled;
+    struct timeval m_startTime, m_endTime, m_lastCheckpointTime;
+
+    PerformanceTracker(void);
+
+    friend class el::PerformanceTrackingData;
+    friend class base::DefaultPerformanceTrackingCallback;
+
+    const inline base::type::string_t getFormattedTimeTaken() const {
+        return getFormattedTimeTaken(m_startTime);
+    }
+    
+    const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const {
+        if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) {
+            base::type::stringstream_t ss;
+            ss << base::utils::DateTime::getTimeDifference(m_endTime,
+                startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast<base::type::EnumType>(m_timestampUnit)].unit;
+            return ss.str();
+        }
+        return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime,
+                startTime, m_timestampUnit), m_timestampUnit);
+    }
+
+    virtual inline void log(el::base::type::ostream_t& os) const {
+        os << getFormattedTimeTaken();
+    }
+};
+class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback {
+protected:
+    void handle(const PerformanceTrackingData* data) {
+        m_data = data;
+        base::type::stringstream_t ss;
+        if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) {
+            ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << *m_data->formattedTimeTaken() << ELPP_LITERAL("]");
+        } else {
+            ss << ELPP_LITERAL("Performance checkpoint");
+            if (!m_data->checkpointId().empty()) {
+                ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]");
+            }
+            ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << *m_data->performanceTracker();
+            if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) && m_data->performanceTracker()->m_hasChecked) {
+                ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from ");
+                if (m_data->performanceTracker()->m_lastCheckpointId.empty()) {
+                    ss << ELPP_LITERAL("last checkpoint");
+                } else {
+                    ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'");
+                }
+                ss << ELPP_LITERAL(")]");
+            } else {
+                ss << ELPP_LITERAL("]");
+            }
+        }
+        el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, m_data->loggerId().c_str()) << ss.str();
+    }
+private:
+    const PerformanceTrackingData* m_data;
+};
+}  // namespace base
+inline const std::string* PerformanceTrackingData::blockName() const {
+    return const_cast<const std::string*>(&m_performanceTracker->m_blockName);
+}
+inline const struct timeval* PerformanceTrackingData::startTime() const {
+    return const_cast<const struct timeval*>(&m_performanceTracker->m_startTime);
+}
+inline const struct timeval* PerformanceTrackingData::endTime() const {
+    return const_cast<const struct timeval*>(&m_performanceTracker->m_endTime);
+}
+inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const {
+    return const_cast<const struct timeval*>(&m_performanceTracker->m_lastCheckpointTime);
+}
+inline const std::string& PerformanceTrackingData::loggerId(void) const { return m_performanceTracker->m_loggerId; }
+namespace base {
+/// @brief Contains some internal debugging tools like crash handler and stack tracer
+namespace debug {
+class StackTrace : base::NoCopy {
+public:
+    static const std::size_t kMaxStack = 64;
+    static const std::size_t kStackStart = 2;  // We want to skip c'tor and StackTrace::generateNew()
+    class StackTraceEntry {
+    public:
+        StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr) {
+            m_index = index;
+            m_location = std::string(loc);
+            m_demangled = std::string(demang);
+            m_hex = std::string(hex);
+            m_addr = std::string(addr);
+        }
+        StackTraceEntry(std::size_t index, char* loc) {
+            m_index = index;
+            m_location = std::string(loc);
+        }
+        std::size_t m_index;
+        std::string m_location;
+        std::string m_demangled;
+        std::string m_hex;
+        std::string m_addr;
+        friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si) {
+           ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled
+                   << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr;
+           return ss;
+        }
+
+    private:
+        StackTraceEntry(void);
+    };
+
+    StackTrace(void) {
+        generateNew();
+    }
+
+    virtual ~StackTrace(void) {
+    }
+
+    inline std::vector<StackTraceEntry>& getLatestStack(void) {
+        return m_stack;
+    }
+
+    friend inline std::ostream& operator<<(std::ostream& os, const StackTrace& st) {
+       std::vector<StackTraceEntry>::const_iterator it = st.m_stack.begin();
+       while (it != st.m_stack.end()) {
+           os << "    " << *it++ << "\n";
+       }
+       return os;
+    }
+
+private:
+    std::vector<StackTraceEntry> m_stack;
+
+    void generateNew(void) {
+#if ELPP_STACKTRACE
+        m_stack.clear();
+        void* stack[kMaxStack];
+        std::size_t size = backtrace(stack, kMaxStack);
+        char** strings = backtrace_symbols(stack, size);
+        if (size > kStackStart) {  // Skip StackTrace c'tor and generateNew
+            for (std::size_t i = kStackStart; i < size; ++i) {
+                char* mangName = nullptr;
+                char* hex = nullptr;
+                char* addr = nullptr;
+                for (char* c = strings[i]; *c; ++c) {
+                    switch (*c) {
+                    case '(':
+                        mangName = c;
+                        break;
+                    case '+':
+                        hex = c;
+                        break;
+                    case ')':
+                        addr = c;
+                        break;
+                    }
+                }
+                // Perform demangling if parsed properly
+                if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) {
+                    *mangName++ = '\0';
+                    *hex++ = '\0';
+                    *addr++ = '\0';
+                    int status = 0;
+                    char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status);
+                    // if demangling is successful, output the demangled function name
+                    if (status == 0) {
+                        // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html)
+                        StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr);
+                        m_stack.push_back(entry);
+                    } else {
+                        // Not successful - we will use mangled name
+                        StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr);
+                        m_stack.push_back(entry);
+                    }
+                    free(demangName);
+                } else {
+                    StackTraceEntry entry(i - 1, strings[i]);
+                    m_stack.push_back(entry);
+                }
+            }
+        }
+        free(strings);
+#else
+        ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler");
+#endif  // ELPP_STACKTRACE
+    }
+};
+static std::string crashReason(int sig) {
+    std::stringstream ss;
+    bool foundReason = false;
+    for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) {
+        if (base::consts::kCrashSignals[i].numb == sig) {
+            ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal";
+            if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) {
+                ss << std::endl <<
+                      "    " << base::consts::kCrashSignals[i].brief << std::endl <<
+                      "    " << base::consts::kCrashSignals[i].detail;
+                }
+            foundReason = true;
+        }
+    }
+    if (!foundReason) {
+        ss << "Application has crashed due to unknown signal [" << sig << "]";
+    }
+    return ss.str();
+}
+/// @brief Logs reason of crash from sig
+static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) {
+    std::stringstream ss;
+    ss << "CRASH HANDLED; ";
+    ss << crashReason(sig);
+#if ELPP_STACKTRACE
+    if (stackTraceIfAvailable) {
+        ss << std::endl << "    ======= Backtrace: =========" << std::endl << base::debug::StackTrace();
+    }
+#else
+    ELPP_UNUSED(stackTraceIfAvailable);
+#endif  // ELPP_STACKTRACE
+    ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str();
+}
+static inline void crashAbort(int sig) {
+    base::utils::abort(sig);
+}
+/// @brief Default application crash handler
+///
+/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program.
+static inline void defaultCrashHandler(int sig) {
+    base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId);
+    base::debug::crashAbort(sig);
+}
+/// @brief Handles unexpected crashes
+class CrashHandler : base::NoCopy {
+public:
+    typedef void (*Handler)(int);
+
+    explicit CrashHandler(bool useDefault) {
+        if (useDefault) {
+            setHandler(defaultCrashHandler);
+        }
+    }
+    explicit CrashHandler(const Handler& cHandler) {
+        setHandler(cHandler);
+    }
+    void setHandler(const Handler& cHandler) {
+        m_handler = cHandler;
+#if defined(ELPP_HANDLE_SIGABRT)
+            int i = 0;  // SIGABRT is at base::consts::kCrashSignals[0]
+#else
+            int i = 1;
+#endif  // defined(ELPP_HANDLE_SIGABRT)
+        for (; i < base::consts::kCrashSignalsCount; ++i) {
+            m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler);
+        }
+    }
+
+private:
+    Handler m_handler;
+};
+}  // namespace debug
+}  // namespace base
+extern base::debug::CrashHandler elCrashHandler;
+#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \
+    el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance)
+/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor
+class SysLogInitializer {
+public:
+    SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) {
+#if defined(ELPP_SYSLOG)
+        openlog(processIdent, options, facility);
+#else
+        ELPP_UNUSED(processIdent);
+        ELPP_UNUSED(options);
+        ELPP_UNUSED(facility);
+#endif  // defined(ELPP_SYSLOG)
+    }
+    virtual ~SysLogInitializer(void) {
+#if defined(ELPP_SYSLOG)
+        closelog();
+#endif  // defined(ELPP_SYSLOG)
+    }
+};
+#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac)
+/// @brief Static helpers for developers
+class Helpers : base::StaticClass {
+public:
+    /// @brief Shares logging repository (base::Storage)
+    static inline void setStorage(base::type::StoragePointer storage) {
+        ELPP = storage;
+    }
+    /// @return Main storage repository
+    static inline base::type::StoragePointer storage() {
+        return ELPP;
+    }
+    /// @brief Sets application arguments and figures out whats active for logging and whats not.
+    static inline void setArgs(int argc, char** argv) {
+        ELPP->setApplicationArguments(argc, argv);
+    }
+    /// @copydoc setArgs(int argc, char** argv)
+    static inline void setArgs(int argc, const char** argv) {
+        ELPP->setApplicationArguments(argc, const_cast<char**>(argv));
+    }
+    /// @brief Overrides default crash handler and installs custom handler.
+    /// @param crashHandler A functor with no return type that takes single int argument.
+    ///        Handler is a typedef with specification: void (*Handler)(int)
+    static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) {
+        el::elCrashHandler.setHandler(crashHandler);
+    }
+    /// @brief Abort due to crash with signal in parameter
+    /// @param sig Crash signal
+    static inline void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0) {
+        std::stringstream ss;
+        ss << base::debug::crashReason(sig).c_str();
+        ss << " - [Called el::Helpers::crashAbort(" << sig << ")]";
+        if (sourceFile != nullptr && strlen(sourceFile) > 0) {
+            ss << " - Source: " << sourceFile;
+            if (line > 0)
+                ss << ":" << line;
+            else
+                ss << " (line number not specified)";
+        }
+        base::utils::abort(sig, ss.str());
+    }
+    /// @brief Logs reason of crash as per sig
+    /// @param sig Crash signal
+    /// @param stackTraceIfAvailable Includes stack trace if available
+    /// @param level Logging level
+    /// @param logger Logger to use for logging
+    static inline void logCrashReason(int sig, bool stackTraceIfAvailable = false,
+            Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId) {
+        el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger);
+    }
+    /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out
+    ///        (can be useful for backing up)
+    static inline void installPreRollOutCallback(const PreRollOutCallback& callback) {
+        ELPP->setPreRollOutCallback(callback);
+    }
+    /// @brief Uninstalls pre rollout callback
+    static inline void uninstallPreRollOutCallback(void) {
+        ELPP->unsetPreRollOutCallback();
+    }
+    /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched
+    template <typename T>
+    static inline bool installLogDispatchCallback(const std::string& id) {
+        return ELPP->installLogDispatchCallback<T>(id);
+    }
+    /// @brief Uninstalls log dispatch callback
+    template <typename T>
+    static inline void uninstallLogDispatchCallback(const std::string& id) {
+        ELPP->uninstallLogDispatchCallback<T>(id);
+    }
+    template <typename T>
+    static inline T* logDispatchCallback(const std::string& id) {
+        return ELPP->logDispatchCallback<T>(id);
+    }
+    /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished
+    template <typename T>
+    static inline bool installPerformanceTrackingCallback(const std::string& id) {
+        return ELPP->installPerformanceTrackingCallback<T>(id);
+    }
+    /// @brief Uninstalls post performance tracking handler
+    template <typename T>
+    static inline void uninstallPerformanceTrackingCallback(const std::string& id) {
+        ELPP->uninstallPerformanceTrackingCallback<T>(id);
+    }
+    template <typename T>
+    static inline T* performanceTrackingCallback(const std::string& id) {
+        return ELPP->performanceTrackingCallback<T>(id);
+    }
+    /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const
+    template <typename T>
+    static std::string convertTemplateToStdString(const T& templ) {
+        el::Logger* logger = 
+            ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId);
+        if (logger == nullptr) {
+            return std::string();
+        }
+        base::MessageBuilder b;
+        b.initialize(logger);
+        logger->acquireLock();
+        b << templ;
+#if defined(ELPP_UNICODE)
+        std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end());
+#else
+        std::string s = logger->stream().str();
+#endif  // defined(ELPP_UNICODE)
+        logger->stream().str(ELPP_LITERAL(""));
+        logger->releaseLock();
+        return s;
+    }
+    /// @brief Returns command line arguments (pointer) provided to easylogging++
+    static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) {
+        return ELPP->commandLineArgs();
+    }
+    /// @brief Installs user defined format specifier and handler
+    static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) {
+        ELPP->installCustomFormatSpecifier(customFormatSpecifier);
+    }
+    /// @brief Uninstalls user defined format specifier and handler
+    static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) {
+        return ELPP->uninstallCustomFormatSpecifier(formatSpecifier);
+    }
+    /// @brief Returns true if custom format specifier is installed
+    static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) {
+        return ELPP->hasCustomFormatSpecifier(formatSpecifier);
+    }
+    static inline void validateFileRolling(Logger* logger, Level level) {
+        if (logger == nullptr) return;
+        logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback());
+    }
+};
+/// @brief Static helpers to deal with loggers and their configurations
+class Loggers : base::StaticClass {
+public:
+    /// @brief Gets existing or registers new logger
+    static inline Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true) {
+        base::threading::ScopedLock scopedLock(ELPP->lock());
+        return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable);
+    }
+    /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister
+    ///        loggers initialized / used by third-party libs.
+    static inline bool unregisterLogger(const std::string& identity) {
+        base::threading::ScopedLock scopedLock(ELPP->lock());
+        return ELPP->registeredLoggers()->remove(identity);
+    }
+    /// @brief Whether or not logger with id is registered
+    static inline bool hasLogger(const std::string& identity) {
+        base::threading::ScopedLock scopedLock(ELPP->lock());
+        return ELPP->registeredLoggers()->has(identity);
+    }
+    /// @brief Reconfigures specified logger with new configurations
+    static inline Logger* reconfigureLogger(Logger* logger, const Configurations& configurations) {
+        if (!logger) return nullptr;
+        logger->configure(configurations);
+        return logger;
+    }
+    /// @brief Reconfigures logger with new configurations after looking it up using identity
+    static inline Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations) {
+        return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations);
+    }
+    /// @brief Reconfigures logger's single configuration
+    static inline Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType,
+            const std::string& value) {
+        Logger* logger = Loggers::getLogger(identity);
+        if (logger == nullptr) {
+            return nullptr;
+        }
+        logger->configurations()->set(Level::Global, configurationType, value);
+        logger->reconfigure();
+        return logger;
+    }
+    /// @brief Reconfigures all the existing loggers with new configurations
+    static inline void reconfigureAllLoggers(const Configurations& configurations) {
+        for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
+                it != ELPP->registeredLoggers()->end(); ++it) {
+            Loggers::reconfigureLogger(it->second, configurations);
+        }
+    }
+    /// @brief Reconfigures single configuration for all the loggers
+    static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) {
+        reconfigureAllLoggers(Level::Global, configurationType, value);
+    }
+    /// @brief Reconfigures single configuration for all the loggers for specified level
+    static inline void reconfigureAllLoggers(Level level, ConfigurationType configurationType, 
+            const std::string& value) {
+        for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin();
+                it != ELPP->registeredLoggers()->end(); ++it) {
+            Logger* logger = it->second;
+            logger->configurations()->set(level, configurationType, value);
+            logger->reconfigure();
+        }
+    }
+    /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers
+    static inline void setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers = false) {
+        ELPP->registeredLoggers()->setDefaultConfigurations(configurations);
+        if (reconfigureExistingLoggers) {
+            Loggers::reconfigureAllLoggers(configurations);
+        }
+    }
+    /// @brief Returns current default
+    static inline const Configurations* defaultConfigurations(void) {
+        return ELPP->registeredLoggers()->defaultConfigurations();
+    }
+    /// @brief Returns log stream reference pointer if needed by user
+    static inline const base::LogStreamsReferenceMap* logStreamsReference(void) {
+        return ELPP->registeredLoggers()->logStreamsReference();
+    }
+    /// @brief Default typed configuration based on existing defaultConf
+    static base::TypedConfigurations defaultTypedConfigurations(void) {
+        return base::TypedConfigurations(
+            ELPP->registeredLoggers()->defaultConfigurations(),
+            ELPP->registeredLoggers()->logStreamsReference());
+    }
+    /// @brief Populates all logger IDs in current repository.
+    /// @param [out] targetList List of fill up.
+    static inline std::vector<std::string>* populateAllLoggerIds(std::vector<std::string>* targetList) {
+        targetList->clear();
+        for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin();
+                it != ELPP->registeredLoggers()->list().end(); ++it) {
+            targetList->push_back(it->first);
+        }
+        return targetList;
+    }
+    /// @brief Sets configurations from global configuration file.
+    static void configureFromGlobal(const char* globalConfigurationFilePath) {
+        std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in);
+        ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath 
+            << "] for parsing.");
+        std::string line = std::string();
+        std::stringstream ss;
+        Logger* logger = nullptr;
+        auto configure = [&](void) {
+            ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() 
+                << "\n--------------");
+            Configurations c;
+            c.parseFromText(ss.str());
+            logger->configure(c);
+        };
+        while (gcfStream.good()) {
+           std::getline(gcfStream, line);
+           ELPP_INTERNAL_INFO(1, "Parsing line: " << line);
+           base::utils::Str::trim(line);
+           if (Configurations::Parser::isComment(line)) continue;
+           Configurations::Parser::ignoreComments(&line);
+           base::utils::Str::trim(line);
+           if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) {
+               if (!ss.str().empty() && logger != nullptr) {
+                   configure();
+               }
+               ss.str(std::string(""));
+               line = line.substr(2);
+               base::utils::Str::trim(line);
+               if (line.size() > 1) {
+                   ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'");
+                   logger = getLogger(line);
+               }
+            } else {
+               ss << line << "\n";
+            }
+        }
+        if (!ss.str().empty() && logger != nullptr) {
+            configure();
+        }
+    }
+    /// @brief Configures loggers using command line arg. Ensure you have already set command line args, 
+    /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger.
+    ///         If true is returned that does not mean it has been configured successfully, it only means that it
+    ///         has attempeted to configure logger using configuration file provided in argument
+    static inline bool configureFromArg(const char* argKey) {
+#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
+        ELPP_UNUSED(argKey);
+#else
+        if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) {
+            return false;
+        }
+        configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey));
+#endif  // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS)
+        return true;
+    }
+    /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered
+    static inline void flushAll(void) {
+        ELPP->registeredLoggers()->flushAll();
+    }
+    /// @brief Adds logging flag used internally.
+    static inline void addFlag(LoggingFlag flag) {
+        ELPP->addFlag(flag);
+    }
+    /// @brief Removes logging flag used internally.
+    static inline void removeFlag(LoggingFlag flag) {
+        ELPP->removeFlag(flag);
+    }
+    /// @brief Determines whether or not certain flag is active
+    static inline bool hasFlag(LoggingFlag flag) {
+        return ELPP->hasFlag(flag);
+    }
+    /// @brief Adds flag and removes it when scope goes out
+    class ScopedAddFlag {
+    public:
+        ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { Loggers::addFlag(m_flag); }
+        ~ScopedAddFlag(void) { Loggers::removeFlag(m_flag); }
+    private:
+        LoggingFlag m_flag;
+    };
+    /// @brief Removes flag and add it when scope goes out
+    class ScopedRemoveFlag {
+    public:
+        ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { Loggers::removeFlag(m_flag); }
+        ~ScopedRemoveFlag(void) { Loggers::addFlag(m_flag); }
+    private:
+        LoggingFlag m_flag;
+    };
+    /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging)
+    static inline void setLoggingLevel(Level level) {
+        ELPP->setLoggingLevel(level);
+    }
+    /// @brief Sets verbose level on the fly
+    static inline void setVerboseLevel(base::type::VerboseLevel level) {
+        ELPP->vRegistry()->setLevel(level);
+    }
+    /// @brief Gets current verbose level
+    static inline base::type::VerboseLevel verboseLevel(void) {
+        return ELPP->vRegistry()->level();
+    }
+	/// @brief Sets vmodules as specified (on the fly)
+    static inline void setVModules(const char* modules) {
+        if (ELPP->vRegistry()->vModulesEnabled()) {
+            ELPP->vRegistry()->setModules(modules);
+        }
+    }
+	/// @brief Clears vmodules
+    static inline void clearVModules(void) {
+        ELPP->vRegistry()->clearModules();
+    }
+};
+class VersionInfo : base::StaticClass {
+public:
+    /// @brief Current version number
+    static inline const std::string version(void) { return std::string("9.80"); }
+    /// @brief Release date of current version
+    static inline const std::string releaseDate(void) { return std::string("08-01-2015 0850hrs"); }
+};
+}  // namespace el
+#undef VLOG_IS_ON
+/// @brief Determines whether verbose logging is on for specified level current file.
+#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__))
+#undef TIMED_BLOCK
+#undef TIMED_SCOPE
+#undef TIMED_FUNC
+#undef ELPP_MIN_UNIT
+#if defined(ELPP_PERFORMANCE_MICROSECONDS)
+#   define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond
+#else
+#   define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond
+#endif  // (defined(ELPP_PERFORMANCE_MICROSECONDS))
+/// @brief Performance tracked scope. Performance gets written when goes out of scope using
+///        'performance' logger.
+///
+/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint();
+/// @see el::base::PerformanceTracker
+/// @see el::base::PerformanceTracker::checkpoint
+// Note: Do not surround this definition with null macro because of obj instance
+#define TIMED_SCOPE(obj, blockname) el::base::PerformanceTracker obj(blockname, ELPP_MIN_UNIT)
+#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::PerformanceTracker timer; } obj = { 0, \
+    el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i)
+/// @brief Performance tracked function. Performance gets written when goes out of scope using
+///        'performance' logger.
+///
+/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint();
+/// @see el::base::PerformanceTracker
+/// @see el::base::PerformanceTracker::checkpoint
+#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC)
+#undef PERFORMANCE_CHECKPOINT
+#undef PERFORMANCE_CHECKPOINT_WITH_ID
+#define PERFORMANCE_CHECKPOINT(obj) obj.checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC)
+#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj.checkpoint(id, __FILE__, __LINE__, ELPP_FUNC)
+#undef ELPP_COUNTER
+#undef ELPP_COUNTER_POS
+/// @brief Gets hit counter for file/line
+#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__))
+/// @brief Gets hit counter position for file/line, -1 if not registered yet
+#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts())
+// Undef levels to support LOG(LEVEL)
+#undef INFO
+#undef WARNING
+#undef DEBUG
+#undef ERROR
+#undef FATAL
+#undef TRACE
+#undef VERBOSE
+// Undef existing
+#undef CINFO
+#undef CWARNING
+#undef CDEBUG
+#undef CFATAL
+#undef CERROR
+#undef CTRACE
+#undef CVERBOSE
+#undef CINFO_IF
+#undef CWARNING_IF
+#undef CDEBUG_IF
+#undef CERROR_IF
+#undef CFATAL_IF
+#undef CTRACE_IF
+#undef CVERBOSE_IF
+#undef CINFO_EVERY_N
+#undef CWARNING_EVERY_N
+#undef CDEBUG_EVERY_N
+#undef CERROR_EVERY_N
+#undef CFATAL_EVERY_N
+#undef CTRACE_EVERY_N
+#undef CVERBOSE_EVERY_N
+#undef CINFO_AFTER_N
+#undef CWARNING_AFTER_N
+#undef CDEBUG_AFTER_N
+#undef CERROR_AFTER_N
+#undef CFATAL_AFTER_N
+#undef CTRACE_AFTER_N
+#undef CVERBOSE_AFTER_N
+#undef CINFO_N_TIMES
+#undef CWARNING_N_TIMES
+#undef CDEBUG_N_TIMES
+#undef CERROR_N_TIMES
+#undef CFATAL_N_TIMES
+#undef CTRACE_N_TIMES
+#undef CVERBOSE_N_TIMES
+// Normal logs
+#if ELPP_INFO_LOG
+#   define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+#   define CINFO(writer, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+#   define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+#   define CWARNING(writer, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+#   define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+#   define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+#   define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+#   define CERROR(writer, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+#   define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+#   define CFATAL(writer, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+#   define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+#   define CTRACE(writer, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+#   define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\
+       el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#else
+#   define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_VERBOSE_LOG
+// Conditional logs
+#if ELPP_INFO_LOG
+#   define CINFO_IF(writer, condition_, dispatchAction, ...) \
+        ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+#   define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+#   define CWARNING_IF(writer, condition_, dispatchAction, ...)\
+        ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+#   define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+#   define CDEBUG_IF(writer, condition_, dispatchAction, ...)\
+        ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+#   define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+#   define CERROR_IF(writer, condition_, dispatchAction, ...)\
+        ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+#   define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+#   define CFATAL_IF(writer, condition_, dispatchAction, ...)\
+        ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+#   define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+#   define CTRACE_IF(writer, condition_, dispatchAction, ...)\
+        ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+#   define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+#   define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \
+       el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__)
+#else
+#   define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_VERBOSE_LOG
+// Occasional logs
+#if ELPP_INFO_LOG
+#   define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\
+        ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+#   define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+#   define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\
+        ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+#   define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+#   define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\
+        ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+#   define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+#   define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\
+        ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+#   define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+#   define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\
+        ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+#   define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+#   define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\
+        ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+#   define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+#   define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\
+        CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__)
+#else
+#   define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_VERBOSE_LOG
+// After N logs
+#if ELPP_INFO_LOG
+#   define CINFO_AFTER_N(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+#   define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+#   define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+#   define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+#   define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+#   define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+#   define CERROR_AFTER_N(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+#   define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+#   define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+#   define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+#   define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+#   define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+#   define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\
+        CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__)
+#else
+#   define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_VERBOSE_LOG
+// N Times logs
+#if ELPP_INFO_LOG
+#   define CINFO_N_TIMES(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__)
+#else
+#   define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_INFO_LOG
+#if ELPP_WARNING_LOG
+#   define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__)
+#else
+#   define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_WARNING_LOG
+#if ELPP_DEBUG_LOG
+#   define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__)
+#else
+#   define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_DEBUG_LOG
+#if ELPP_ERROR_LOG
+#   define CERROR_N_TIMES(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__)
+#else
+#   define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_ERROR_LOG
+#if ELPP_FATAL_LOG
+#   define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__)
+#else
+#   define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_FATAL_LOG
+#if ELPP_TRACE_LOG
+#   define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\
+        ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__)
+#else
+#   define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_TRACE_LOG
+#if ELPP_VERBOSE_LOG
+#   define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\
+        CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__)
+#else
+#   define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter()
+#endif  // ELPP_VERBOSE_LOG
+//
+// Custom Loggers - Requires (level, dispatchAction, loggerId/s)
+//
+// undef existing
+#undef CLOG
+#undef CLOG_VERBOSE
+#undef CVLOG
+#undef CLOG_IF
+#undef CLOG_VERBOSE_IF
+#undef CVLOG_IF
+#undef CLOG_EVERY_N
+#undef CVLOG_EVERY_N
+#undef CLOG_AFTER_N
+#undef CVLOG_AFTER_N
+#undef CLOG_N_TIMES
+#undef CVLOG_N_TIMES
+// Normal logs
+#define CLOG(LEVEL, ...)\
+    C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+// Conditional logs
+#define CLOG_IF(condition, LEVEL, ...)\
+    C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_IF(condition, vlevel, ...)\
+    CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+// Hit counts based logs
+#define CLOG_EVERY_N(n, LEVEL, ...)\
+    C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_EVERY_N(n, vlevel, ...)\
+    CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CLOG_AFTER_N(n, LEVEL, ...)\
+    C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_AFTER_N(n, vlevel, ...)\
+    CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CLOG_N_TIMES(n, LEVEL, ...)\
+    C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CVLOG_N_TIMES(n, vlevel, ...)\
+    CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+//
+// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros
+//
+// undef existing
+#undef LOG
+#undef VLOG
+#undef LOG_IF
+#undef VLOG_IF
+#undef LOG_EVERY_N
+#undef VLOG_EVERY_N
+#undef LOG_AFTER_N
+#undef VLOG_AFTER_N
+#undef LOG_N_TIMES
+#undef VLOG_N_TIMES
+#undef ELPP_CURR_FILE_LOGGER_ID
+#if defined(ELPP_DEFAULT_LOGGER)
+#   define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER
+#else
+#   define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId
+#endif
+#undef ELPP_TRACE
+#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID)
+// Normal logs
+#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Conditional logs
+#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Hit counts based logs
+#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Generic PLOG()
+#undef CPLOG
+#undef CPLOG_IF
+#undef PLOG
+#undef PLOG_IF
+#undef DCPLOG
+#undef DCPLOG_IF
+#undef DPLOG
+#undef DPLOG_IF
+#define CPLOG(LEVEL, ...)\
+    C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define CPLOG_IF(condition, LEVEL, ...)\
+    C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define DCPLOG(LEVEL, ...)\
+    if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define DCPLOG_IF(condition, LEVEL, ...)\
+    C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__)
+#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+// Generic SYSLOG()
+#undef CSYSLOG
+#undef CSYSLOG_IF
+#undef CSYSLOG_EVERY_N
+#undef CSYSLOG_AFTER_N
+#undef CSYSLOG_N_TIMES
+#undef SYSLOG
+#undef SYSLOG_IF
+#undef SYSLOG_EVERY_N
+#undef SYSLOG_AFTER_N
+#undef SYSLOG_N_TIMES
+#undef DCSYSLOG
+#undef DCSYSLOG_IF
+#undef DCSYSLOG_EVERY_N
+#undef DCSYSLOG_AFTER_N
+#undef DCSYSLOG_N_TIMES
+#undef DSYSLOG
+#undef DSYSLOG_IF
+#undef DSYSLOG_EVERY_N
+#undef DSYSLOG_AFTER_N
+#undef DSYSLOG_N_TIMES
+#if defined(ELPP_SYSLOG)
+#   define CSYSLOG(LEVEL, ...)\
+        C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define CSYSLOG_IF(condition, LEVEL, ...)\
+        C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId)
+#   define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId)
+#   define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId)
+#   define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId)
+#   define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId)
+#   define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define DCSYSLOG_IF(condition, LEVEL, ...)\
+        C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define DCSYSLOG_EVERY_N(n, LEVEL, ...)\
+        if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define DCSYSLOG_AFTER_N(n, LEVEL, ...)\
+        if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define DCSYSLOG_N_TIMES(n, LEVEL, ...)\
+        if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__)
+#   define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId)
+#   define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId)
+#   define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId)
+#   define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId)
+#   define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId)
+#else
+#   define CSYSLOG(LEVEL, ...) el::base::NullWriter()
+#   define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter()
+#   define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter()
+#   define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter()
+#   define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter()
+#   define SYSLOG(LEVEL) el::base::NullWriter()
+#   define SYSLOG_IF(condition, LEVEL) el::base::NullWriter()
+#   define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter()
+#   define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter()
+#   define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter()
+#   define DCSYSLOG(LEVEL, ...) el::base::NullWriter()
+#   define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter()
+#   define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter()
+#   define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter()
+#   define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter()
+#   define DSYSLOG(LEVEL) el::base::NullWriter()
+#   define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter()
+#   define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter()
+#   define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter()
+#   define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter()
+#endif  // defined(ELPP_SYSLOG)
+//
+// Custom Debug Only Loggers - Requires (level, loggerId/s)
+//
+// undef existing
+#undef DCLOG
+#undef DCVLOG
+#undef DCLOG_IF
+#undef DCVLOG_IF
+#undef DCLOG_EVERY_N
+#undef DCVLOG_EVERY_N
+#undef DCLOG_AFTER_N
+#undef DCVLOG_AFTER_N
+#undef DCLOG_N_TIMES
+#undef DCVLOG_N_TIMES
+// Normal logs
+#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__)
+#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__)
+#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__)
+// Conditional logs
+#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__)
+#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__)
+// Hit counts based logs
+#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__)
+#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__)
+#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__)
+#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__)
+#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__)
+#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__)
+//
+// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros
+//
+// undef existing
+#undef DLOG
+#undef DVLOG
+#undef DLOG_IF
+#undef DVLOG_IF
+#undef DLOG_EVERY_N
+#undef DVLOG_EVERY_N
+#undef DLOG_AFTER_N
+#undef DVLOG_AFTER_N
+#undef DLOG_N_TIMES
+#undef DVLOG_N_TIMES
+// Normal logs
+#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Conditional logs
+#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Hit counts based logs
+#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID)
+#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID)
+// Check macros
+#undef CCHECK
+#undef CPCHECK
+#undef CCHECK_EQ
+#undef CCHECK_NE
+#undef CCHECK_LT
+#undef CCHECK_GT
+#undef CCHECK_LE
+#undef CCHECK_GE
+#undef CCHECK_BOUNDS
+#undef CCHECK_NOTNULL
+#undef CCHECK_STRCASEEQ
+#undef CCHECK_STRCASENE
+#undef CHECK
+#undef PCHECK
+#undef CHECK_EQ
+#undef CHECK_NE
+#undef CHECK_LT
+#undef CHECK_GT
+#undef CHECK_LE
+#undef CHECK_GE
+#undef CHECK_BOUNDS
+#undef CHECK_NOTNULL
+#undef CHECK_STRCASEEQ
+#undef CHECK_STRCASENE
+#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] "
+#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] "
+#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__)
+#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__)
+#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__)
+#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__)
+#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__)
+#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__)
+#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__)
+#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID)
+namespace el {
+namespace base {
+namespace utils {
+template <typename T>
+static T* checkNotNull(T* ptr, const char* name, const char* loggers, ...) {
+    CLOG_IF(ptr == nullptr, FATAL, loggers) << "Check failed: [" << name << " != nullptr]";
+    return ptr;
+}
+}  // namespace utils
+}  // namespace base
+}  // namespace el
+#define CCHECK_NOTNULL(ptr, ...) el::base::utils::checkNotNull(ptr, #ptr, __VA_ARGS__)
+#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \
+                        << "Check failed: [" << #str1 << " == " << #str2 << "] "
+#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \
+                        << "Check failed: [" << #str1 << " != " << #str2 << "] "
+#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \
+                        << "Check failed: [" << #str1 << " == " << #str2 << "] "
+#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \
+                        << "Check failed: [" << #str1 << " != " << #str2 << "] "
+#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#undef DCCHECK
+#undef DCCHECK_EQ
+#undef DCCHECK_NE
+#undef DCCHECK_LT
+#undef DCCHECK_GT
+#undef DCCHECK_LE
+#undef DCCHECK_GE
+#undef DCCHECK_BOUNDS
+#undef DCCHECK_NOTNULL
+#undef DCCHECK_STRCASEEQ
+#undef DCCHECK_STRCASENE
+#undef DCPCHECK
+#undef DCHECK
+#undef DCHECK_EQ
+#undef DCHECK_NE
+#undef DCHECK_LT
+#undef DCHECK_GT
+#undef DCHECK_LE
+#undef DCHECK_GE
+#undef DCHECK_BOUNDS_
+#undef DCHECK_NOTNULL
+#undef DCHECK_STRCASEEQ
+#undef DCHECK_STRCASENE
+#undef DPCHECK
+#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__)
+#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__)
+#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__)
+#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__)
+#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__)
+#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__)
+#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__)
+#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__)
+#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL(ptr, __VA_ARGS__)
+#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__)
+#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__)
+#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__)
+#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__)
+#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__)
+#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID)
+#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID)
+#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING)
+#   define ELPP_USE_DEF_CRASH_HANDLER false
+#else
+#   define ELPP_USE_DEF_CRASH_HANDLER true
+#endif  // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING)
+#define ELPP_CRASH_HANDLER_INIT
+#define ELPP_INIT_EASYLOGGINGPP(val)\
+    ELPP_INITI_BASIC_DECLR\
+    namespace el {\
+        namespace base {\
+            el::base::type::StoragePointer elStorage(val);\
+        }\
+        el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\
+    }
+
+#if ELPP_ASYNC_LOGGING
+#   define INITIALIZE_EASYLOGGINGPP\
+       ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\
+                                                          new el::base::AsyncDispatchWorker()))\
+       
+#else
+#   define INITIALIZE_EASYLOGGINGPP\
+       ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder())))
+#endif  // ELPP_ASYNC_LOGGING
+#define INITIALIZE_NULL_EASYLOGGINGPP\
+    ELPP_INITI_BASIC_DECLR\
+    namespace el {\
+        namespace base {\
+            el::base::type::StoragePointer elStorage;\
+        }\
+        el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\
+    }
+// NOTE: no ELPP_INITI_BASIC_DECLR when sharing - causes double free corruption on external symbols
+#define SHARE_EASYLOGGINGPP(initializedStorage)\
+    namespace el {\
+        namespace base {\
+            el::base::type::StoragePointer elStorage(initializedStorage);\
+        }\
+        el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\
+    }
+
+#if defined(ELPP_UNICODE)
+#   define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale(""))
+#else
+#   define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv)
+#endif  // defined(ELPP_UNICODE)
+#endif // EASYLOGGINGPP_H
diff --git a/src/filterm4.py b/src/filterm4.py
new file mode 100755
index 0000000..eb5d9ac
--- /dev/null
+++ b/src/filterm4.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+
+# Filters for unique, highest scoring subread query/target pairs from an m4
+# file. Helps get rid of chimeras, at the cost of some yield.
+
+import sys
+from collections import namedtuple
+
+M4Record = namedtuple('M4Record', ('qname tname score pctsimilarity qstrand '
+                                   'qstart qend qseqlength tstrand tstart '
+                                   'tend tseqlength mapqv'))
+
+
+class Count(object):
+    """Tracks record count for original and filtered"""
+    def __init__(self):
+        self.orig = 0
+        self.filt = 0
+
+    def __repr__(self):
+        return "Record count: original=%i, filtered=%i\n" % \
+            (self.orig, self.filt)
+
+
+def printUniq(qgroup, count):
+    top = dict()
+    for q in qgroup:
+        m = M4Record._make(q.split())
+        k = "%s%s" % (m.qname, m.tname)
+        if k in top:
+            n = M4Record._make(top[k].split())
+            if int(m.score) < int(n.score):
+                top[k] = q
+        else:
+            top[k] = q
+
+    for r in top.values():
+        count.filt += 1
+        print r,
+
+    qgroup[:] = []
+
+
+def main():
+    m4file = sys.argv[1]
+    m4Hndl = open(m4file)
+    qgroup = []
+    curr = ''
+    count = Count()
+    for rec in m4Hndl:
+        count.orig += 1
+        m = M4Record._make(rec.split())
+        if curr != m.qname:
+            printUniq(qgroup, count)
+            qgroup.append(rec)
+            curr = m.qname
+        else:
+            qgroup.append(rec)
+
+    printUniq(qgroup, count)
+    sys.stderr.write(str(count))
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/src/m4topre.py b/src/m4topre.py
new file mode 100755
index 0000000..ad0f983
--- /dev/null
+++ b/src/m4topre.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+"""Super-simple converter from blasr m4 alignments to pbdagcon 'pre'
+alignments. For use in the pre-assembler dagcon workflow.
+"""
+
+import sys
+import heapq
+import string # pylint: disable=W0402
+from itertools import ifilter
+from collections import namedtuple, defaultdict
+import numpy as np
+from pbcore.io.FastaIO import FastaReader
+
+# qname tname score pctsimilarity qstrand qstart qend qseqlength tstrand tstart
+# ... tend tseqlength mapqv
+#
+# store only fields we need
+__m4fields__ = [0, 1, 2, 5, 6, 8, 9, 10, 11]
+M4RECORD = namedtuple(
+    'M4RECORD', 'qname tname score qstart qend tstrand tstart tend tseqlength')
+
+__tuplfy__ = M4RECORD._make # pylint: disable=W0212
+
+# dna compliment
+__rc__ = string.maketrans('actgACTG', 'tgacTGAC')
+
+
+def parse_m4(rec):
+    """Parse in the m4 file, returning a list of records"""
+    return [y for (x, y) in enumerate(rec.split()) if x in __m4fields__]
+
+
+def rating(rec):
+    """Rates the alignment for by length and score (revisit at some point)"""
+    score = -int(rec.score)
+    alen = int(rec.tend) - int(rec.tstart)
+    return score + alen
+
+
+def schwartzian(rec):
+    """Provides a schwartzian transform for the given record, used for 
+    sorting
+    """
+    flds = rec.split()
+    return (flds[1], float(flds[2]), rec)
+
+
+def sort_targ_score(recs):
+    """Sorts the list in place by target id (string), then score (float)"""
+    recs[:] = [schwartzian(x) for x in recs]
+    recs.sort()
+    recs[:] = [rec for (target, score, rec) in recs] # pylint: disable=W0612
+
+
+def rescore(recs):
+    """Rescore alignments using coverage based statistics"""
+    prev = ""
+    cov = np.zeros(1)
+    for idx, rec in enumerate(recs):
+        fields = rec.split()
+        rec = __tuplfy__(fields)
+        if rec.tname != prev:
+            prev = rec.tname
+            cov = np.zeros(int(rec.tseqlength), dtype=np.float16)
+
+        if rec.tstrand:
+            start = int(rec.tseqlength) - int(rec.tend)
+            end = int(rec.tseqlength) - int(rec.tstart)
+        else:
+            start = int(rec.tstart)
+            end = int(rec.tend)
+
+        cov[start:end] += 1
+        score = np.sum(1/cov[start:end])
+        fields[2] = str(-score)
+        recs[idx] = " ".join(fields)
+
+
+def bestn_true(recstr, myq):
+    """Checks if the record falls inside bestn (used when blasr is chunked)"""
+    rec = __tuplfy__(recstr.split())
+    rate = rating(rec)
+    return rate in myq[rec.qname[32:]].top
+
+
+class AlnLimiter(object): # pylint: disable=R0903
+    """Functor that returns alignments until some count is reached. Alignments
+    should be sorted. 
+    """
+    def __init__(self, limit=76):
+        self.count = 0
+        self.target = ''
+        self.limit = limit
+
+    def __call__(self, rec):
+        target = rec.split()[1]
+        if target != self.target:
+            self.count = 0
+            self.target = target
+        self.count += 1
+        return self.count < self.limit
+
+
+class TopAlignments(object): # pylint: disable=R0903
+    """Tracks the top alignments for a given query, used for bestn calc"""
+    bestn = 10
+
+    def __init__(self):
+        self.top = [0] * TopAlignments.bestn
+
+    def __call__(self):
+        return  # noop
+
+    def add(self, aln):
+        """Adds an alignment to a bounded list, kicking out another if 
+        necessary
+        """
+        heapq.heappushpop(self.top, aln)
+
+
+def main(): # pylint: disable=R0914
+    """Drives the program"""
+    mym4 = sys.argv[1]
+    allm4 = sys.argv[2]
+    reads = sys.argv[3]
+    TopAlignments.bestn = int(sys.argv[4])
+
+    # tracks bestn
+    my_queries = defaultdict(TopAlignments)
+    my_m4recs = []
+
+    # load my m4 chunk
+    m4h = open(mym4)
+    rec_add = my_m4recs.append
+    for line in m4h:
+        flds = parse_m4(line)
+        rec = __tuplfy__(flds)
+        rate = rating(rec)
+        my_queries[rec.qname[32:]].add(rate)
+        rec_add(' '.join(flds))
+
+    m4h.close()
+
+    # if we're chunked locate relevant alignments
+    if mym4 != allm4:
+        # assuming fofn here
+        m4files = [x.rstrip() for x in open(allm4) if x.rstrip() != mym4]
+        for m4f in m4files:
+            m4h = open(m4f)
+            for recstr in m4h:
+                rec = __tuplfy__(parse_m4(recstr))
+                if rec.qname[32:] in my_queries:
+                    rate = rating(rec)
+                    my_queries[rec.qname[32:]].add(rate)
+            m4h.close()
+
+        # remove alignments that fall outside of bestn
+        my_m4recs[:] = [x for x in my_m4recs if bestn_true(x, my_queries)]
+
+    # sort by target name/score
+    sort_targ_score(my_m4recs)
+
+    # rescore based on coverage
+    rescore(my_m4recs)
+
+    # sort one more time be new score
+    sort_targ_score(my_m4recs)
+
+    # take a max number of alignments for each target
+    limiter = AlnLimiter()
+    my_m4recs[:] = [x for x in ifilter(limiter, my_m4recs)]
+
+    # load only related sequences
+    seqs = {}
+    frea = FastaReader(reads)
+    for fent in frea:
+        if fent.name[32:] in my_queries:
+            seqs[fent.name] = fent.sequence
+
+    # may or may not help
+    del my_queries
+
+    # generate pre-alignments
+    for recstr in my_m4recs:
+        rec = __tuplfy__(recstr.split())
+
+        # Bug 24538, rare case missing self hit
+        if rec.tname not in seqs:
+            msg = "Warning: skipping query %s target %s\n"
+            sys.stderr.write(msg % (rec.qname, rec.tname))
+            continue
+
+        qst = int(rec.qstart)
+        qnd = int(rec.qend)
+        qseq = seqs[rec.qname][qst:qnd]
+        strand = '-' if rec.tstrand == '1' else '+'
+        tst = int(rec.tstart)
+        tnd = int(rec.tend)
+        if strand == '+':
+            tseq = seqs[rec.tname][tst:tnd]
+        else:
+            tseq = seqs[rec.tname].translate(__rc__)[::-1][tst:tnd]
+
+        print ' '.join([rec.qname, rec.tname, strand,
+                       rec.tseqlength, str(tst), str(tnd), qseq, tseq])
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/src/q-sense.py b/src/q-sense.py
new file mode 100755
index 0000000..72e51f1
--- /dev/null
+++ b/src/q-sense.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python
+
+#################################################################################$$
+# Copyright (c) 2011-2013, Pacific Biosciences of California, Inc.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted (subject to the limitations in the
+# disclaimer below) provided that the following conditions are met:
+#
+#  * Redistributions of source code must retain the above copyright
+#  notice, this list of conditions and the following disclaimer.
+#
+#  * Redistributions in binary form must reproduce the above
+#  copyright notice, this list of conditions and the following
+#  disclaimer in the documentation and/or other materials provided
+#  with the distribution.
+#
+#  * Neither the name of Pacific Biosciences nor the names of its
+#  contributors may be used to endorse or promote products derived
+#  from this software without specific prior written permission.
+#
+# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
+# GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY PACIFIC
+# BIOSCIENCES AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL PACIFIC BIOSCIENCES OR ITS
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#################################################################################$$
+
+
+import sys
+import os
+import logging
+import pkg_resources
+
+from pbcore.util.ToolRunner import PBMultiToolRunner
+from pbcore.io import FastaReader
+from pbtools.pbdagcon.q_sense import *
+
+try:
+    __p4revision__ = "$Revision: #15 $"
+    __p4change__ = "$Change: 115421 $"
+    revNum = int(__p4revision__.strip("$").split(" ")[1].strip("#"))
+    changeNum = int(__p4change__.strip("$").split(":")[-1])
+    __version__ = "%s-r%d-c%d" % ( pkg_resources.require("pbtools.pbdagcon")[0].version, revNum, changeNum )
+except:
+    __version__ = "pbtools.pbdagcon-github"
+
+
+class Consensus(PBMultiToolRunner):
+
+    def __init__(self):
+        desc = ["Making consensus sequence from a group reads storing in a fasta file. "
+                "All of the reads are expected to cover most of the target templates that gets sequenced. "
+                "If the reads have broad read length distribution and not all of them cover the same region of "
+                "a template, this code won't generate correct result. "
+                "This code is designed for getting consensus up to the length of reads (~10k). "
+                "It is not optimized for getting consensus for larger templates."]
+        super(Consensus, self).__init__('\n'.join(desc))
+
+        subparsers = self.subParsers
+
+        desc = ['using self-self alignment for getting the seed sequence for constructing consensus']
+        parser_d = subparsers.add_parser('d', help = "generate consensus using the alignments between the input sequneces to find seed sequence",
+                                         description = "\n".join(desc))
+        parser_d.add_argument('input', metavar = 'input.fasta',
+                              help = 'an input fasta file')
+        
+        desc = ['using a reference file as seed for consensus']
+        parser_r = subparsers.add_parser('r', help = "using a reference fasta as the seed sequence",
+                                         description = "\n".join(desc))
+        parser_r.add_argument('input', metavar = 'input.fasta',
+                              help = 'an input fasta file')
+        parser_r.add_argument('ref', metavar = 'ref.fasta',
+                              help = 'a reference fasta file')
+
+        for subp in (parser_r, parser_d): 
+            subp.add_argument('-o', '--output', metavar = 'file-name', dest = 'out_file_name', default = "g_consensus", 
+                               help = 'consensus output filename')
+            subp.add_argument('-d', '--output_dir', metavar = 'directory-name', dest = 'out_dir_name', default = "./", 
+                               help = 'consensus output working directory')
+            subp.add_argument('--cname', metavar = 'consensus-seq-name', dest = 'consensus_seq_name', default = "consensus", 
+                               help = 'consensus sequence name')
+            subp.add_argument('--enable_hp_correction', action='store_true', default = False, dest="enable_hp_corr",
+                               help = 'enable aggressive homopolymer missing errot detection and correction')
+            subp.add_argument('--mark_lower_case', action='store_true', default = False, dest="mark_lower_case",
+                               help = 'mark low quality consensus base with lower case letter')
+            subp.add_argument('--hp_correction_th', dest = 'entropy_th', default = 0.65,
+                               help = 'homopolymer missing correction entropy threshold')
+            subp.add_argument('--n_iter', default = 4, dest = 'niter',
+                              help = 'number of iteration of consensus correction')
+            subp.add_argument('--min_cov', default = 8, dest = 'min_cov',
+                              help = 'minimum coverage for generate consensus')
+            subp.add_argument('--max_cov', default = 60, dest = 'max_cov',
+                              help = 'maximum coverage for generate consensus')
+            subp.add_argument('--max_n_reads', default = 150, dest = 'max_num_reads',
+                              help = 'the maximum number of reads used for consensus')
+            subp.add_argument('--nproc', default = 4, dest = 'nproc',
+                              help = 'the number cpu core used by blasr, default to 4 cores')
+            subp.add_argument('--dump_dag_info', action='store_true', default = False, dest="dump_dag_info",
+                               help = 'dump the information of the dag, including a pileup view of the alignments')
+                    
+    def getVersion(self):
+        return __version__
+    
+    def denovoConsensus(self):
+        prefix = self.args.out_file_name.split(".")
+        input_fasta_name = self.args.input 
+
+        rid,s =best_template_by_blasr(input_fasta_name)
+        if len(prefix) > 1:
+            prefix = ".".join(prefix[:-1])
+        else:
+            prefix = ".".join(prefix)
+        full_prefix = os.path.join(self.args.out_dir_name, prefix)
+        with open("%s_ref.fa" % full_prefix, "w") as f:
+            print >>f ,">%s_ref" % self.args.consensus_seq_name
+            print >>f, s
+        hp_corr = True if self.args.enable_hp_corr else False
+        mark_lower_case = True if self.args.mark_lower_case else False
+        generate_consensus(input_fasta_name, "%s_ref.fa" % full_prefix, full_prefix, self.args.consensus_seq_name, 
+                           hp_corr, int(self.args.niter), 
+                           int(self.args.max_num_reads), 
+                           float(self.args.entropy_th),
+                           self.args.dump_dag_info,
+                           int(self.args.min_cov),
+                           int(self.args.max_cov),
+                           mark_lower_case,
+                           int(self.args.nproc))
+
+    def refConsensus(self):
+        input_fasta_name = self.args.input 
+        prefix = self.args.out_file_name.split(".")
+        if len(prefix) > 1:
+            prefix = ".".join(prefix[:-1])
+        else:
+            prefix = ".".join(prefix)
+        full_prefix = os.path.join(self.args.out_dir_name, prefix)
+        hp_corr = True if self.args.enable_hp_corr else False
+        mark_lower_case = True if self.args.mark_lower_case else False
+        generate_consensus(input_fasta_name, self.args.ref, full_prefix, self.args.consensus_seq_name,
+                           hp_corr, int(self.args.niter), 
+                           int(self.args.max_num_reads), 
+                           float(self.args.entropy_th),
+                           self.args.dump_dag_info,
+                           int(self.args.min_cov),
+                           int(self.args.max_cov),
+                           mark_lower_case,
+                           int(self.args.nproc))
+
+    def run(self):
+        logging.debug("Arguments" + str(self.args))
+        if self.args.subCommand == 'd':
+            self.denovoConsensus()
+        elif self.args.subCommand == 'r':
+            self.refConsensus()
+
+if __name__ == '__main__':    
+    sys.exit(Consensus().start())
+
diff --git a/src/tests/test_aligngraph.py b/src/tests/test_aligngraph.py
new file mode 100755
index 0000000..dfa8e89
--- /dev/null
+++ b/src/tests/test_aligngraph.py
@@ -0,0 +1,204 @@
+from nose.tools import assert_equal
+from nose import SkipTest
+import random
+from pbtools.pbdagcon.aligngraph import *
+
+def generate_simulated_reads(pi=None, pd=None, n = 4):
+    import random
+    random.seed(42)
+    seq   = "ATATTTGGC"
+    seq1  = "ATAGCCGGC"
+    seq2  = "ATACCCGGC"
+    seq3  = "ATATCCGGC"
+    seq4  = "ATATCGGC"
+    if pi == None:
+        pi = 0.03
+    if pd == None:
+        pd = 0.03
+    out_seq = []
+    for i in range(n):
+        c = 0
+        s = []
+        if i % 4 == 0:
+            ss = seq1
+        elif i % 4 == 1:
+            ss = seq2
+        elif i % 4 == 2:
+            ss = seq3
+        else:
+            ss = seq4
+        while 1:
+            if random.uniform(0,1) < pi:
+                s.append(random.choice( ("A","G","C","T") ) )
+                continue
+            if random.uniform(0,1) < pd:
+                c += 1
+                continue
+            if c < len(ss):
+                s.append(ss[c])
+                c += 1
+            else:
+                break
+        out_seq.append( "".join(s) )
+    return seq, out_seq
+
+class TestPhiCoeff:
+    def test_phi_coeff(self):
+        # assert_equal(expected, phi_coeff(xvec, yvec))
+        raise SkipTest # TODO: implement your test here
+
+class TestConvertMismatches:
+    def test_convert_mismatches(self):
+        assert_equal( ('C-AC', 'CG-C'), convert_mismatches("CAC","CGC") ) 
+        assert_equal( ('CAACAT', 'CAA--T'), convert_mismatches("CAACAT","C-A-AT" ) )
+        assert_equal( ('CCG--T', 'CCGACT'), convert_mismatches("-C--CGT","CCGAC-T") ) 
+
+class TestAlnEdge:
+    def test___init__(self):
+        # aln_edge = AlnEdge(in_node, out_node)
+        raise SkipTest # TODO: implement your test here
+
+    def test___repr__(self):
+        # aln_edge = AlnEdge(in_node, out_node)
+        # assert_equal(expected, aln_edge.__repr__())
+        raise SkipTest # TODO: implement your test here
+
+    def test_add_to_score(self):
+        # aln_edge = AlnEdge(in_node, out_node)
+        # assert_equal(expected, aln_edge.add_to_score(s))
+        raise SkipTest # TODO: implement your test here
+
+    def test_increase_count(self):
+        # aln_edge = AlnEdge(in_node, out_node)
+        # assert_equal(expected, aln_edge.increase_count())
+        raise SkipTest # TODO: implement your test here
+
+    def test_set_score(self):
+        # aln_edge = AlnEdge(in_node, out_node)
+        # assert_equal(expected, aln_edge.set_score(s))
+        raise SkipTest # TODO: implement your test here
+
+class TestAlnNode:
+    def test___init__(self):
+        # aln_node = AlnNode(base)
+        raise SkipTest # TODO: implement your test here
+
+    def test___repr__(self):
+        # aln_node = AlnNode(base)
+        # assert_equal(expected, aln_node.__repr__())
+        raise SkipTest # TODO: implement your test here
+
+    def test_add_in_edge(self):
+        # aln_node = AlnNode(base)
+        # assert_equal(expected, aln_node.add_in_edge(in_edge))
+        raise SkipTest # TODO: implement your test here
+
+    def test_addout_edge(self):
+        # aln_node = AlnNode(base)
+        # assert_equal(expected, aln_node.addout_edge(out_edge))
+        raise SkipTest # TODO: implement your test here
+
+    def test_increase_weight(self):
+        # aln_node = AlnNode(base)
+        # assert_equal(expected, aln_node.increase_weight(w))
+        raise SkipTest # TODO: implement your test here
+
+class TestAlnGraph:
+    def test___init__(self):
+        backbone_seq, reads = generate_simulated_reads()
+        aln_graph = AlnGraph(backbone_seq)
+        assert len(aln_graph.nodes) == len(backbone_seq) + 2
+
+    def test_add_alignment(self):
+        aln_graph = AlnGraph("ATATTAGGC")
+        alns = [((0,  9, 'A-TAGCCGGC'),   (2, 9, 'ATTA---GGC')), 
+                ((0, 10, 'ATA-TACCGAG-'), (0, 9, 'ATATTA--G-GC')), 
+                ((0, 10, 'ATCATCC--GGC'), (0, 9, 'AT-AT--TAGGC')), 
+                ((0,  9, 'ATA-TACGGC'),   (0, 9, 'ATATTA-GGC'))]
+        for aln in alns:
+            aln_graph.add_alignment( aln )
+        assert len(aln_graph.nodes) != 0
+        assert len(aln_graph.edges) != 0
+
+
+    def test_add_edge(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.add_edge(edge))
+        raise SkipTest # TODO: implement your test here
+
+    def test_add_node(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.add_node(node))
+        raise SkipTest # TODO: implement your test here
+
+    def test_delete_edge(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.delete_edge(edge))
+        raise SkipTest # TODO: implement your test here
+
+    def test_delete_node(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.delete_node(node))
+        raise SkipTest # TODO: implement your test here
+
+    def test_find_best_path(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.find_best_path())
+        raise SkipTest # TODO: implement your test here
+
+    def test_generate_consensus(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.generate_consensus())
+        raise SkipTest # TODO: implement your test here
+
+    def test_merge_in_nodes(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.merge_in_nodes(nodes, node))
+        raise SkipTest # TODO: implement your test here
+
+    def test_merge_nodes(self):
+        aln_graph = AlnGraph("ATAATTGGC")
+        alns = [((0, 9, 'ATAG--CTGGC'), (0, 9, 'ATA-AT-TGGC')), 
+                ((0, 9, 'ATAG--CTGGC'), (0, 9, 'ATA-AT-TGGC')), 
+                ((0, 9, 'ATAG-TTGGC'), (0, 9, 'ATA-ATTGGC')), 
+                ((0, 9, 'ATAG-TTGGC'), (0, 9, 'ATA-ATTGGC')), 
+                ((0, 9, 'ATAG--CTGGC'), (0, 9, 'ATA-AT-TGGC'))]
+        for aln in alns:
+            aln_graph.add_alignment( aln )
+        aln_graph.merge_nodes()
+
+    def test_merge_out_nodes(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.merge_out_nodes(node, nodes))
+        raise SkipTest # TODO: implement your test here
+
+    def test_output_consensus_fasta(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.output_consensus_fasta(fn, rID))
+        raise SkipTest # TODO: implement your test here
+
+    def test_track_path(self):
+        # aln_graph = AlnGraph(backbone_seq)
+        # assert_equal(expected, aln_graph.track_path(seq, node))
+        raise SkipTest # TODO: implement your test here
+
+class TestOutputDot:
+    def test_output_dot(self):
+        # assert_equal(expected, output_dot(aln_graph, fn, r))
+        raise SkipTest # TODO: implement your test here
+
+class TestOutputDot2:
+    def test_output_dot_2(self):
+        # assert_equal(expected, output_dot_2(aln_graph, fn))
+        raise SkipTest # TODO: implement your test here
+
+class TestGenerateSimulatedReads:
+    def test_generate_simulated_reads(self):
+        # assert_equal(expected, generate_simulated_reads())
+        raise SkipTest # TODO: implement your test here
+
+class TestSimpleTest:
+    def test_simple_test(self):
+        # assert_equal(expected, simple_test())
+        raise SkipTest # TODO: implement your test here
+
diff --git a/test/cpp/AlignmentTest.cpp b/test/cpp/AlignmentTest.cpp
new file mode 100644
index 0000000..6cd3bc8
--- /dev/null
+++ b/test/cpp/AlignmentTest.cpp
@@ -0,0 +1,78 @@
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <gtest/gtest.h>
+#include <boost/graph/adjacency_list.hpp>
+#include "Alignment.hpp"
+
+using namespace dagcon;
+
+TEST(Alignment, Normalize) {
+    Alignment a, b;
+    a.start = 1;
+
+    // test mismatch
+    b.qstr = "CAC";
+    b.tstr = "CGC";
+    b = normalizeGaps(b);
+    EXPECT_EQ("C-AC", b.qstr);
+    EXPECT_EQ("CG-C", b.tstr);
+
+    // test shifting gaps on read
+    b.qstr = "-C--CGT";
+    b.tstr = "CCGAC-T";
+    b = normalizeGaps(b);
+    EXPECT_EQ("CCG--T", b.qstr);
+    EXPECT_EQ("CCGACT", b.tstr);
+    
+    // another gap reference push
+    a.tstr = "ATATTA---GGC"; 
+    a.qstr = "ATAT-AGCCGGC"; 
+
+    b = a;
+    b = normalizeGaps(b);
+    // query remains unchanged
+    EXPECT_EQ(a.qstr, b.qstr);
+    // target shifts the G bases to the left
+    EXPECT_EQ("ATATTAG--G-C", b.tstr);
+}
+
+TEST(Alignment, ParseBasic) {
+    std::ifstream file("basic.m5");
+    Alignment aln;
+    file >> aln;
+    EXPECT_EQ(1, aln.start);
+    EXPECT_EQ("CAC", aln.qstr);
+    EXPECT_EQ("CGC", aln.tstr);
+
+    file >> aln;
+    EXPECT_EQ(1, aln.start);
+    EXPECT_EQ("AATTGGCC", aln.qstr);
+    EXPECT_EQ("GGCCAATT", aln.tstr);
+}
+
+TEST(Alignment, ParseQuery) {
+    std::ifstream file("parsequery.m5");
+    Alignment aln;
+    Alignment::groupByTarget = false;
+    file >> aln;
+    EXPECT_EQ("m130110_062238_00114_c100480560100000001823075906281381_s1_p0/311/1102_3151",
+              aln.id);
+    EXPECT_EQ(2049, aln.tlen);
+    EXPECT_EQ(8, aln.start);
+    EXPECT_EQ("CTGCATGCT", aln.tstr.substr(0,9));
+    EXPECT_EQ("CTGCA--CT", aln.qstr.substr(0,9));
+}
+
+TEST(AlnGraphBoostTest, Trim) {
+    dagcon::Alignment aln;
+    aln.tstr = "GATGAAGCCGGGG---TTACAGGCATGGATGTGGATAACGTCGAGTA-C-AGTC-GTA----TTC--C-TGCAGGGGTTGACG-TTTT-CCACCATCGCACGCCGGGACCATCACCGT-TAT-GAAGATTCAGCA-CCGGGG-GCGGCTGAATGATTTTCTCCTGATGGCCATTGACGGAGGATGATTGCCCGGCCGGAC";
+    aln.qstr = "GATGAAGC-GGGGGCGTTACAG-CATGGATGT------CG---AGTAACG-GTCAGTACCAGTTCATCCTGCAGG--TTGACGGTTTTTCCACCATCGCACGCCGGGACCATCACCGTGTATCGAAGATTCA-CAAC-GGGGC-CG-CTGAATGATTTTCTC-TG-TG-CCAT-GACGGAGGATGAT-GCCCG-CCGGA-";
+    aln.start = 1;
+    aln.strand = '-';
+
+    trimAln(aln, 14);
+    EXPECT_EQ("TACAGGCATGGATGTGGATAACGTCGAGTA-C-AGTC-GTA----TTC--C-TGCAGGGGTTGACG-TTTT-CCACCATCGCACGCCGGGACCATCACCGT-TAT-GAAGATTCAGCA-CCGGGG-GCGGCTGAATGATTTTCTCCTGATGGCCATTGACGGAGGATGA", aln.tstr);
+    EXPECT_EQ("TACAG-CATGGATGT------CG---AGTAACG-GTCAGTACCAGTTCATCCTGCAGG--TTGACGGTTTTTCCACCATCGCACGCCGGGACCATCACCGTGTATCGAAGATTCA-CAAC-GGGGC-CG-CTGAATGATTTTCTC-TG-TG-CCAT-GACGGAGGATGA", aln.qstr);
+    EXPECT_EQ(15, aln.start);
+}
diff --git a/test/cpp/AlnGraphBoostTest.cpp b/test/cpp/AlnGraphBoostTest.cpp
new file mode 100644
index 0000000..8e2fee8
--- /dev/null
+++ b/test/cpp/AlnGraphBoostTest.cpp
@@ -0,0 +1,56 @@
+#include <string>
+#include <sstream>
+#include <map>
+#include <vector>
+#include <gtest/gtest.h>
+#include <boost/graph/adjacency_list.hpp>
+#include "Alignment.hpp"
+#include "AlnGraphBoost.hpp"
+
+TEST(AlnGraphBoostTest, RawConsensus) {
+    std::string backbone = "ATATTAGGC";
+    AlnGraphBoost ag(backbone); 
+    dagcon::Alignment *algs = new dagcon::Alignment[5];
+    
+    algs[0].tstr = "ATATTA---GGC";
+    algs[0].qstr = "ATAT-AGCCGGC";
+
+    algs[1].tstr = "ATATTA-GGC";
+    algs[1].qstr = "ATAT-ACGGC";
+
+    algs[2].tstr = "AT-ATTA--GGC";
+    algs[2].qstr = "ATCAT--CCGGC";
+
+    algs[3].tstr = "ATATTA--G-GC";
+    algs[3].qstr = "ATAT-ACCGAG-";
+
+    algs[4].tstr = "ATATTA---GGC";
+    algs[4].qstr = "ATAT-AGCCGGC";
+
+    for(int i=0; i < 5; i++) {
+        dagcon::Alignment& ra = algs[i];
+        ra.id = "target";
+        ra.tlen = 9;
+        ra.start = 1;
+    }
+    ag.addAln(algs[0]);
+    ag.addAln(algs[1]);
+    ag.addAln(algs[2]);
+    ag.addAln(algs[3]);
+    ag.addAln(algs[4]);
+
+    ag.mergeNodes();
+    std::string expected = "ATATAGCCGGC";
+    const std::string actual = ag.consensus();
+    EXPECT_EQ(expected, actual);
+}
+
+TEST(AlnGraphBoostTest, DanglingNodes) {
+    AlnGraphBoost ag(12); 
+    dagcon::Alignment a;
+    a.tstr = "C-GCGGA-T-G-";
+    a.qstr = "CCGCGG-G-A-T";
+
+    ag.addAln(a);
+    EXPECT_FALSE(ag.danglingNodes());
+}
diff --git a/test/cpp/Makefile b/test/cpp/Makefile
new file mode 100644
index 0000000..5a72813
--- /dev/null
+++ b/test/cpp/Makefile
@@ -0,0 +1,77 @@
+.PHONY: all check test_target_hit test_alngraph test_alignment test_simple_aligner
+# project source code
+SRCDIR := ../../src/cpp
+
+include $(SRCDIR)/pbi.mk
+include $(SRCDIR)/boost.mk
+include gtest.mk
+
+GTEST_CPPFLAGS += -isystem $(GTEST_DIR)/include
+GTEST_CXXFLAGS += -g -Wall -Wextra -pthread
+
+INCDIRS := -I$(PBDATA) -I$(BLASR) -I$(BOOST_HEADERS) -I$(GTEST_DIR)/include -I$(GTEST_DIR) $(EXTRA_INCDIRS)
+LDFLAGS := -L$(PBDATA) -L$(BLASR) $(EXTRA_LDFLAGS)
+LDLIBS := -lpbdata -lblasr -lpthread ${EXTRA_LDLIBS}
+
+CXXFLAGS := -O3 -std=c++11 $(INCDIRS) -I$(SRCDIR)
+
+GTEST_OBJECTS := gtest-all.o gtest_main.o
+DAZCON_OBJECTS := $(SRCDIR)/DB.o $(SRCDIR)/align.o $(SRCDIR)/Alignment.o \
+			      $(SRCDIR)/DazAlnProvider.o
+
+PBDAGCON_OBJECTS := $(SRCDIR)/AlnGraphBoost.o $(SRCDIR)/Alignment.o \
+					$(SRCDIR)/SimpleAligner.o 
+vpath %.cc $(gtest_version)/src
+
+BUILDMSG = "=== Building $@ ==="
+
+all: check
+
+check: test_target_hit test_alngraph test_alignment test_simple_aligner
+
+# XXX: need to mock out db_ in order to run test_target
+
+test_target_hit: $(GTEST_OBJECTS) $(DAZCON_OBJECTS) TargetHitTest.o 
+	@echo $(BUILDMSG)
+	$(CXX) $^ -o $@ -lpthread
+	./$@
+
+test_target: $(GTEST_OBJECTS) $(DAZCON_OBJECTS) TargetTest.o
+	@echo $(BUILDMSG)
+	$(CXX) $^ -o $@ -lpthread
+	./$@
+
+test_alngraph: $(GTEST_OBJECTS) $(PBDAGCON_OBJECTS) AlnGraphBoostTest.o
+	@echo $(BUILDMSG)
+	$(CXX) $^ -static -o $@ $(LDFLAGS) $(LDLIBS)
+	./$@
+
+test_alignment: $(GTEST_OBJECTS) $(PBDAGCON_OBJECTS) AlignmentTest.o
+	@echo $(BUILDMSG)
+	$(CXX) $^ -static -o $@ $(LDFLAGS) $(LDLIBS)
+	./$@
+
+test_simple_aligner: $(GTEST_OBJECTS) $(PBDAGCON_OBJECTS) SimpleAlignerTest.o
+	@echo $(BUILDMSG)
+	$(CXX) $^ -static -o $@ $(LDFLAGS) $(LDLIBS)
+	./$@
+
+$(SRCDIR)/AlnGraphBoost.o: $(BOOST_HEADERS)
+
+$(GTEST_OBJECTS): $(GTEST_DIR)
+	$(CXX) $(GTEST_CPPFLAGS) -I$(GTEST_DIR) $(GTEST_CXXFLAGS) -c $</src/$(@:.o=.cc)
+
+$(BOOST_HEADERS):
+	@echo Fetching boost headers from $(URI)
+	cd $(SRCDIR)/third-party && $(GET_BOOST)
+
+$(GTEST_DIR):
+	@echo Fetching gtest from $(gtest_uri)
+	$(get_gtest)
+	unzip -q $(gtest_version).zip
+	$(RM) $(gtest_version).zip
+
+clean:
+	$(RM) $(SRCDIR)/*.o
+	$(RM) *.o
+	$(RM) test_*
diff --git a/test/cpp/SimpleAlignerTest.cpp b/test/cpp/SimpleAlignerTest.cpp
new file mode 100644
index 0000000..2499de1
--- /dev/null
+++ b/test/cpp/SimpleAlignerTest.cpp
@@ -0,0 +1,21 @@
+#include <cstring>
+#include <string>
+#include <algorithm>
+#include <gtest/gtest.h>
+#include "Alignment.hpp"
+#include "SimpleAligner.hpp"
+
+TEST(SimpleAligner, align) {
+    SimpleAligner sa;
+    dagcon::Alignment a;
+    a.id = "test";
+    a.start = 765;
+    a.end = 826;
+    a.tlen = 2092;
+    a.strand = '-';
+    a.tstr = "ACAGAGATGCAAGGTAAAGTACAATTGAAAAACTAACCTCTTCCAGCGAGACTTATAGCGA";
+    a.qstr = "ACAGAAGATGAAGGTAAATACAATGAAAAAACTACCTCGGTTCCAGCGAGAACTATAGCGA";
+    sa.align(a);
+    EXPECT_EQ("TCGCTATAAGT-CTCGCTGGAA--GAGGTTAGTTTTT-CAATTGTACTTTACCTTGCATCT-CTGT", a.tstr);
+    EXPECT_EQ(1267, a.start);
+}
diff --git a/test/cpp/TargetHitTest.cpp b/test/cpp/TargetHitTest.cpp
new file mode 100644
index 0000000..758251a
--- /dev/null
+++ b/test/cpp/TargetHitTest.cpp
@@ -0,0 +1,77 @@
+#include <gtest/gtest.h>
+#include <DazAlnProvider.hpp>
+
+TEST(TargetHitTest, single_overlap_perfect) {
+    Record rec;
+    Overlap ovl;
+    rec.ovl = ovl;
+
+    Path path;
+    path.abpos = 27;
+    path.aepos = 7013;
+    path.bbpos = 1231;
+    path.bepos = 8217;
+    path.diffs = 0;
+    
+    rec.ovl.path = path;
+
+    TargetHit th;
+    th.add(rec);
+    th.computeOvlScore();
+    EXPECT_FLOAT_EQ(6986, th.ovlScore); 
+}
+
+TEST(TargetHitTest, single_overlap_inaccurate) {
+    Record rec;
+    Overlap ovl;
+    rec.ovl = ovl;
+
+    Path path;
+    path.abpos = 10;
+    path.aepos = 5000;
+    path.bbpos = 2000;
+    path.bepos = 6000;
+    path.diffs = 230;
+    
+    rec.ovl.path = path;
+
+    TargetHit th;
+    th.add(rec);
+    th.computeOvlScore();
+    EXPECT_FLOAT_EQ(3770, th.ovlScore); 
+}
+
+TEST(TargetHitTest, multi_overlap_inaccurate) {
+    Record r1;
+    Overlap o1;
+    r1.ovl = o1;
+
+    Path p1;
+    p1.abpos = 10;
+    p1.aepos = 5000;
+    p1.bbpos = 2000;
+    p1.bepos = 6000;
+    p1.diffs = 230;
+    r1.ovl.path = p1;
+
+    Record r2;
+    Overlap o2;
+    r2.ovl = o2;
+
+    Path p2;
+    p2.abpos = 5005;
+    p2.aepos = 7005;
+    p2.bbpos = 6001;
+    p2.bepos = 7995;
+    p2.diffs = 53;
+    r2.ovl.path = p2;
+    
+    TargetHit th;
+
+    th.add(r1);
+    th.computeOvlScore();
+    EXPECT_FLOAT_EQ(3770, th.ovlScore); 
+    th.add(r2);
+    th.computeOvlScore();
+    EXPECT_FLOAT_EQ(4721, th.ovlScore); 
+}
diff --git a/test/cpp/TargetTest.cpp b/test/cpp/TargetTest.cpp
new file mode 100644
index 0000000..f06f526
--- /dev/null
+++ b/test/cpp/TargetTest.cpp
@@ -0,0 +1,22 @@
+#include <gtest/gtest.h>
+#include <DazAlnProvider.hpp>
+
+TEST(TargetTest, add_record) {
+    Record r1, r2, r3;
+    Overlap o1, o2, o3;
+
+    o1.aread = 1; o1.bread = 3; o1.flags = 0;
+    o2.aread = 1; o2.bread = 3; o2.flags = 0;
+    o3.aread = 2; o3.bread = 3; o3.flags = 1;
+     
+    r1.ovl = o1;
+    r2.ovl = o2;
+    r3.ovl = o3;
+    
+    Target t;
+    t.addRecord(r1);
+    t.addRecord(r2);
+    t.addRecord(r3);
+
+    EXPECT_EQ(2, t.hits.size());
+}
diff --git a/test/cpp/basic.m5 b/test/cpp/basic.m5
new file mode 100644
index 0000000..7971f66
--- /dev/null
+++ b/test/cpp/basic.m5
@@ -0,0 +1,2 @@
+id 3 0 3 +  ref 3 0 3 + -40645 8129 0 0 0 254 CAC |-| CGC
+id 3 0 3 +  ref 3 0 3 - -40645 8129 0 0 0 254 GGCCAATT |-| AATTGGCC
diff --git a/test/cpp/gtest.mk b/test/cpp/gtest.mk
new file mode 100644
index 0000000..c5b2fee
--- /dev/null
+++ b/test/cpp/gtest.mk
@@ -0,0 +1,10 @@
+# user-specified location of gtest
+ifdef gtest
+GTEST_DIR := $(gtest)
+else
+# download/unpack a version from the inter-web
+gtest_version := gtest-1.7.0
+gtest_uri := https://googletest.googlecode.com/files/$(gtest_version).zip
+get_gtest := curl -O $(gtest_uri)
+GTEST_DIR := $(gtest_version)
+endif
diff --git a/test/cpp/parsequery.m5 b/test/cpp/parsequery.m5
new file mode 100644
index 0000000..8c5038a
--- /dev/null
+++ b/test/cpp/parsequery.m5
@@ -0,0 +1 @@
+m130110_062238_00114_c100480560100000001823075906281381_s1_p0/311/1102_3151/0_2049 2049 7 1640 +  m130110_062238_00114_c100480560100000001823075906281381_s1_p0/311/1102_3151 2049 493 2044 - -3997 1303 93 237 155 0 CTGCATGCT-GTGACTCTGGATAGAGTTTTCTGCCGCATGTTAATTGTAATCAAGACCGTTTAGTAAGTTCTTATCGATTATCATTACGTTTC-ATCG-C-T-ATTATCGTGGTTTTGGTAGGTCAATGGTGTTCAGGTGTGCATTGTTGTTGATGTATTTCTGCAGTATCGATTTCTGTTCTGTATCGCTGGCAT-TGTTG-TCATCAATAGG-AGATGGTAGTC--TGGTTTTTG-----------T-TAATAGATTATACGC-TGTGATAATGCT [...]

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/pbdagcon.git



More information about the debian-med-commit mailing list