diff --git a/contrib/vcflib-min/include/vcflib/Variant.h b/contrib/vcflib-min/include/vcflib/Variant.h
new file mode 100644
index 0000000..27e8c4a
--- /dev/null
+++ b/contrib/vcflib-min/include/vcflib/Variant.h
@@ -0,0 +1,655 @@
+/*
+    vcflib C++ library for parsing and manipulating VCF files
+
+    Variant.h is used by external tools, such as freebayes. We should take care to
+    minimize what it pulls in.
+
+    Copyright © 2010-2024 Erik Garrison
+    Copyright © 2020-2024 Pjotr Prins
+
+    This software is published under the MIT License. See the LICENSE file.
+*/
+
+#pragma once
+
+#include <vector>
+#include <list>
+#include <map>
+#include <string>
+#include <iostream>
+#include <fstream>
+#include <utility>
+#include <cassert>
+#include <stack>
+#include <queue>
+#include <set>
+#include "split.h"
+#include <tabix.hpp>
+#include <Fasta.h>
+
+#include "allele.hpp"
+
+// The following includes moved into their sources because of lib dependencies
+// #include <SmithWatermanGotoh.h>
+// #include "ssw_cpp.hpp"
+// #include <Fasta.h> --> see canonicalize.h
+// #include "multichoose.h"
+
+extern "C" {
+  #include "filevercmp.h"
+}
+
+using namespace std;
+
+namespace vcflib {
+
+class Variant;
+
+enum VariantFieldType { FIELD_FLOAT = 0
+                      , FIELD_INTEGER
+                      , FIELD_BOOL
+                      , FIELD_STRING
+                      , FIELD_UNKNOWN
+                      };
+
+enum VariantFieldNumber { ALLELE_NUMBER = -2
+                        , GENOTYPE_NUMBER = -1
+                        };
+
+const int INDEX_NONE = -1;
+const int NULL_ALLELE = -1;
+
+VariantFieldType typeStrToFieldType(string& typeStr);
+ostream& operator<<(ostream& out, VariantFieldType type);
+
+typedef map<string, map<string, vector<string> > > Samples;
+
+/// Compute a reverse complement. Supports both upper-case and lower-case input characters.
+std::string reverse_complement(const std::string& seq);
+/// Convert a sequence to upper-case
+std::string toUpper(const std::string& seq);
+bool allATGCN(const string& s, bool allowLowerCase = true);
+
+class VariantCallFile {
+
+public:
+
+    istream* file;
+    Tabix* tabixFile;
+
+    bool usingTabix;
+    string vcf_header;
+
+
+    string header;
+    string line; // the current line
+    string fileformat;
+    string fileDate;
+    string source;
+    string reference;
+    string phasing;
+    map<string, VariantFieldType> infoTypes;
+    map<string, int> infoCounts;
+    map<string, VariantFieldType> formatTypes;
+    map<string, int> formatCounts;
+    vector<string> sampleNames;
+    bool parseSamples;
+    bool _done;
+
+    void updateSamples(vector<string>& newSampleNames);
+    string headerWithSampleNames(vector<string>& newSamples); // non-destructive, for output
+    void addHeaderLine(string line);
+    void removeInfoHeaderLine(string const & line);
+    void removeGenoHeaderLine(string const & line);
+    vector<string> infoIds(void);
+    vector<string> formatIds(void);
+
+    bool open(const string& filename) {
+        vector<string> filenameParts = split(filename, ".");
+        if (filenameParts.back() == "gz" || filenameParts.back() == "bgz") {
+            return openTabix(filename);
+        } else {
+            return openFile(filename);
+        }
+    }
+
+    bool openFile(const string& filename) {
+        file = &_file;
+        _file.open(filename.c_str(), ifstream::in);
+        parsedHeader = parseHeader();
+        return parsedHeader;
+    }
+
+    bool openTabix(const string& filename) {
+        usingTabix = true;
+        // Tabix does not modify the string, better to keep rest of the interface clean
+        tabixFile = new Tabix(const_cast<string&>(filename));
+        parsedHeader = parseHeader();
+        return parsedHeader;
+    }
+
+    bool open(istream& stream) {
+        file = &stream;
+        parsedHeader = parseHeader();
+        return parsedHeader;
+    }
+
+    bool open(ifstream& stream) {
+        file = &stream;
+        parsedHeader = parseHeader();
+        return parsedHeader;
+    }
+
+    bool openForOutput(string& headerStr) {
+        parsedHeader = parseHeader(headerStr);
+        return parsedHeader;
+    }
+
+    off_t file_pos() {
+        if (usingTabix) {
+            return tabixFile->file_pos();
+        }
+        return file->tellg();
+    }
+
+    VariantCallFile(void) :
+        usingTabix(false),
+        parseSamples(true),
+        justSetRegion(false),
+        parsedHeader(false)
+    { }
+
+    ~VariantCallFile(void) {
+        if (usingTabix) {
+            delete tabixFile;
+        }
+    }
+
+    bool is_open(void) { return parsedHeader; }
+
+    bool eof(void) { return _file.eof(); }
+
+    bool done(void) { return _done; }
+
+    bool parseHeader(string& headerStr);
+
+    bool parseHeader(void);
+
+    bool getNextVariant(Variant& var);
+
+    bool setRegion(const string& region);
+    bool setRegion(const string& seq, long int start, long int end = 0);
+    vector<string> getHeaderLinesFromFile();
+
+private:
+    bool firstRecord;
+    bool justSetRegion;
+    bool usingFile;
+    ifstream _file;
+    bool parsedHeader;
+
+};
+
+class Variant {
+
+    friend ostream& operator<<(ostream& out, Variant& var);
+
+public:
+
+    string sequenceName;
+    long position;
+    long zeroBasedPosition(void) const;
+    string id;
+    string ref;
+    vector<string> alt;      // a list of all the alternate alleles present at this locus
+    vector<string> alleles;  // a list all alleles (ref + alt) at this locus
+                             // the indicies are organized such that the genotype codes (0,1,2,.etc.)
+                             // correspond to the correct offest into the alleles vector.
+                             // that is, alleles[0] = ref, alleles[1] = first alternate allele, etc.
+
+    string vrepr(void);  // a comparable record of the variantion described by the record
+    set<string> altSet(void);  // set of alleles, rather than vector of them
+    map<string, int> altAlleleIndexes;  // reverse lookup for alleles
+
+
+    map<string, vector<VariantAllele> > parsedAlternates(bool includePreviousBaseForIndels = false,
+                                                         bool useMNPs = false,
+                                                         bool useEntropy = false,
+                                                         float matchScore = 10.0f,
+                                                         float mismatchScore = -9.0f,
+                                                         float gapOpenPenalty = 15.0f,
+                                                         float gapExtendPenalty = 6.66f,
+                                                         float repeatGapExtendPenalty = 0.0f,
+                                                         const string& flankingRefLeft = "",
+                                                         const string& flankingRefRight = "");
+
+    // the same output format as parsedAlternates, without parsing
+    map<string, vector<VariantAllele> > flatAlternates(void);
+
+    map<string, string> extendedAlternates(long int newPosition, long int length);
+
+    /**
+     * Convert a structural variant to the canonical VCF4.3 format using a reference.
+     *   Meturns true if the variant is canonicalized, false otherwise.
+     *   May NOT be called twice on the same variant; it will fail an assert.
+     *   Returns false for non-SVs
+     *   place_seq: if true, the ref/alt fields are
+     *       filled in with the corresponding sequences
+     *     from the reference (and optionally insertion FASTA)
+     * min_size_override: If a variant is less than this size,
+     *     and it has a valid REF and ALT, consider it canonicalized
+     *     even if the below conditions are not true.
+     * Fully canonicalized variants (which are greater than min_size_override)
+     * guarantee the following:
+     *  - POS <= END and corresponds to the anchoring base for symbolic alleles
+     *  - SVLEN info field is set and is positive for all variants except DELs
+     *  - SVTYPE info field is set and is in {DEL, INS, INV, DUP}
+     *  - END info field is set to the POS + len(REF allele) - 1 and corresponds to the final affected reference base
+     *  - Insertions get an upper-case SEQ info field
+     *  - REF and ALT are upper-case if filled in by this function
+     *  - canonical = true;
+     * TODO: CURRENTLY: canonical requires there be only one alt allele
+    **/
+    bool canonicalize(FastaReference& ref,
+         vector<FastaReference*> insertions,
+         bool place_seq = true,
+         int min_size_override = 0);
+
+    /**
+     * Returns true if the variant's ALT contains a symbolic allele like <INV>
+     * instead of sequence, and the variant has an SVTYPE INFO tag.
+     */
+    bool isSymbolicSV() const;
+
+    /**
+     * Returns true if the variant has an SVTYPE INFO tag and either an SVLEN or END INFO tag.
+     */
+    bool hasSVTags() const;
+
+    /**
+     * This returns true if the variant appears able to be handled by
+     * canonicalize(). It checks if it has fully specified sequence, or if it
+     * has a defined SV type and length/endpoint.
+     */
+    bool canonicalizable();
+
+    /**
+     * This gets set to true after canonicalize() has been called on the variant, if it succeeded.
+     */
+    bool canonical;
+
+    /**
+     * Get the maximum zero-based position of the reference affected by this variant.
+     * Only works reliably for variants that are not SVs or for SVs that have been canonicalize()'d.
+     */
+    int getMaxReferencePos();
+
+    /**
+     * Return the SV type of the given alt, or "" if there is no SV type set for that alt.
+     * This is the One True Way to get the SVTYPE of a variant; we should not touch the SVTYPE tag anywhere else.
+     */
+    string getSVTYPE(int altpos = 0) const;
+
+    string originalLine; // the literal of the record, as read
+    // TODO
+    // the ordering of genotypes for the likelihoods is given by: F(j/k) = (k*(k+1)/2)+j
+    // vector<pair<int, int> > genotypes;  // indexes into the alleles, ordered as per the spec
+    string filter;
+    double quality;
+    VariantFieldType infoType(const string& key);
+    map<string, vector<string> > info;  // vector<string> allows for lists by Genotypes or Alternates
+    map<string, bool> infoFlags; // INFO flags are stored separately
+    vector<string> infoOrderedKeys; // track order of INFO fields
+    VariantFieldType formatType(const string& key);
+    vector<string> format;
+    map<string, map<string, vector<string> > > samples;  // vector<string> allows for lists by Genotypes or Alternates
+    vector<string> sampleNames;
+    vector<string> outputSampleNames;
+    VariantCallFile* vcf;
+
+    //void addInfoInt(string& tag, int value);
+    //void addInfoFloat(string& tag, double value);
+    //void addInfoString(string& tag, string& value);
+
+    void removeAlt(const string& altallele);
+
+public:
+
+    Variant() { }
+
+    Variant(VariantCallFile& v)
+        : sampleNames(v.sampleNames)
+        , outputSampleNames(v.sampleNames)
+        , vcf(&v)
+    { }
+
+    void setVariantCallFile(VariantCallFile& v);
+    void setVariantCallFile(VariantCallFile* v);
+
+    void parse(string& line, bool parseSamples = true);
+    void addFilter(const string& tag);
+    bool getValueBool(const string& key, string& sample, int index = INDEX_NONE);
+    double getValueFloat(const string& key, string& sample, int index = INDEX_NONE);
+    string getValueString(const string& key, string& sample, int index = INDEX_NONE);
+    bool getSampleValueBool(const string& key, string& sample, int index = INDEX_NONE);
+    double getSampleValueFloat(const string& key, string& sample, int index = INDEX_NONE);
+    string getSampleValueString(const string& key, string& sample, int index = INDEX_NONE);
+    bool getInfoValueBool(const string& key, int index = INDEX_NONE);
+    double getInfoValueFloat(const string& key, int index = INDEX_NONE);
+    string getInfoValueString(const string& key, int index = INDEX_NONE);
+    void printAlt(ostream& out) const;      // print a comma-sep list of alternate alleles to an ostream
+    void printAlleles(ostream& out) const;  // print a comma-sep list of *all* alleles to an ostream
+    int getAltAlleleIndex(const string& allele);
+    void updateAlleleIndexes(void);
+    void addFormatField(const string& key);
+    void setOutputSampleNames(const vector<string>& outputSamples);
+    map<pair<int, int>, int> getGenotypeIndexesDiploid(void);
+    int getNumSamples(void);
+    int getNumValidGenotypes(void);
+    string getGenotype(const string& sample);
+    bool isPhased(void);
+    // TODO
+    //void setInfoField(const string& key, string& val);
+    void reduceAlleles(
+	map<string, pair<vector<VariantAllele>, bool> > varAlleles,
+	VariantCallFile &variantFile,
+	Variant var,
+	const string& parseFlag,
+	bool keepInfo=true,
+	bool keepGeno=true,
+	bool debug=false);
+
+private:
+
+    string lastFormat;
+
+};
+
+
+// from BamTools
+// RuleToken implementation
+
+class RuleToken {
+
+public:
+
+    // enums
+    enum RuleTokenType { OPERAND = 0
+                       , NUMBER
+                       , BOOLEAN_VARIABLE
+                       , NUMERIC_VARIABLE
+                       , STRING_VARIABLE
+                       , AND_OPERATOR
+                       , OR_OPERATOR
+                       , ADD_OPERATOR
+                       , SUBTRACT_OPERATOR
+                       , MULTIPLY_OPERATOR
+                       , DIVIDE_OPERATOR
+                       , NOT_OPERATOR
+                       , EQUAL_OPERATOR
+                       , GREATER_THAN_OPERATOR
+                       , LESS_THAN_OPERATOR
+                       , LEFT_PARENTHESIS
+                       , RIGHT_PARENTHESIS
+                       };
+
+    // constructor
+    RuleToken(const string& token, map<string, VariantFieldType>& variables);
+    RuleToken(void)
+        : type(BOOLEAN_VARIABLE)
+        , state(false)
+    { }
+
+    // data members
+    RuleTokenType type;
+    string value;
+
+    double number;
+    string str;
+    bool state;
+
+    bool isVariable; // if this is a variable
+    //bool isEvaluated; // when we evaluate variables
+
+    RuleToken apply(RuleToken& other);
+
+};
+
+inline int priority(const RuleToken& token) {
+    switch ( token.type ) {
+        case ( RuleToken::MULTIPLY_OPERATOR )     : return 8;
+        case ( RuleToken::DIVIDE_OPERATOR )       : return 8;
+        case ( RuleToken::ADD_OPERATOR )          : return 7;
+        case ( RuleToken::SUBTRACT_OPERATOR )     : return 7;
+        case ( RuleToken::NOT_OPERATOR )          : return 6;
+        case ( RuleToken::EQUAL_OPERATOR )        : return 5;
+        case ( RuleToken::GREATER_THAN_OPERATOR ) : return 5;
+        case ( RuleToken::LESS_THAN_OPERATOR )    : return 5;
+        case ( RuleToken::AND_OPERATOR )          : return 4;
+        case ( RuleToken::OR_OPERATOR )           : return 3;
+        case ( RuleToken::LEFT_PARENTHESIS )      : return 0;
+        case ( RuleToken::RIGHT_PARENTHESIS )     : return 0;
+        default: cerr << "invalid token type" << endl; exit(1);
+    }
+}
+
+inline bool isRightAssociative(const RuleToken& token) {
+    return (token.type == RuleToken::NOT_OPERATOR ||
+            token.type == RuleToken::LEFT_PARENTHESIS);
+}
+
+inline bool isLeftAssociative(const RuleToken& token) {
+    return !isRightAssociative(token);
+}
+
+inline bool isLeftParenthesis(const RuleToken& token) {
+    return ( token.type == RuleToken::LEFT_PARENTHESIS );
+}
+
+inline bool isRightParenthesis(const RuleToken& token) {
+    return ( token.type == RuleToken::RIGHT_PARENTHESIS );
+}
+
+inline bool isOperand(const RuleToken& token) {
+    return ( token.type == RuleToken::OPERAND ||
+             token.type == RuleToken::NUMBER ||
+             token.type == RuleToken::NUMERIC_VARIABLE ||
+             token.type == RuleToken::STRING_VARIABLE ||
+             token.type == RuleToken::BOOLEAN_VARIABLE
+           );
+}
+
+inline bool isOperator(const RuleToken& token) {
+    return ( token.type == RuleToken::AND_OPERATOR ||
+             token.type == RuleToken::OR_OPERATOR  ||
+             token.type == RuleToken::NOT_OPERATOR ||
+             token.type == RuleToken::EQUAL_OPERATOR ||
+             token.type == RuleToken::GREATER_THAN_OPERATOR ||
+             token.type == RuleToken::LESS_THAN_OPERATOR ||
+             token.type == RuleToken::MULTIPLY_OPERATOR ||
+             token.type == RuleToken::DIVIDE_OPERATOR ||
+             token.type == RuleToken::ADD_OPERATOR ||
+             token.type == RuleToken::SUBTRACT_OPERATOR
+             );
+}
+
+inline bool isOperatorChar(const char& c) {
+    return (c == '!' ||
+            c == '&' ||
+            c == '|' ||
+            c == '=' ||
+            c == '>' ||
+            c == '<' ||
+            c == '*' ||
+            c == '/' ||
+            c == '+' ||
+            c == '-');
+}
+
+inline bool isParanChar(const char& c) {
+    return (c == '(' || c == ')');
+}
+
+inline bool isNumeric(const RuleToken& token) {
+    return token.type == RuleToken::NUMERIC_VARIABLE;
+}
+
+inline bool isString(const RuleToken& token) {
+    return token.type == RuleToken::STRING_VARIABLE;
+}
+
+inline bool isBoolean(const RuleToken& token) {
+    return token.type == RuleToken::BOOLEAN_VARIABLE;
+}
+
+inline bool isVariable(const RuleToken& token) {
+    return isNumeric(token) || isString(token) || isBoolean(token);
+}
+
+void tokenizeFilterSpec(string& filterspec, stack<RuleToken>& tokens, map<string, VariantFieldType>& variables);
+
+
+class VariantFilter {
+
+public:
+
+    enum VariantFilterType { SAMPLE = 0,
+                             RECORD };
+
+    string spec;
+    queue<RuleToken> tokens; // tokens, infix notation
+    queue<RuleToken> rules;  // tokens, prefix notation
+    VariantFilterType type;
+    VariantFilter(string filterspec, VariantFilterType filtertype, map<string, VariantFieldType>& variables);
+    bool passes(Variant& var, string& sample); // all alts pass
+    bool passes(Variant& var, string& sample, string& allele);
+    void removeFilteredGenotypes(Variant& var, bool keepInfo);
+
+};
+
+
+// genotype manipulation
+
+// TODO
+//map<string, int> decomposeGenotype(string& genotype);
+
+vector<int> decomposePhasedGenotype(const string& genotype);
+map<int, int> decomposeGenotype(const string& genotype);
+
+string genotypeToString(const map<int, int>& genotype);
+
+string phasedGenotypeToString(const vector<int>& genotype);
+
+bool isHet(const map<int, int>& genotype);
+
+bool isHom(const map<int, int>& genotype);
+
+bool hasNonRef(const map<int, int>& genotype);
+
+bool isHomRef(const map<int, int>& genotype);
+
+bool isHomNonRef(const map<int, int>& genotype);
+
+bool isNull(const map<int, int>& genotype);
+
+int ploidy(const map<int, int>& genotype);
+
+string unionInfoHeaderLines(string& s1, string& s2);
+
+// genotype likelihood ordering
+
+list<list<int> > glorder(int ploidy, int alleles);
+list<list<int> > _glorder(int ploidy, int alleles);
+list<int> glsWithAlt(int alt, int ploidy, int numalts);
+map<int, int> glReorder(int ploidy, int numalts, map<int, int>& alleleIndexMapping, vector<int>& altsToRemove);
+
+vector<string>& unique(vector<string>& strings);
+
+// for sorting, generating maps ordered by chromosome name
+class ChromNameCompare {
+public:
+    bool operator()(const string& a, const string& b) const {
+        return (filevercmp(a.c_str(), b.c_str()) < 0);
+    }
+};
+
+class VCFHeader
+{
+public:
+    VCFHeader();
+    ~VCFHeader() {}
+
+    /*
+     * Adds header_column to this->header_columns if
+     * it doesn't already exits.
+     */
+    void addHeaderColumn(const string& header_column);
+
+    /*
+     * Adds meta_line to either header_lines or header_lists.
+     *
+     * We parse out the ##_type_ from meta_line
+     * - If the meta_line ##_type_ is a key in header_lines then meta_line is added to header_lines
+     * - If the meta_line ##_type_ is a key in header_lists then meta_line is added to header_lists[##_type_] vector<string>
+     *    Unless that header_lists[##_type_] vector already contains the ID that is in meta_line, in that case it is not added
+     */
+    void addMetaInformationLine(const string& meta_line);
+
+    /*
+     * Converts header_lines, header_lists and header_columns to a proper VCF header
+     */
+    string getHeaderString();
+
+private:
+    VCFHeader(const VCFHeader& vcfHeader); // Do not implement the copy constructor, there is no reason to add this functionality
+    VCFHeader& operator=(const VCFHeader& vcfHeader); // Do not implement operator=, there is no reason to add this functionality
+
+    /*
+     * This is a helper function that determines if the ID substring contained in meta_line
+     * exists as a ID substring within the vector<string> meta_lines. Returns true if
+     * the ID exists within the vector and false otherwise.
+     */
+    bool metaInfoIdExistsInVector(const string& meta_line, vector<string>& meta_lines);
+
+    /*
+     * header_line_names_ordered contains all the header lines that
+     * are available and in the expected order for a valid VCF file
+     */
+    vector<string> header_line_names_ordered;
+    /*
+     * header_list_names_ordered contains all the header lists that
+     * are available and in the expected order for a valid VCF file
+     */
+    vector<string> header_list_names_ordered;
+
+    /*
+     * header_columns is set by the constructor to contain the 8 manditory VCF fields.
+     * Also, unique header_columns for each of the vcf files are added as well.
+     * Duplicates are not allowed, to prevent duplicates use addHeaderColumn when adding header columns
+     */
+    vector<string> header_columns;
+
+    /*
+     * the maps we're going to be using will be case-insensitive
+     * so that "fileFormat" and "fileformat" hash to the same item.
+     */
+    struct stringcasecmp {
+        struct charcasecmp {
+            bool operator() (const unsigned char& c1, const unsigned char& c2) const {
+                return tolower (c1) < tolower (c2);
+            }
+        };
+        bool operator() (const std::string & s1, const std::string & s2) const {
+            return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), charcasecmp());
+        }
+    };
+
+    // contains all the ##_types_ as keys, the value is either empty or a VCF file has set it
+    map<string, string, stringcasecmp> header_lines;
+
+    // contains all the ##_types_ as keys, the value is a vector of ##_type_ (since there can be duplicate #INFO for example, duplicate ids are not allowed)
+    map<string, vector<string>, stringcasecmp> header_lists;
+
+};
+
+} // end namespace VCF
diff --git a/contrib/vcflib-min/include/vcflib/allele.hpp b/contrib/vcflib-min/include/vcflib/allele.hpp
new file mode 100644
index 0000000..60f1350
--- /dev/null
+++ b/contrib/vcflib-min/include/vcflib/allele.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <string>
+#include <iosfwd>
+
+namespace vcflib {
+
+using namespace std;
+
+/*
+    A VariantAllele simply tracks [position,ref,alt] and has a string representation in 'repr'
+*/
+
+class VariantAllele {
+    friend ostream& operator<<(ostream& out, const VariantAllele& var);
+    friend bool operator<(const VariantAllele& a, const VariantAllele& b);
+    friend VariantAllele operator+(const VariantAllele& a, const VariantAllele& b);
+    friend bool operator==(const VariantAllele& a, const VariantAllele& b);
+    friend void shift_mid_left(VariantAllele& a, VariantAllele& b);
+    friend void shift_mid_right(VariantAllele& a, VariantAllele& b);
+public:
+    string ref;
+    string alt;
+    long position;
+    VariantAllele(string const & r, string const & a, long p)
+        : ref(r), alt(a), position(p) { }
+    bool is_pure_indel(void);
+};
+
+}
diff --git a/contrib/vcflib-min/include/vcflib/cigar.hpp b/contrib/vcflib-min/include/vcflib/cigar.hpp
new file mode 100644
index 0000000..99829a5
--- /dev/null
+++ b/contrib/vcflib-min/include/vcflib/cigar.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "allele.hpp"
+
+#include <vector>
+#include <list>
+
+namespace vcflib {
+
+using namespace std;
+
+typedef vector<pair<int, char> > Cigar;
+string varCigar(const vector<VariantAllele>& vav, bool xForMismatch = false);
+string mergeCigar(const string& c1, const string& c2);
+vector<pair<int, char> > splitUnpackedCigar(const string& cigarStr);
+vector<pair<int, string> > old_splitCigar(const string& cigarStr);
+vector<pair<int, char> > splitCigar(const string& cigarStr);
+list<pair<int, char> > splitCigarList(const string& cigarStr);
+int cigarRefLen(const vector<pair<int, char> >& cigar);
+vector<pair<int, char> > cleanCigar(const vector<pair<int, char> >& cigar);
+string old_joinCigar(const vector<pair<int, string> >& cigar);
+string joinCigar(const vector<pair<int, char> >& cigar);
+string joinCigarList(const list<pair<int, char> >& cigar);
+bool isEmptyCigarElement(const pair<int, char>& elem);
+
+}
diff --git a/contrib/vcflib-min/include/vcflib/convert.h b/contrib/vcflib-min/include/vcflib/convert.h
new file mode 100644
index 0000000..a17ae88
--- /dev/null
+++ b/contrib/vcflib-min/include/vcflib/convert.h
@@ -0,0 +1,31 @@
+/*
+    vcflib C++ library for parsing and manipulating VCF files
+
+    Copyright © 2010-2020 Erik Garrison
+    Copyright © 2020      Pjotr Prins
+
+    This software is published under the MIT License. See the LICENSE file.
+*/
+
+#ifndef __CONVERT_H
+#define __CONVERT_H
+
+#include <sstream>
+
+// converts the string into the specified type, setting r to the converted
+// value and returning true/false on success or failure
+template<typename T>
+bool convert(const std::string& s, T& r) {
+    std::istringstream iss(s);
+    iss >> r;
+    return iss.eof() ? true : false;
+}
+
+template<typename T>
+std::string convert(const T& r) {
+    std::ostringstream oss;
+    oss << r;
+    return oss.str();
+}
+
+#endif
diff --git a/contrib/vcflib-min/include/vcflib/filevercmp.h b/contrib/vcflib-min/include/vcflib/filevercmp.h
new file mode 100644
index 0000000..f3d78f0
--- /dev/null
+++ b/contrib/vcflib-min/include/vcflib/filevercmp.h
@@ -0,0 +1,42 @@
+/*
+   Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.uk>
+   Copyright (C) 2001 Anthony Towns <aj@azure.humbug.org.au>
+   Copyright (C) 2008-2009 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef FILEVERCMP_H
+#define FILEVERCMP_H
+
+/* Compare version strings:
+
+   This function compares strings S1 and S2:
+   1) By PREFIX in the same way as strcmp.
+   2) Then by VERSION (most similarly to version compare of Debian's dpkg).
+      Leading zeros in version numbers are ignored.
+   3) If both (PREFIX and  VERSION) are equal, strcmp function is used for
+      comparison. So this function can return 0 if (and only if) strings S1
+      and S2 are identical.
+
+   It returns number >0 for S1 > S2, 0 for S1 == S2 and number <0 for S1 < S2.
+
+   This function compares strings, in a way that if VER1 and VER2 are version
+   numbers and PREFIX and SUFFIX (SUFFIX defined as (\.[A-Za-z~][A-Za-z0-9~]*)*)
+   are strings then VER1 < VER2 implies filevercmp (PREFIX VER1 SUFFIX,
+   PREFIX VER2 SUFFIX) < 0.
+
+   This function is intended to be a replacement for strverscmp. */
+int filevercmp (const char *s1, const char *s2);
+
+#endif /* FILEVERCMP_H */
diff --git a/contrib/vcflib-min/include/vcflib/join.h b/contrib/vcflib-min/include/vcflib/join.h
new file mode 100644
index 0000000..5691b47
--- /dev/null
+++ b/contrib/vcflib-min/include/vcflib/join.h
@@ -0,0 +1,50 @@
+/*
+    vcflib C++ library for parsing and manipulating VCF files
+
+    Copyright © 2010-2020 Erik Garrison
+    Copyright © 2020      Pjotr Prins
+
+    This software is published under the MIT License. See the LICENSE file.
+*/
+
+#ifndef __JOIN_H
+#define __JOIN_H
+
+// functions to split a string by a specific delimiter
+#include <string>
+#include <vector>
+#include <sstream>
+#include <string.h>
+#include <list>
+
+// join a vector of elements by a delimiter object.  ostream<< must be defined
+// for both class S and T and an ostream, as it is e.g. in the case of strings
+// and character arrays
+template<class S, class T>
+std::string join(const std::vector<T>& elems, S& delim) {
+    std::stringstream ss;
+    typename std::vector<T>::const_iterator e = elems.begin();
+    if (e != elems.end()) {
+        ss << *e++;
+        for (; e != elems.end(); ++e) {
+            ss << delim << *e;
+        }
+    }
+    return ss.str();
+}
+
+// same for lists
+template<class S, class T>
+std::string join(std::list<T>& elems, S& delim) {
+    std::stringstream ss;
+    typename std::list<T>::iterator e = elems.begin();
+    if (e != elems.end()) {
+        ss << *e++;
+        for (; e != elems.end(); ++e) {
+            ss << delim << *e;
+        }
+    }
+    return ss.str();
+}
+
+#endif
diff --git a/contrib/vcflib-min/include/vcflib/multichoose.h b/contrib/vcflib-min/include/vcflib/multichoose.h
new file mode 100644
index 0000000..4ad9779
--- /dev/null
+++ b/contrib/vcflib-min/include/vcflib/multichoose.h
@@ -0,0 +1,85 @@
+#ifndef __MULTICHOOSE_H
+#define __MULTICHOOSE_H
+
+/*
+
+multichoose.h  -- n multichoose k for generic vectors
+
+author: Erik Garrison <erik.garrison@bc.edu>
+last revised: 2010-04-16
+author: Pjotr Prins
+last revised: 2024 (fixed template error and merged template into vcflib)
+
+Copyright (c) 2010 by Erik Garrison
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+
+// provides multiset combinations out of the std::vector of objects
+template <class T>
+std::vector< std::vector<T> > multichoose(int k, std::vector<T>& objects) {
+
+    std::vector< std::vector<T> > choices;
+
+    int j,j_1,q,r;
+
+    r = objects.size() - 1;
+
+    // combination indexes
+    std::vector<T*> a, b;
+
+    for (int i=0;i<k;i++) {
+        a.push_back(&objects[0]); b.push_back(&objects[r]);
+    }
+
+    j=k;
+    while(true){
+        std::vector<T> multiset;
+        multiset.reserve(k);
+        for(const auto elm : a)
+            multiset.push_back(*elm);
+        choices.push_back(multiset);
+        j=k;
+        do {
+	    j--;
+	    if (j<0) break;
+	} while(a[j]==b[j]);
+        if (j<0) break;
+        j_1=j;
+        while(j_1<=k-1){
+            a[j_1]=a[j_1]+1;
+            q=j_1;
+            while(q<k-1) {
+                a[q+1]=a[q];
+                q++;
+            }
+            q++;
+            j_1=q;
+        }
+    }
+
+    return choices;
+}
+
+#endif
diff --git a/contrib/vcflib-min/include/vcflib/ssw.hpp b/contrib/vcflib-min/include/vcflib/ssw.hpp
new file mode 100644
index 0000000..9e9e736
--- /dev/null
+++ b/contrib/vcflib-min/include/vcflib/ssw.hpp
@@ -0,0 +1,202 @@
+/*
+    vcflib C++ library for parsing and manipulating VCF files
+
+    Copyright © 2010-2020 Erik Garrison
+    Copyright © 2020      Pjotr Prins
+
+    This software is published under the MIT License. See the LICENSE file.
+*/
+
+/*
+ *  ssw.hpp
+ *
+ *  Created by Mengyao Zhao on 6/22/10.
+ *  Copyright 2010 Boston College. All rights reserved.
+ *	Last revision by Mengyao Zhao on 02/11/16.
+ *  Modified for inclusion in vcflib by Adam Novak on 2/14/17
+ *
+ */
+
+#ifndef VCFLIB_SSW_H
+#define VCFLIB_SSW_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#define SIMDE_ENABLE_NATIVE_ALIASES
+#include <simde/x86/sse2.h>
+
+namespace vcflib {
+
+#define MAPSTR "MIDNSHP=X"
+#ifndef BAM_CIGAR_SHIFT
+#define BAM_CIGAR_SHIFT 4
+#define BAM_CIGAR_SHIFT_DEFAULTED
+#endif
+
+
+/*!	@typedef	structure of the query profile	*/
+struct _profile;
+typedef struct _profile s_profile;
+
+/*!	@typedef	structure of the alignment result
+	@field	score1	the best alignment score
+	@field	score2	sub-optimal alignment score
+	@field	ref_begin1	0-based best alignment beginning position on reference;	ref_begin1 = -1 when the best alignment beginning
+						position is not available
+	@field	ref_end1	0-based best alignment ending position on reference
+	@field	read_begin1	0-based best alignment beginning position on read; read_begin1 = -1 when the best alignment beginning
+						position is not available
+	@field	read_end1	0-based best alignment ending position on read
+	@field	read_end2	0-based sub-optimal alignment ending position on read
+	@field	cigar	best alignment cigar; stored the same as that in BAM format, high 28 bits: length, low 4 bits: M/I/D (0/1/2);
+					cigar = 0 when the best alignment path is not available
+	@field	cigarLen	length of the cigar string; cigarLen = 0 when the best alignment path is not available
+*/
+typedef struct {
+	uint16_t score1;
+	uint16_t score2;
+	int32_t ref_begin1;
+	int32_t ref_end1;
+	int32_t	read_begin1;
+	int32_t read_end1;
+	int32_t ref_end2;
+	uint32_t* cigar;
+	int32_t cigarLen;
+} s_align;
+
+/*!	@function	Create the query profile using the query sequence.
+	@param	read	pointer to the query sequence; the query sequence needs to be numbers
+	@param	readLen	length of the query sequence
+	@param	mat	pointer to the substitution matrix; mat needs to be corresponding to the read sequence
+	@param	n	the square root of the number of elements in mat (mat has n*n elements)
+	@param	score_size	estimated Smith-Waterman score; if your estimated best alignment score is surely < 255 please set 0; if
+						your estimated best alignment score >= 255, please set 1; if you don't know, please set 2
+	@return	pointer to the query profile structure
+	@note	example for parameter read and mat:
+			If the query sequence is: ACGTATC, the sequence that read points to can be: 1234142
+			Then if the penalty for match is 2 and for mismatch is -2, the substitution matrix of parameter mat will be:
+			//A  C  G  T
+			  2 -2 -2 -2 //A
+			 -2  2 -2 -2 //C
+			 -2 -2  2 -2 //G
+			 -2 -2 -2  2 //T
+			mat is the pointer to the array {2, -2, -2, -2, -2, 2, -2, -2, -2, -2, 2, -2, -2, -2, -2, 2}
+*/
+s_profile* ssw_init (const int8_t* read, const int32_t readLen, const int8_t* mat, const int32_t n, const int8_t score_size);
+
+/*!	@function	Release the memory allocated by function ssw_init.
+	@param	p	pointer to the query profile structure
+*/
+void init_destroy (s_profile* p);
+
+// @function	ssw alignment.
+/*!	@function	Do Striped Smith-Waterman alignment.
+	@param	prof	pointer to the query profile structure
+	@param	ref	pointer to the target sequence; the target sequence needs to be numbers and corresponding to the mat parameter of
+				function ssw_init
+	@param	refLen	length of the target sequence
+	@param	weight_gapO	the absolute value of gap open penalty
+	@param	weight_gapE	the absolute value of gap extension penalty
+	@param	flag	bitwise FLAG; (from high to low) bit 5: when setted as 1, function ssw_align will return the best alignment
+					beginning position; bit 6: when setted as 1, if (ref_end1 - ref_begin1 < filterd && read_end1 - read_begin1
+					< filterd), (whatever bit 5 is setted) the function will return the best alignment beginning position and
+					cigar; bit 7: when setted as 1, if the best alignment score >= filters, (whatever bit 5 is setted) the function
+  					will return the best alignment beginning position and cigar; bit 8: when setted as 1, (whatever bit 5, 6 or 7 is
+ 					setted) the function will always return the best alignment beginning position and cigar. When flag == 0, only
+					the optimal and sub-optimal scores and the optimal alignment ending position will be returned.
+	@param	filters	score filter: when bit 7 of flag is setted as 1 and bit 8 is setted as 0, filters will be used (Please check the
+ 					decription of the flag parameter for detailed usage.)
+	@param	filterd	distance filter: when bit 6 of flag is setted as 1 and bit 8 is setted as 0, filterd will be used (Please check
+					the decription of the flag parameter for detailed usage.)
+	@param	maskLen	The distance between the optimal and suboptimal alignment ending position >= maskLen. We suggest to use
+					readLen/2, if you don't have special concerns. Note: maskLen has to be >= 15, otherwise this function will NOT
+					return the suboptimal alignment information. Detailed description of maskLen: After locating the optimal
+					alignment ending position, the suboptimal alignment score can be heuristically found by checking the second
+					largest score in the array that contains the maximal score of each column of the SW matrix. In order to avoid
+					picking the scores that belong to the alignments sharing the partial best alignment, SSW C library masks the
+					reference loci nearby (mask length = maskLen) the best alignment ending position and locates the second largest
+					score from the unmasked elements.
+	@return	pointer to the alignment result structure
+	@note	Whatever the parameter flag is setted, this function will at least return the optimal and sub-optimal alignment score,
+			and the optimal alignment ending positions on target and query sequences. If both bit 6 and 7 of the flag are setted
+			while bit 8 is not, the function will return cigar only when both criteria are fulfilled. All returned positions are
+			0-based coordinate.
+*/
+s_align* ssw_align (const s_profile* prof,
+					const int8_t* ref,
+					int32_t refLen,
+					const uint8_t weight_gapO,
+					const uint8_t weight_gapE,
+					const uint8_t flag,
+					const uint16_t filters,
+					const int32_t filterd,
+					const int32_t maskLen);
+
+/*!	@function	Release the memory allocated by function ssw_align.
+	@param	a	pointer to the alignment result structure
+*/
+void align_destroy (s_align* a);
+
+/*!	@function		Produce CIGAR 32-bit unsigned integer from CIGAR operation and CIGAR length
+	@param	length		length of CIGAR
+	@param	op_letter	CIGAR operation character ('M', 'I', etc)
+	@return			32-bit unsigned integer, representing encoded CIGAR operation and length
+*/
+static inline uint32_t to_cigar_int (uint32_t length, char op_letter)
+{
+	switch (op_letter) {
+		case 'M': /* alignment match (can be a sequence match or mismatch */
+		default:
+			return length << BAM_CIGAR_SHIFT;
+		case 'S': /* soft clipping (clipped sequences present in SEQ) */
+			return (length << BAM_CIGAR_SHIFT) | (4u);
+		case 'D': /* deletion from the reference */
+			return (length << BAM_CIGAR_SHIFT) | (2u);
+		case 'I': /* insertion to the reference */
+			return (length << BAM_CIGAR_SHIFT) | (1u);
+		case 'H': /* hard clipping (clipped sequences NOT present in SEQ) */
+			return (length << BAM_CIGAR_SHIFT) | (5u);
+		case 'N': /* skipped region from the reference */
+			return (length << BAM_CIGAR_SHIFT) | (3u);
+		case 'P': /* padding (silent deletion from padded reference) */
+			return (length << BAM_CIGAR_SHIFT) | (6u);
+		case '=': /* sequence match */
+			return (length << BAM_CIGAR_SHIFT) | (7u);
+		case 'X': /* sequence mismatch */
+			return (length << BAM_CIGAR_SHIFT) | (8u);
+	}
+	return (uint32_t)-1; // This never happens
+}
+
+
+/*!	@function		Extract CIGAR operation character from CIGAR 32-bit unsigned integer
+	@param	cigar_int	32-bit unsigned integer, representing encoded CIGAR operation and length
+	@return			CIGAR operation character ('M', 'I', etc)
+*/
+//char cigar_int_to_op (uint32_t cigar_int);
+static inline char cigar_int_to_op(uint32_t cigar_int) 
+{
+	return (cigar_int & 0xfU) > 8 ? 'M': MAPSTR[cigar_int & 0xfU];
+}
+
+
+/*!	@function		Extract length of a CIGAR operation from CIGAR 32-bit unsigned integer
+	@param	cigar_int	32-bit unsigned integer, representing encoded CIGAR operation and length
+	@return			length of CIGAR operation
+*/
+//uint32_t cigar_int_to_len (uint32_t cigar_int);
+static inline uint32_t cigar_int_to_len (uint32_t cigar_int)
+{
+	return cigar_int >> BAM_CIGAR_SHIFT;
+}
+
+// Clean up defines
+#undef MAPSTR
+#ifdef BAM_CIGAR_SHIFT_DEFAULTED
+#undef BAM_CIGAR_SHIFT
+#endif
+
+}
+
+#endif	// VCFLIB_SSW_H
diff --git a/contrib/vcflib-min/include/vcflib/ssw_cpp.hpp b/contrib/vcflib-min/include/vcflib/ssw_cpp.hpp
new file mode 100644
index 0000000..060a1cc
--- /dev/null
+++ b/contrib/vcflib-min/include/vcflib/ssw_cpp.hpp
@@ -0,0 +1,232 @@
+/*
+    vcflib C++ library for parsing and manipulating VCF files
+
+    Copyright © 2010-2020 Erik Garrison
+    Copyright © 2020      Pjotr Prins
+
+    This software is published under the MIT License. See the LICENSE file.
+*/
+
+#ifndef VCFLIB_COMPLETE_STRIPED_SMITH_WATERMAN_CPP_H_
+#define VCFLIB_COMPLETE_STRIPED_SMITH_WATERMAN_CPP_H_
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace vcflib {
+
+namespace StripedSmithWaterman {
+
+struct Alignment {
+  uint16_t sw_score;           // The best alignment score
+  uint16_t sw_score_next_best; // The next best alignment score
+  int32_t  ref_begin;          // Reference begin position of the best alignment
+  int32_t  ref_end;            // Reference end position of the best alignment
+  int32_t  query_begin;        // Query begin position of the best alignment
+  int32_t  query_end;          // Query end position of the best alignment
+  int32_t  ref_end_next_best;  // Reference end position of the next best alignment
+  int32_t  mismatches;         // Number of mismatches of the alignment
+  std::string cigar_string;    // Cigar string of the best alignment
+  std::vector<uint32_t> cigar; // Cigar stored in the BAM format
+                               //   high 28 bits: length
+			       //   low 4 bits: M/I/D/S/X (0/1/2/4/8);
+  void Clear() {
+    sw_score           = 0;
+    sw_score_next_best = 0;
+    ref_begin          = 0;
+    ref_end            = 0;
+    query_begin        = 0;
+    query_end          = 0;
+    ref_end_next_best  = 0;
+    mismatches         = 0;
+    cigar_string.clear();
+    cigar.clear();
+  };
+};
+
+struct Filter {
+  // NOTE: No matter the filter, those five fields of Alignment will be given anyway.
+  //       sw_score; sw_score_next_best; ref_end; query_end; ref_end_next_best.
+  // NOTE: Only need score of alignments, please set 'report_begin_position'
+  //       and 'report_cigar' false.
+
+  bool report_begin_position;    // Give ref_begin and query_begin.
+                                 //   If it is not set, ref_begin and query_begin are -1.
+  bool report_cigar;             // Give cigar_string and cigar.
+                                 //   report_begin_position is automatically TRUE.
+
+  // When *report_cigar* is true and alignment passes these two filters,
+  //   cigar_string and cigar will be given.
+  uint16_t score_filter;         // score >= score_filter
+  uint16_t distance_filter;      // ((ref_end - ref_begin) < distance_filter) &&
+                                 // ((query_end - read_begin) < distance_filter)
+
+  Filter()
+    : report_begin_position(true)
+    , report_cigar(true)
+    , score_filter(0)
+    , distance_filter(32767)
+  {};
+
+  Filter(const bool& pos, const bool& cigar, const uint16_t& score, const uint16_t& dis)
+    : report_begin_position(pos)
+    , report_cigar(cigar)
+    , score_filter(score)
+    , distance_filter(dis)
+    {};
+};
+
+class Aligner {
+ public:
+  // =========
+  // @function Construct an Aligner on default values.
+  //             The function will build the {A.C,G,T,N} aligner.
+  //             If you target for other character aligners, then please
+  //             use the other constructor and pass the corresponding matrix in.
+  // =========
+  Aligner(void);
+
+  // =========
+  // @function Construct an Aligner by assigning scores.
+  //             The function will build the {A.C,G,T,N} aligner.
+  //             If you target for other character aligners, then please
+  //             use the other constructor and pass the corresponding matrix in.
+  // =========
+  Aligner(const uint8_t& match_score,
+          const uint8_t& mismatch_penalty,
+	  const uint8_t& gap_opening_penalty,
+	  const uint8_t& gap_extending_penalty);
+
+  // =========
+  // @function Construct an Aligner by the specific matrixs.
+  // =========
+  Aligner(const int8_t* score_matrix,
+          const int&    score_matrix_size,
+          const int8_t* translation_matrix,
+	  const int&    translation_matrix_size);
+
+  ~Aligner(void);
+
+  // =========
+  // @function Build the reference sequence and thus make
+  //             Align(const char* query, s_align* alignment) function;
+  //             otherwise the reference should be given when aligning.
+  //           [NOTICE] If there exists a sequence, that one will be deleted
+  //                    and replaced.
+  // @param    seq    The reference bases;
+  //                  [NOTICE] It is not necessary null terminated.
+  // @param    length The length of bases will be be built.
+  // @return   The length of the built bases.
+  // =========
+  int SetReferenceSequence(const char* seq, const int& length);
+
+  void CleanReferenceSequence(void);
+
+  // =========
+  // @function Set penalties for opening and extending gaps
+  //           [NOTICE] The defaults are 3 and 1 respectively.
+  // =========
+  void SetGapPenalty(const uint8_t& opening, const uint8_t& extending) {
+    gap_opening_penalty_ = opening;
+    gap_extending_penalty_ = extending;
+  };
+
+  // =========
+  // @function Align the query againt the reference that is set by
+  //             SetReferenceSequence.
+  // @param    query     The query sequence.
+  // @param    filter    The filter for the alignment.
+  // @param    alignment The container contains the result.
+  // @return   True: succeed; false: fail.
+  // =========
+  bool Align(const char* query, const Filter& filter, Alignment* alignment) const;
+
+  // =========
+  // @function Align the query againt the reference.
+  //           [NOTICE] The reference won't replace the reference
+  //                      set by SetReferenceSequence.
+  // @param    query     The query sequence.
+  // @param    ref       The reference sequence.
+  //                     [NOTICE] It is not necessary null terminated.
+  // @param    ref_len   The length of the reference sequence.
+  // @param    filter    The filter for the alignment.
+  // @param    alignment The container contains the result.
+  // @return   True: succeed; false: fail.
+  // =========
+  bool Align(const char* query, const char* ref, const int& ref_len,
+             const Filter& filter, Alignment* alignment) const;
+
+  // @function Clear up all containers and thus the aligner is disabled.
+  //             To rebuild the aligner please use Build functions.
+  void Clear(void);
+
+  // =========
+  // @function Rebuild the aligner's ability on default values.
+  //           [NOTICE] If the aligner is not cleaned, rebuilding will fail.
+  // @return   True: succeed; false: fail.
+  // =========
+  bool ReBuild(void);
+
+  // =========
+  // @function Rebuild the aligner's ability by the specific matrixs.
+  //           [NOTICE] If the aligner is not cleaned, rebuilding will fail.
+  // @return   True: succeed; false: fail.
+  // =========
+  bool ReBuild(
+          const uint8_t& match_score,
+          const uint8_t& mismatch_penalty,
+	  const uint8_t& gap_opening_penalty,
+	  const uint8_t& gap_extending_penalty);
+
+  // =========
+  // @function Construct an Aligner by the specific matrixs.
+  //           [NOTICE] If the aligner is not cleaned, rebuilding will fail.
+  // @return   True: succeed; false: fail.
+  // =========
+  bool ReBuild(
+          const int8_t* score_matrix,
+          const int&    score_matrix_size,
+          const int8_t* translation_matrix,
+	  const int&    translation_matrix_size);
+
+ private:
+  int8_t* score_matrix_;
+  int     score_matrix_size_;
+  int8_t* translation_matrix_;
+
+  uint8_t match_score_;           // default: 2
+  uint8_t mismatch_penalty_;      // default: 2
+  uint8_t gap_opening_penalty_;   // default: 3
+  uint8_t gap_extending_penalty_; // default: 1
+
+  int8_t* translated_reference_;
+  int32_t reference_length_;
+
+  int TranslateBase(const char* bases, const int& length, int8_t* translated) const;
+  void SetAllDefault(void);
+  void BuildDefaultMatrix(void);
+  void ClearMatrices(void);
+
+  Aligner& operator= (const Aligner&);
+  Aligner (const Aligner&);
+}; // class Aligner
+
+
+// ================
+// inline functions
+// ================
+inline void Aligner::CleanReferenceSequence(void) {
+  if (reference_length_ == 0) return;
+
+  // delete the current buffer
+  if (reference_length_ > 1) delete [] translated_reference_;
+  else delete translated_reference_;
+
+  reference_length_ = 0;
+}
+} // namespace StripedSmithWaterman
+
+}
+
+#endif // VCFLIB_COMPLETE_STRIPED_SMITH_WATERMAN_CPP_H_
diff --git a/contrib/vcflib-min/src/Variant.cpp b/contrib/vcflib-min/src/Variant.cpp
new file mode 100644
index 0000000..fdc1934
--- /dev/null
+++ b/contrib/vcflib-min/src/Variant.cpp
@@ -0,0 +1,2908 @@
+/*
+    vcflib C++ library for parsing and manipulating VCF files
+
+    Copyright © 2010-2023 Erik Garrison
+    Copyright © 2020-2023 Pjotr Prins
+
+    This software is published under the MIT License. See the LICENSE file.
+*/
+
+#include "vcflib/Variant.h"
+#include "vcflib/cigar.hpp"
+// #include <utility>
+#include "vcflib/multichoose.h"
+#include <SmithWatermanGotoh.h>
+#include "vcflib/ssw_cpp.hpp"
+#include <regex>
+#include "vcflib/join.h"
+
+namespace vcflib {
+
+static char rev_arr [26] = {84, 66, 71, 68, 69, 70, 67, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 65,
+                           85, 86, 87, 88, 89, 90};
+
+std::string reverse_complement(const std::string& seq) {
+    // The old implementation of this function forgot to null-terminate its
+    // returned string. This implementation uses heavier-weight C++ stuff that
+    // may be slower but should ensure that that doesn't happen again.
+
+    if (seq.size() == 0) {
+        return seq;
+    }
+
+    string ret;
+    ret.reserve(seq.size());
+
+    std::transform(seq.rbegin(), seq.rend(), std::back_inserter(ret), [](char in) -> char {
+        bool lower_case = (in >= 'a' && in <= 'z');
+        if (lower_case) {
+            // Convert to upper case
+            in -= 32;
+        }
+        if (in < 'A' || in > 'Z') {
+            throw std::runtime_error("Out of range character " + std::to_string((uint8_t)in) + " in inverted sequence");
+        }
+        // Compute RC in terms of letter identity, and then lower-case if necessary.
+        return rev_arr[((int) in) - 'A'] + (lower_case ? 32 : 0);
+    });
+
+    return ret;
+}
+
+std::string toUpper(const std::string& seq) {
+    if (seq.size() == 0) {
+        return seq;
+    }
+
+    string ret;
+    ret.reserve(seq.size());
+
+    std::transform(seq.begin(), seq.end(), std::back_inserter(ret), [](char in) -> char {
+        // If it's lower-case, bring it down in value to upper-case.
+        return (in >= 'a' && in <= 'z') ? (in - 32) : in;
+    });
+
+    return ret;
+}
+
+
+bool allATGCN(const string& s, bool allowLowerCase){
+    if (allowLowerCase){
+       for (const auto c : s){
+            if (c != 'A' && c != 'a' &&
+                c != 'C' && c != 'c' &&
+                c != 'T' && c != 't' &&
+                c != 'G' && c != 'g' &&
+                c != 'N' && c != 'n'){
+                    return false;
+            }
+        }
+    }
+    else{
+        for (const auto c : s){
+            if (c != 'A' && c != 'C' && c != 'T' && c != 'G' && c != 'N'){
+                return false;
+            }
+        }
+
+    }
+    return true;
+}
+
+
+/*
+  Main VCF record parser
+*/
+
+void Variant::parse(string& line, bool parseSamples) {
+    // clean up potentially variable data structures because the record may get reused(!)
+    infoOrderedKeys.clear();
+    info.clear();
+    infoFlags.clear();
+    format.clear();
+    alt.clear();
+    alleles.clear();
+
+    // #CHROM  POS     ID      REF     ALT     QUAL    FILTER  INFO    FORMAT [SAMPLE1 .. SAMPLEN]
+    vector<string> fields = split(line, '\t');
+    if (fields.size() < 7) {
+        cerr << "broken VCF record (less than 7 fields)" << endl
+             << "Input line: " << line << endl;
+        exit(1);
+    }
+
+    sequenceName = fields.at(0);
+    char* end; // dummy variable for strtoll
+    position = strtoll(fields.at(1).c_str(), &end, 10);
+    id = fields.at(2);
+    ref = fields.at(3);
+    alt = split(fields.at(4), ","); // a comma-separated list of alternate alleles
+
+    // make a list of all (ref + alts) alleles, allele[0] = ref, alleles[1:] = alts
+    // add the ref allele ([0]), resize for the alt alleles, and then add the alt alleles
+    alleles.push_back(ref);
+    alleles.resize(alt.size()+1);
+    std::copy(alt.begin(), alt.end(), alleles.begin()+1);
+
+    // set up reverse lookup of allele index
+    altAlleleIndexes.clear();
+    int n = 0;
+    for (vector<string>::iterator a = alt.begin();
+            a != alt.end(); ++a, ++n) {
+        altAlleleIndexes[*a] = n;
+    }
+
+    convert(fields.at(5), quality);
+    filter = fields.at(6);
+    // Process the INFO fields
+    if (fields.size() > 7) {
+        vector<string> infofields = split(fields.at(7), ';');
+        for (const auto& field: infofields) {
+            if (field == ".") {
+                continue;
+            }
+            vector<string> kv = split(field, '='); // note that field gets split in place
+            const auto& key = kv.at(0);
+            if (kv.size() == 2) {
+                split(kv.at(1), ',', info[key]); // value gets split in place
+                infoOrderedKeys.push_back(key);
+            } else if (kv.size() == 1) {
+                infoFlags[key] = true;
+                infoOrderedKeys.push_back(key);
+            }
+            // malformed fields with double '=' are silently skipped
+        }
+    }
+    // check if we have samples specified
+    // and that we are supposed to parse them
+    if (parseSamples && fields.size() > 8) {
+        format = split(fields.at(8), ':');
+        // if the format changed, we have to rebuild the samples
+        if (fields.at(8) != lastFormat) {
+            samples.clear();
+            lastFormat = fields.at(8);
+        }
+        vector<string>::iterator sampleName = sampleNames.begin();
+        vector<string>::iterator sample = fields.begin() + 9;
+        for (; sample != fields.end() && sampleName != sampleNames.end();
+                ++sample, ++sampleName) {
+			string& name = *sampleName;
+
+			vector<string> samplefields = split(*sample, ':');
+        	vector<string>::iterator i = samplefields.begin();
+
+        	for (const auto& f : format) {
+        		if(i != samplefields.end()){
+        			samples[name][f] = split(*i, ',');
+        			++i;
+        		}
+        		else{
+        			std::vector<string> missing;
+        			missing.push_back(".");
+        			samples[name][f] = missing;
+        		}
+        	}
+                }
+
+        if (sampleName != sampleNames.end()) {
+            cerr << "error: more sample names in header than sample fields" << endl;
+            cerr << "samples: " << join(sampleNames, " ") << endl;
+            cerr << "line: " << line << endl;
+            exit(1);
+        }
+        if (sample != fields.end()) {
+            cerr << "error: more sample fields than samples listed in header" << endl;
+            cerr << "samples: " << join(sampleNames, " ") << endl;
+            cerr << "line: " << line << endl;
+            cerr << *sample << endl;
+            exit(1);
+        }
+    } else if (!parseSamples) {
+        originalLine = line;
+    }
+
+    //return true; // we should be catching exceptions...
+}
+
+bool Variant::hasSVTags() const{
+    bool found_svtype = !getSVTYPE().empty();
+    bool found_len = this->info.find("SVLEN") != this->info.end() || this->info.find("END") != this->info.end() || this->info.find("SPAN") != this->info.end();
+
+   return found_svtype && found_len;
+}
+
+  /*
+According to the VCF spec the ALT field can be use to indicate 'imprecise' structural
+variants.
+   */
+
+bool Variant::isSymbolicSV() const{
+
+    bool found_svtype = !getSVTYPE().empty();
+
+    bool ref_valid = allATGCN(this->ref);
+    bool alts_valid = true;
+    for (const auto& a : this->alt){
+        if (!allATGCN(a)){
+            alts_valid = false;
+        }
+    }
+
+    return (!ref_valid || !alts_valid) && (found_svtype);
+}
+
+string Variant::getSVTYPE(int altpos) const{
+
+    if (altpos > 0){
+        // TODO: Implement multi-alt SVs
+        return "";
+    }
+
+
+    if (this->info.find("SVTYPE") != this->info.end()){
+        if (altpos >= this->info.at("SVTYPE").size()) {
+            return "";
+        }
+        return this->info.at("SVTYPE")[altpos];
+    }
+
+    return "";
+};
+
+
+
+int Variant::getMaxReferencePos(){
+    if (this->canonical && this->info.find("END") != this->info.end()) {
+        // We are cannonicalized and must have a correct END
+
+        int end = 0;
+        for (const auto& s : this->info.at("END")){
+            // Get the latest one defined.
+            end = max(abs(stoi(s)), end);
+        }
+        // Convert to 0-based.
+        return end - 1;
+
+    }
+
+    if (!this->isSymbolicSV()){
+        // We don't necessarily have an END, but we don't need one
+        return this->zeroBasedPosition() + this->ref.length() - 1;
+    }
+
+    if (this->canonicalizable()){
+        // We aren't canonical, but we could be.
+        if (this->info.find("END") != this->info.end()){
+            // We have an END; blindly trust it
+            int end = 0;
+            for (const auto& s : this->info.at("END")){
+                // Get the latest one defined.
+                end = max(abs(stoi(s)), end);
+            }
+            // Convert to 0-based.
+            return end - 1;
+
+        }
+        else if (this->info.find("SVLEN") != this->info.end()){
+            // There's no endpoint, but we know an SVLEN.
+            // A negative SVLEN means a deletion, so if we find one we can say we delete that much.
+            int deleted = 0;
+            for (const auto& s : this->info.at("SVLEN")){
+                int alt_len = stoi(s);
+                if (alt_len > 0){
+                    // Not a deletion, so doesn't affect any ref bases
+                    continue;
+                }
+                deleted = max(-alt_len, deleted);
+            }
+
+            // The anchoring base at POS gets added in (because it isn't
+            // deleted) but then subtracted out (because we have to do that to
+            // match non-SV deletions). For insertions, deleted is 0 and we
+            // return 0-based POS. Inversions must have an END.
+            return this->zeroBasedPosition() + deleted;
+        }
+        else{
+            cerr << "Warning: insufficient length information for " << *this << endl;
+            return -1;
+        }
+    }
+    else {
+        cerr << "Warning: can't get end of non-canonicalizeable variant " << *this << endl;
+    }
+    return -1;
+}
+
+
+
+
+// To canonicalize a variant, we need either both REF and ALT seqs filled in
+// or SVTYPE and SVLEN or END or SPAN or SEQ sufficient to define the variant.
+bool Variant::canonicalizable(){
+    bool pre_canon = allATGCN(this->ref);
+
+    for (auto& a : this->alt){
+        if (!allATGCN(a)){
+            pre_canon = false;
+        }
+    }
+
+    if (pre_canon){
+        // It came in in a fully specified way.
+        // TODO: ideally, we'd check to make sure ref/alt lengths, svtypes, and ends line up right here.
+        return true;
+    }
+
+    string svtype = getSVTYPE();
+
+    if (svtype.empty()){
+        // We have no SV type, so we can't interpret things.
+        return false;
+    }
+
+    // Check the tags
+    bool has_len = this->info.count("SVLEN") && !this->info.at("SVLEN").empty();
+    bool has_seq = this->info.count("SEQ") && !this->info.at("SEQ").empty();
+    bool has_span = this->info.count("SPAN") && !this->info.at("SPAN").empty();
+    bool has_end = this->info.count("END") && !this->info.at("END").empty();
+
+
+    if (svtype == "INS"){
+        // Insertions need a SEQ, SVLEN, or SPAN
+        return has_seq || has_len || has_span;
+    }
+    else if (svtype == "DEL"){
+        // Deletions need an SVLEN, SPAN, or END
+        return has_len || has_span || has_end;
+    }
+    else if (svtype == "INV"){
+        // Inversions need a SPAN or END
+        return has_span || has_end;
+    }
+    else{
+        // Other SV types are unsupported
+        // TODO: DUP
+        return false;
+    }
+}
+
+bool Variant::canonicalize(FastaReference& fasta_reference, vector<FastaReference*> insertions, bool place_seq, int min_size){
+
+    // Nobody should call this without checking
+    assert(canonicalizable());
+
+    // Nobody should call this twice
+    assert(!this->canonical);
+
+    // Find where the inserted sequence can come from for insertions
+    bool do_external_insertions = !insertions.empty();
+    FastaReference* insertion_fasta;
+    if (do_external_insertions){
+        insertion_fasta = insertions[0];
+    }
+
+    bool ref_valid = allATGCN(ref);
+
+    if (!ref_valid && !place_seq){
+        // If the reference is invalid, and we aren't allowed to change the ref sequence,
+        // we can't canonicalize the variant.
+        return false;
+    }
+
+    // Check the alts to see if they are not symbolic
+    vector<bool> alt_i_atgcn (alt.size());
+    for (int i = 0; i < alt.size(); ++i){
+        alt_i_atgcn[i] = allATGCN(alt[i]);
+    }
+
+    // Only allow single-alt variants
+    bool single_alt = alt.size() == 1;
+    if (!single_alt){
+        // TODO: this will need to be remove before supporting multiple alleles
+        cerr << "Warning: multiple ALT alleles not yet allowed for SVs" << endl;
+        return false;
+    }
+
+    // Fill in the SV tags
+    string svtype = getSVTYPE();
+    bool has_len = this->info.count("SVLEN") && !this->info.at("SVLEN").empty();
+    bool has_seq = this->info.count("SEQ") && !this->info.at("SEQ").empty();
+    bool has_span = this->info.count("SPAN") && !this->info.at("SPAN").empty();
+    bool has_end = this->info.count("END") && !this->info.at("END").empty();
+
+    // Where is the end, or where should it be?
+    long info_end = 0;
+    if (has_end) {
+        // Get the END from the tag
+        info_end = stol(this->info.at("END")[0]);
+    }
+    else if(ref_valid && !place_seq) {
+        // Get the END from the reference sequence, which is ready.
+        info_end = this->position + this->ref.length() - 1;
+    }
+    else if ((svtype == "DEL" || svtype == "INV") && has_span) {
+        // For deletions and inversions, we can get the END from the SPAN
+        info_end = this->position + abs(stol(this->info.at("SPAN")[0]));
+    }
+    else if (svtype == "DEL" && has_len) {
+        // For deletions, we can get the END from the SVLEN
+        info_end = this->position + abs(stol(this->info.at("SVLEN")[0]));
+    }
+    else if (svtype == "INS"){
+        // For insertions, END is just POS if not specified
+        info_end = this->position;
+    }
+    else{
+        cerr << "Warning: could not set END info " << *this << endl;
+        return false;
+    }
+
+    // Commit back the END
+    this->info["END"].resize(1);
+    this->info["END"][0] = to_string(info_end);
+    has_end = true;
+
+    // What is the variant length change?
+    // We store it as absolute value
+    long info_len = 0;
+    if (has_len){
+        // Get the SVLEN from the tag
+        info_len = abs(stol(this->info.at("SVLEN")[0]));
+    }
+    else if ((svtype == "INS" || svtype == "DEL") && has_span){
+        info_len = abs(stol(this->info.at("SPAN")[0]));
+    }
+    else if (svtype == "DEL"){
+        // We always have the end by now
+        // Deletion ends give you length change
+        info_len = info_end - this->position;
+    }
+    else if (svtype == "INV"){
+        // Inversions have 0 length change unless otherwise specified.
+        info_len = 0;
+    }
+    else if (svtype == "INS" && has_seq) {
+        // Insertions can let us pick it up from the SEQ tag
+        info_len = this->info.at("SEQ").at(0).size();
+    }
+    else{
+        cerr << "Warning: could not set SVLEN info " << *this << endl;
+        return false;
+    }
+
+    // Commit the SVLEN back
+    if (svtype == "DEL"){
+        // Should be saved as negative
+        this->info["SVLEN"].resize(1);
+        this->info["SVLEN"][0] = to_string(-info_len);
+    }
+    else{
+        // Should be saved as positive
+        this->info["SVLEN"].resize(1);
+        this->info["SVLEN"][0] = to_string(info_len);
+    }
+    // Now the length change is known
+    has_len = true;
+
+    // We also compute a span
+    long info_span = 0;
+    if (has_span){
+        // Use the specified span
+        info_span = abs(stol(this->info.at("SVLEN")[0]));
+    }
+    else if (svtype == "INS" || svtype == "DEL"){
+        // has_len is always true here
+        // Insertions and deletions let us determine the span from the length change, unless they are complex.
+        info_span = info_len;
+    }
+    else if (svtype == "INV"){
+        // has_end is always true here
+        // Inversion span is start to end
+        info_span = info_end - this->position;
+    }
+    else{
+        cerr << "Warning: could not set SPAN info " << *this << endl;
+        return false;
+    }
+
+    // Commit the SPAN back
+    this->info["SPAN"].resize(1);
+    this->info["SPAN"][0] = to_string(info_span);
+    // Now the span change is known
+    has_span = true;
+
+    if (info_end < this->position) {
+        cerr << "Warning: SV END is before POS [canonicalize] " <<
+        *this << endl << "END: " << info_end << "  " << "POS: " << this->position << endl;
+        return false;
+    }
+
+    if (has_seq) {
+        // Force the SEQ to upper case, if already present
+        this->info["SEQ"].resize(1);
+        this->info["SEQ"][0] = toUpper(this->info["SEQ"][0]);
+    }
+
+    // Set the other necessary SV Tags (SVTYPE, SEQ (if insertion))
+    // Also check for agreement in the position tags
+    if (svtype == "INS"){
+        if (info_end != this->position){
+            cerr << "Warning: insertion END and POS do not agree (complex insertions not canonicalizeable) [canonicalize] " <<
+            *this << endl << "END: " << info_end << "  " << "POS: " << this->position << endl;
+
+            if (info_end == this->position + info_len) {
+                // We can probably guess what they meant here.
+                cerr << "Warning: VCF writer incorrecty produced END = POS + SVLEN for an insertion. Fixing END to POS." << endl;
+                info_end = this->position;
+                this->info["END"][0] = to_string(info_end);
+            } else {
+                return false;
+            }
+        }
+
+        if (info_len != info_span){
+            cerr << "Warning: insertion SVLEN and SPAN do not agree (complex insertions not canonicalizeable) [canonicalize] " <<
+            *this << endl << "SVLEN: " << info_len << "  " << "SPAN: " << info_span << endl;
+            return false;
+        }
+
+        if (has_seq && allATGCN(this->info.at("SEQ")[0]) && this->info.at("SEQ")[0].size() != info_len){
+            cerr << "Warning: insertion SVLEN and SEQ do not agree (complex insertions not canonicalizeable) [canonicalize] " <<
+            *this << endl << "SVLEN: " << info_len << "  " << "SEQ length: " << this->info.at("SEQ")[0].size() << endl;
+            return false;
+        }
+
+        // Set REF
+        string ref_base = toUpper(fasta_reference.getSubSequence(this->sequenceName, this->zeroBasedPosition(), 1));
+        if (place_seq){
+            this->ref.assign(ref_base);
+        }
+
+        if (has_seq &&
+                 alt[0] != this->info.at("SEQ")[0] &&
+                 allATGCN(this->info.at("SEQ")[0])){
+            // Try to remove prepended ref sequence, assuming it's left-aligned
+            string s = this->alt[0];
+            s = toUpper(s.substr(this->ref.length()));
+            if (s != this->info.at("SEQ")[0] && !place_seq){
+                cerr << "Warning: INS sequence in alt field does not match SEQ tag" << endl <<
+                this->alt[0] << " " << this->info.at("SEQ")[0] << endl;
+                return false;
+            }
+            if (place_seq){
+                this->alt[0].assign( ref_base + this->info.at("SEQ")[0] );
+            }
+
+        }
+        else if (alt_i_atgcn[0] && !has_seq){
+            string s = this->alt[0];
+            s = toUpper(s.substr(this->ref.length()));
+            this->info["SEQ"].resize(1);
+            this->info.at("SEQ")[0].assign(s);
+
+            if (s.size() != info_len){
+                cerr << "Warning: insertion SVLEN and added bases do not agree (complex insertions not canonicalizeable) [canonicalize] " <<
+                *this << endl << "SVLEN: " << info_len << "  " << "added bases: " << s.size() << endl;
+                return false;
+            }
+
+        }
+        else if (alt[0][0] == '<' && do_external_insertions){
+
+            string ins_seq;
+            string seq_id = alt[0].substr(1, alt[0].size() - 2);
+
+            if (insertion_fasta->index->find(seq_id) != insertion_fasta->index->end()){
+                ins_seq = toUpper(insertion_fasta->getSequence(seq_id));
+                if (allATGCN(ins_seq)){
+                    this->info["SEQ"].resize(1);
+                    this->info["SEQ"][0].assign(ins_seq);
+                    if (place_seq){
+                        this->alt[0].assign(ref_base + ins_seq);
+                    }
+                }
+                else {
+                    cerr << "Warning: Loaded invalid alt sequence for: " << *this << endl;
+                    return false;
+                }
+
+                if (ins_seq.size() != info_len){
+                    cerr << "Warning: insertion SVLEN and FASTA do not agree (complex insertions not canonicalizeable) [canonicalize] " <<
+                    *this << endl << "SVLEN: " << info_len << "  " << "FASTA bases: " << ins_seq.size() << endl;
+                    return false;
+                }
+            }
+            else{
+                cerr << "Warning: could not locate alt sequence for: " << *this << endl;
+                return false;
+            }
+
+        }
+        else{
+            cerr << "Warning: could not set SEQ [canonicalize]. " << *this << endl;
+            return false;
+        }
+    }
+    else if (svtype == "DEL"){
+        // Note that info_len has been abs'd and is always positive
+        if (this->position + info_len != info_end){
+            cerr << "Warning: deletion END and SVLEN do not agree [canonicalize] " << *this << endl <<
+            "END: " << info_end << "  " << "SVLEN: " << info_len << endl;
+            return false;
+        }
+
+        if (this->position + info_span != info_end){
+            cerr << "Warning: deletion END and SPAN do not agree [canonicalize] " << *this << endl <<
+            "END: " << info_end << "  " << "SPAN: " << info_span << endl;
+            return false;
+        }
+
+        if (info_end > fasta_reference.sequenceLength(this->sequenceName)) {
+            cerr << "Warning: deletion END is past end of sequence [canonicalize] " << *this << endl <<
+            "END: " << info_end << "  " << "length: " << fasta_reference.sequenceLength(this->sequenceName) << endl;
+            return false;
+        }
+
+        // Set REF
+        if (place_seq){
+            string del_seq = toUpper(fasta_reference.getSubSequence(this->sequenceName, this->zeroBasedPosition(), info_len + 1));
+            string ref_base = toUpper(fasta_reference.getSubSequence(this->sequenceName, this->zeroBasedPosition(), 1));
+            this->ref.assign( del_seq );
+            this->alt[0].assign( ref_base );
+        }
+    }
+    else if (svtype == "INV"){
+        if (this->position + info_span != info_end){
+            cerr << "Warning: inversion END and SPAN do not agree [canonicalize] " << *this << endl <<
+            "END: " << info_end << "  " << "SPAN: " << info_span << endl;
+            return false;
+        }
+
+        if (info_len != 0){
+            cerr << "Warning: inversion SVLEN specifies nonzero length change (complex inversions not canonicalizeable) [canonicalize] " <<
+            *this << endl << "SVLEN: " << info_len << endl;
+
+            if (info_end == this->position + info_len) {
+                // We can probably guess what they meant here.
+                cerr << "Warning: VCF writer incorrecty produced END = POS + SVLEN for an inversion. Fixing SVLEN to 0." << endl;
+                info_len = 0;
+                this->info["SVLEN"][0] = to_string(info_len);
+            } else {
+                return false;
+            }
+        }
+
+        if (info_end > fasta_reference.sequenceLength(this->sequenceName)) {
+            cerr << "Warning: inversion END is past end of sequence [canonicalize] " << *this << endl <<
+            "END: " << info_end << "  " << "length: " << fasta_reference.sequenceLength(this->sequenceName) << endl;
+            return false;
+        }
+
+        if (place_seq){
+            string ref_seq = toUpper(fasta_reference.getSubSequence(this->sequenceName, this->zeroBasedPosition(), info_span + 1));
+            // Note that inversions still need an anchoring left base at POS
+            string inv_seq = ref_seq.substr(0, 1) + reverse_complement(ref_seq.substr(1));
+            this->ref.assign(ref_seq);
+            this->alt[0].assign(inv_seq);
+        }
+
+    }
+    else{
+        cerr << "Warning: invalid SV type [canonicalize]:" << *this << endl;
+        return false;
+    }
+
+
+    this->updateAlleleIndexes();
+
+    // Check for harmony between ref / alt / tags
+    if (this->position > stol(this->info.at("END").at(0))){
+        cerr << "Warning: position > END. Possible reference genome mismatch." << endl;
+        return false;
+    }
+
+    if (svtype == "INS"){
+        assert(!this->info.at("SEQ")[0].empty());
+    }
+
+    this->canonical = true;
+    return true;
+}
+
+void Variant::setVariantCallFile(VariantCallFile& v) {
+    sampleNames = v.sampleNames;
+    outputSampleNames = v.sampleNames;
+    vcf = &v;
+}
+
+void Variant::setVariantCallFile(VariantCallFile* v) {
+    sampleNames = v->sampleNames;
+    outputSampleNames = v->sampleNames;
+    vcf = v;
+}
+
+ostream& operator<<(ostream& out, VariantFieldType type) {
+    switch (type) {
+        case FIELD_INTEGER:
+            out << "integer";
+            break;
+        case FIELD_FLOAT:
+            out << "float";
+            break;
+        case FIELD_BOOL:
+            out << "bool";
+            break;
+        case FIELD_STRING:
+            out << "string";
+            break;
+        default:
+            out << "unknown";
+            break;
+    }
+    return out;
+}
+
+VariantFieldType typeStrToVariantFieldType(string& typeStr) {
+    if (typeStr == "Integer") {
+        return FIELD_INTEGER;
+    } else if (typeStr == "Float") {
+        return FIELD_FLOAT;
+    } else if (typeStr == "Flag") {
+        return FIELD_BOOL;
+    } else if (typeStr == "String") {
+        return FIELD_STRING;
+    } else {
+        return FIELD_UNKNOWN;
+    }
+}
+
+VariantFieldType Variant::infoType(const string& key) {
+    map<string, VariantFieldType>::iterator s = vcf->infoTypes.find(key);
+    if (s == vcf->infoTypes.end()) {
+        if (key == "FILTER") { // hack to use FILTER as an "info" field (why the hack?)
+            return FIELD_STRING;
+        }
+        if (key == "QUAL") { // hack to use QUAL as an "info" field
+            return FIELD_INTEGER;
+        }
+        cerr << "no info field " << key << endl;
+        exit(1);
+    } else {
+        return s->second;
+    }
+}
+
+    VariantFieldType Variant::formatType(const string& key) {
+        map<string, VariantFieldType>::iterator s = vcf->formatTypes.find(key);
+        if (s == vcf->formatTypes.end()) {
+            cerr << "no format field " << key << endl;
+            exit(1);
+        } else {
+            return s->second;
+        }
+    }
+
+    bool Variant::getInfoValueBool(const string& key, int index) {
+        map<string, VariantFieldType>::iterator s = vcf->infoTypes.find(key);
+        if (s == vcf->infoTypes.end()) {
+            cerr << "no info field " << key << endl;
+            exit(1);
+        } else {
+            int count = vcf->infoCounts[key];
+            // XXX TODO, fix for Genotype variants...
+            if (count != ALLELE_NUMBER) {
+                index = 0;
+            }
+            if (index == INDEX_NONE) {
+                if (count != 1) {
+                    cerr << "no field index supplied and field count != 1" << endl;
+                    exit(1);
+                } else {
+                    index = 0;
+                }
+            }
+            VariantFieldType type = s->second;
+            if (type == FIELD_BOOL) {
+                map<string, bool>::iterator b = infoFlags.find(key);
+                if (b == infoFlags.end())
+                    return false;
+                else
+                    return true;
+            } else {
+                cerr << "not flag type " << key << endl;
+                exit(1);
+            }
+        }
+    }
+
+    string Variant::getInfoValueString(const string& key, int index) {
+        map<string, VariantFieldType>::iterator s = vcf->infoTypes.find(key);
+        if (s == vcf->infoTypes.end()) {
+            if (key == "FILTER") {
+              return filter;
+            }
+            cerr << "no info field " << key << endl;
+            exit(1);
+        } else {
+            int count = vcf->infoCounts[key];
+            // XXX TODO, fix for Genotype variants...
+            if (count != ALLELE_NUMBER) {
+                index = 0;
+            }
+            if (index == INDEX_NONE) {
+                if (count != 1) {
+                    cerr << "no field index supplied and field count != 1" << endl;
+                    exit(1);
+                } else {
+                    index = 0;
+                }
+            }
+            VariantFieldType type = s->second;
+            if (type == FIELD_STRING) {
+                map<string, vector<string> >::iterator b = info.find(key);
+                if (b == info.end())
+                    return "";
+                return b->second.at(index);
+            } else {
+                cerr << "not string type " << key << endl;
+                return "";
+            }
+        }
+    }
+
+    double Variant::getInfoValueFloat(const string& key, int index) {
+        map<string, VariantFieldType>::iterator s = vcf->infoTypes.find(key);
+        if (s == vcf->infoTypes.end()) {
+            if (key == "QUAL") {
+                return quality;
+            }
+            cerr << "no info field " << key << endl;
+            exit(1);
+        } else {
+            int count = vcf->infoCounts[key];
+            // XXX TODO, fix for Genotype variants...
+            if (count != ALLELE_NUMBER) {
+                index = 0;
+            }
+            if (index == INDEX_NONE) {
+                if (count != 1) {
+                    cerr << "no field index supplied and field count != 1" << endl;
+                    exit(1);
+                } else {
+                    index = 0;
+                }
+            }
+            VariantFieldType type = s->second;
+            if (type == FIELD_FLOAT || type == FIELD_INTEGER) {
+                map<string, vector<string> >::iterator b = info.find(key);
+                if (b == info.end())
+                    return false;
+                double r;
+                if (!convert(b->second.at(index), r)) {
+                    cerr << "could not convert field " << key << "=" << b->second.at(index) << " to " << type << endl;
+                    exit(1);
+                }
+                return r;
+            } else {
+                cerr << "unsupported type for variant record " << type << endl;
+                exit(1);
+            }
+        }
+    }
+
+    int Variant::getNumSamples(void) {
+        return sampleNames.size();
+    }
+
+    int Variant::getNumValidGenotypes(void) {
+        int valid_genotypes = 0;
+        map<string, map<string, vector<string> > >::const_iterator s     = samples.begin();
+        map<string, map<string, vector<string> > >::const_iterator sEnd  = samples.end();
+        for (; s != sEnd; ++s) {
+            map<string, vector<string> > sample_info = s->second;
+            if (sample_info["GT"].front() != "./.") {
+                valid_genotypes++;
+            }
+        }
+        return valid_genotypes;
+    }
+
+    bool Variant::getSampleValueBool(const string& key, string& sample, int index) {
+        map<string, VariantFieldType>::iterator s = vcf->formatTypes.find(key);
+        if (s == vcf->infoTypes.end()) {
+            cerr << "no info field " << key << endl;
+            exit(1);
+        } else {
+            int count = vcf->formatCounts[key];
+            // XXX TODO, fix for Genotype variants...
+            if (count != ALLELE_NUMBER) {
+                index = 0;
+            }
+            if (index == INDEX_NONE) {
+                if (count != 1) {
+                    cerr << "no field index supplied and field count != 1" << endl;
+                    exit(1);
+                } else {
+                    index = 0;
+                }
+            }
+            VariantFieldType type = s->second;
+            map<string, vector<string> >& sampleData = samples[sample];
+            if (type == FIELD_BOOL) {
+                map<string, vector<string> >::iterator b = sampleData.find(key);
+                if (b == sampleData.end())
+                    return false;
+                else
+                    return true;
+            } else {
+                cerr << "not bool type " << key << endl;
+                exit(1);
+            }
+        }
+    }
+
+    string Variant::getSampleValueString(const string& key, string& sample, int index) {
+        map<string, VariantFieldType>::iterator s = vcf->formatTypes.find(key);
+        if (s == vcf->infoTypes.end()) {
+            cerr << "no info field " << key << endl;
+            exit(1);
+        } else {
+            int count = vcf->formatCounts[key];
+            // XXX TODO, fix for Genotype variants...
+            if (count != ALLELE_NUMBER) {
+                index = 0;
+            }
+            if (index == INDEX_NONE) {
+                if (count != 1) {
+                    cerr << "no field index supplied and field count != 1" << endl;
+                    exit(1);
+                } else {
+                    index = 0;
+                }
+            }
+            VariantFieldType type = s->second;
+            map<string, vector<string> >& sampleData = samples[sample];
+            if (type == FIELD_STRING) {
+                map<string, vector<string> >::iterator b = sampleData.find(key);
+                if (b == sampleData.end()) {
+                    return "";
+                } else {
+                    return b->second.at(index);
+                }
+            } else {
+                cerr << "not string type " << key << endl;
+                exit(1);
+            }
+        }
+    }
+
+    double Variant::getSampleValueFloat(const string& key, string& sample, int index) {
+        map<string, VariantFieldType>::iterator s = vcf->formatTypes.find(key);
+        if (s == vcf->infoTypes.end()) {
+            cerr << "no info field " << key << endl;
+            exit(1);
+        } else {
+            // XXX TODO wrap this with a function call
+            int count = vcf->formatCounts[key];
+            // XXX TODO, fix for Genotype variants...
+            if (count != ALLELE_NUMBER) {
+                index = 0;
+            }
+            if (index == INDEX_NONE) {
+                if (count != 1) {
+                    cerr << "no field index supplied and field count != 1" << endl;
+                    exit(1);
+                } else {
+                    index = 0;
+                }
+            }
+            VariantFieldType type = s->second;
+            map<string, vector<string> >& sampleData = samples[sample];
+            if (type == FIELD_FLOAT || type == FIELD_INTEGER) {
+                map<string, vector<string> >::iterator b = sampleData.find(key);
+                if (b == sampleData.end())
+                    return false;
+                double r;
+                if (!convert(b->second.at(index), r)) {
+                    cerr << "could not convert field " << key << "=" << b->second.at(index) << " to " << type << endl;
+                    exit(1);
+                }
+                return r;
+            } else {
+                cerr << "unsupported type for sample " << type << endl;
+                exit(1);
+            }
+        }
+    }
+
+    bool Variant::getValueBool(const string& key, string& sample, int index) {
+        if (sample.empty()) { // an empty sample name means
+            return getInfoValueBool(key, index);
+        } else {
+            return getSampleValueBool(key, sample, index);
+        }
+    }
+
+    double Variant::getValueFloat(const string& key, string& sample, int index) {
+        if (sample.empty()) { // an empty sample name means
+            return getInfoValueFloat(key, index);
+        } else {
+            return getSampleValueFloat(key, sample, index);
+        }
+    }
+
+    string Variant::getValueString(const string& key, string& sample, int index) {
+        if (sample.empty()) { // an empty sample name means
+            return getInfoValueString(key, index);
+        } else {
+            return getSampleValueString(key, sample, index);
+        }
+    }
+
+    int Variant::getAltAlleleIndex(const string& allele) {
+        map<string, int>::iterator f = altAlleleIndexes.find(allele);
+        if (f == altAlleleIndexes.end()) {
+            cerr << "no such allele \'" << allele << "\' in record " << sequenceName << ":" << position << endl;
+            exit(1);
+        } else {
+            return f->second;
+        }
+    }
+
+    void Variant::addFilter(const string& tag) {
+        if (filter == "" || filter == ".")
+            filter = tag;
+        else
+            filter += "," + tag;
+    }
+
+    void Variant::addFormatField(const string& key) {
+        bool hasTag = false;
+        for (const auto& t : format) {
+            if (t == key) {
+                hasTag = true;
+                break;
+            }
+        }
+        if (!hasTag) {
+            format.push_back(key);
+        }
+    }
+
+    void Variant::printAlt(ostream& out) const {
+        for (vector<string>::const_iterator i = alt.begin(); i != alt.end(); ++i) {
+            out << *i;
+            // add a comma for all but the last alternate allele
+            if (i != (alt.end() - 1)) out << ",";
+        }
+    }
+
+    void Variant::printAlleles(ostream& out) const {
+        for (vector<string>::const_iterator i = alleles.begin(); i != alleles.end(); ++i) {
+            out << *i;
+            // add a comma for all but the last alternate allele
+            if (i != (alleles.end() - 1)) out << ",";
+        }
+    }
+
+    /*
+      This is the main outputter of VCF records/lines
+    */
+    ostream& operator<<(ostream& out, Variant& var) {
+        // ensure there are no empty fields
+        if (var.sequenceName.empty()) var.sequenceName = ".";
+        if (var.id.empty()) var.id = ".";
+        if (var.ref.empty()) var.ref = ".";
+        if (var.alt.empty()) var.alt.push_back(".");
+        if (var.filter.empty()) var.filter = ".";
+
+        out << var.sequenceName << "\t"
+            << var.position << "\t"
+            << var.id << "\t"
+            << var.ref << "\t";
+        // report the list of alternate alleles.
+        var.printAlt(out);
+
+        out << "\t"
+            << var.quality << "\t"
+            << var.filter << "\t";
+        if (var.info.empty() && var.infoFlags.empty()) {
+            out << ".";
+        } else {
+            // We want to display the info fields in the original
+            // order.  Because the actual info list may have been
+            // modified since the record was read, we need to recreate
+            // a valid ordered key list.
+            map<string,bool> lookup_keys; // for quick lookup in 2nd step
+            vector<string> ordered_keys, missing_keys;  // the output list
+            // first lookup the keys that appear both in infoOrdered keys
+            // and the info field:
+            for (const auto& name: var.infoOrderedKeys)
+            {
+                lookup_keys[name] = true;
+                if (!var.info[name].empty()) ordered_keys.push_back(name);
+                if (var.infoFlags[name]) ordered_keys.push_back(name);
+            };
+            // next add the keys that are not in the original list:
+            for (const auto& [name1, value]: var.info)
+                if (!lookup_keys[name1]) missing_keys.push_back(name1);
+            for (const auto& [name2, value]: var.infoFlags)
+                if (lookup_keys[name2] == false) missing_keys.push_back(name2);
+
+            // append sorted missing keys
+            std::sort(missing_keys.begin(), missing_keys.end());
+
+            ordered_keys.insert(ordered_keys.end(), missing_keys.begin(), missing_keys.end());
+            // output the ordered info fields
+            string s = "";
+            for (const auto& name: ordered_keys) {
+                const auto& value = var.info[name];
+                if (!value.empty()) {
+                    s += name + "=" + join(value, ",") + ";" ;
+                } else {
+                    const auto infoflag = var.infoFlags[name];
+                    if (infoflag == true)
+                        s += name + ";";
+                }
+            }
+            auto len = s.length();
+            if (len)
+                out << s.substr(0, len-1); // chop s1.substr(0, i-1);
+        }
+        if (!var.format.empty()) {
+            out << "\t";
+            string format = "";
+            for (const auto& f: var.format) {
+                format += f + ":";
+            }
+            auto len = format.length();
+            if (len)
+                out << format.substr(0, len-1); // chop s1.substr(0, i-1);
+            for (const auto& s: var.outputSampleNames) {
+                out << "\t";
+                const auto sampleItr = var.samples.find(s);
+                if (sampleItr == var.samples.end()) {
+                    out << ".";
+                } else {
+                    const map<string, vector<string> >& sample = sampleItr->second;
+                    if (sample.empty()) {
+                        out << ".";
+                    } else {
+                        for (vector<string>::iterator f = var.format.begin(); f != var.format.end(); ++f) {
+                            const auto g = sample.find(*f);
+                            out << ((f == var.format.begin()) ? "" : ":");
+                            if (g != sample.end() && !g->second.empty()) {
+                                out << join(g->second, ",");
+                            } else {
+                                out << ".";
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return out;
+    }
+
+    void Variant::setOutputSampleNames(const vector<string>& samplesToOutput) {
+        outputSampleNames = samplesToOutput;
+    }
+
+
+// shunting yard algorithm
+    void infixToPrefix(queue<RuleToken> tokens, queue<RuleToken>& prefixtokens) {
+        stack<RuleToken> ops;
+        while (!tokens.empty()) {
+            RuleToken& token = tokens.front();
+            if (isOperator(token)) {
+                //cerr << "found operator " << token.value << endl;
+                while (ops.size() > 0 && isOperator(ops.top())
+                       && (   (isLeftAssociative(token)  && priority(token) <= priority(ops.top()))
+                              || (isRightAssociative(token) && priority(token) <  priority(ops.top())))) {
+                    prefixtokens.push(ops.top());
+                    ops.pop();
+                }
+                ops.push(token);
+            } else if (isLeftParenthesis(token)) {
+                //cerr << "found paran " << token.value << endl;
+                ops.push(token);
+            } else if (isRightParenthesis(token)) {
+                //cerr << "found paran " << token.value << endl;
+                while (ops.size() > 0 && !isLeftParenthesis(ops.top())) {
+                    prefixtokens.push(ops.top());
+                    ops.pop();
+                }
+                if (ops.size() == 0) {
+                    cerr << "error: mismatched parentheses" << endl;
+                    exit(1);
+                }
+                if (isLeftParenthesis(ops.top())) {
+                    ops.pop();
+                }
+            } else {
+                //cerr << "found operand " << token.value << endl;
+                prefixtokens.push(token);
+            }
+            tokens.pop();
+        }
+        while (ops.size() > 0) {
+            if (isRightParenthesis(ops.top()) || isLeftParenthesis(ops.top())) {
+                cerr << "error: mismatched parentheses" << endl;
+                exit(1);
+            }
+            prefixtokens.push(ops.top());
+            ops.pop();
+        }
+    }
+
+    RuleToken::RuleToken(const string& tokenstr, map<string, VariantFieldType>& variables) {
+        isVariable = false;
+        if (tokenstr == "!") {
+            type = RuleToken::NOT_OPERATOR;
+        } else if (tokenstr == "&") {
+            type = RuleToken::AND_OPERATOR;
+        } else if (tokenstr == "|") {
+            type = RuleToken::OR_OPERATOR;
+        } else if (tokenstr == "+") {
+            type = RuleToken::ADD_OPERATOR;
+        } else if (tokenstr == "-") {
+            type = RuleToken::SUBTRACT_OPERATOR;
+        } else if (tokenstr == "*") {
+            type = RuleToken::MULTIPLY_OPERATOR;
+        } else if (tokenstr == "/") {
+            type = RuleToken::DIVIDE_OPERATOR;
+        } else if (tokenstr == "=") {
+            type = RuleToken::EQUAL_OPERATOR;
+        } else if (tokenstr == ">") {
+            type = RuleToken::GREATER_THAN_OPERATOR;
+        } else if (tokenstr == "<") {
+            type = RuleToken::LESS_THAN_OPERATOR;
+        } else if (tokenstr == "(") {
+            type = RuleToken::LEFT_PARENTHESIS;
+        } else if (tokenstr == ")") {
+            type = RuleToken::RIGHT_PARENTHESIS;
+        } else { // operand
+            type = RuleToken::OPERAND;
+            if (variables.find(tokenstr) == variables.end()) {
+                if (convert(tokenstr, number)) {
+                    type = RuleToken::NUMBER;
+                } else if (tokenstr == "QUAL") {
+                    isVariable = true;
+                } else if (tokenstr == "FILTER") {
+                    isVariable = true;
+                } else {
+                    type = RuleToken::STRING_VARIABLE;
+                }
+            } else {
+                isVariable = true;
+            }
+        }
+        value = tokenstr;
+    }
+
+
+    void tokenizeFilterSpec(string& filterspec, queue<RuleToken>& tokens, map<string, VariantFieldType>& variables) {
+        string lastToken = "";
+        bool inToken = false;
+        for (unsigned int i = 0; i <  filterspec.size(); ++i) {
+            char c = filterspec.at(i);
+            if (c == ' ' || c == '\n') {
+                inToken = false;
+                if (!inToken && lastToken.size() > 0) {
+                    tokens.push(RuleToken(lastToken, variables));
+                    lastToken = "";
+                }
+            } else if (!inToken && (isOperatorChar(c) || isParanChar(c))) {
+                inToken = false;
+                if (lastToken.size() > 0) {
+                    tokens.push(RuleToken(lastToken, variables));
+                    lastToken = "";
+                }
+                tokens.push(RuleToken(filterspec.substr(i,1), variables));
+            } else {
+                inToken = true;
+                lastToken += c;
+            }
+        }
+        // get the last token
+        if (inToken) {
+            tokens.push(RuleToken(lastToken, variables));
+        }
+    }
+
+// class which evaluates filter expressions
+// allow filters to be defined using boolean infix expressions e.g.:
+//
+// "GQ > 10 & (DP < 3 | DP > 5) & SAMPLE = NA12878"
+// or
+// "GT = 1/1 | GT = 0/0"
+//
+// on initialization, tokenizes the input sequence, and converts it from infix to postfix
+// on call to
+//
+
+
+    VariantFilter::VariantFilter(string filterspec, VariantFilterType filtertype, map<string, VariantFieldType>& variables) {
+        type = filtertype;
+        spec = filterspec;
+        tokenizeFilterSpec(filterspec, tokens, variables);
+        infixToPrefix(tokens, rules);
+        /*while (!rules.empty()) {
+          cerr << " " << rules.front().value << ((isNumeric(rules.front())) ? "f" : "");
+          rules.pop();
+          }
+        */
+        //cerr << endl;
+        //cerr << join(" ", tokens) << endl;
+    }
+
+// all alts pass
+    bool VariantFilter::passes(Variant& var, string& sample) {
+        for (vector<string>::iterator a = var.alt.begin(); a != var.alt.end(); ++a) {
+            string& allele = *a;
+            if (!passes(var, sample, allele)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    bool VariantFilter::passes(Variant& var, string& sample, string& allele) {
+        // to evaluate a rpn boolean queue with embedded numbers and variables
+        // make a result stack, use float to allow comparison of floating point
+        // numbers, booleans, and integers
+        stack<RuleToken> results;
+        queue<RuleToken> rulesCopy = rules; // copy
+
+        int index;
+        if (allele.empty()) {
+            index = 0; // apply to the whole record
+        } else {
+            // apply to a specific allele
+            index = var.getAltAlleleIndex(allele);
+        }
+
+        while (!rulesCopy.empty()) {
+            RuleToken token = rulesCopy.front();
+            rulesCopy.pop();
+        // pop operands from the front of the queue and push them onto the stack
+        if (isOperand(token)) {
+            //cout << "is operand: " << token.value << endl;
+            // if the token is variable, i.e. not evaluated in this context, we
+            // must evaluate it before pushing it onto the stack
+            if (token.isVariable) {
+                //cout << "is variable" << endl;
+                // look up the variable using the Variant, depending on our filter type
+                //cout << "token.value " << token.value << endl;
+                VariantFieldType vtype;
+                if (sample.empty()) { // means we are record-specific
+                    vtype = var.infoType(token.value);
+                } else {
+                    vtype = var.formatType(token.value);
+                    //cout << "type = " << type << endl;
+                }
+                //cout << "type: " << type << endl;
+
+                if (vtype == FIELD_INTEGER || vtype == FIELD_FLOAT) {
+                    token.type = RuleToken::NUMERIC_VARIABLE;
+                    token.number = var.getValueFloat(token.value, sample, index);
+                    //cerr << "number: " << token.number << endl;
+                } else if (vtype == FIELD_BOOL) {
+                    token.type = RuleToken::BOOLEAN_VARIABLE;
+                    token.state = var.getValueBool(token.value, sample, index);
+                    //cerr << "state: " << token.state << endl;
+                } else if (vtype == FIELD_STRING) {
+                    //cout << "token.value = " << token.value << endl;
+                    token.type = RuleToken::STRING_VARIABLE;
+                    token.str = var.getValueString(token.value, sample, index);
+                } else if (isString(token)) {
+                    token.type = RuleToken::STRING_VARIABLE;
+                    token.str = var.getValueString(token.value, sample, index);
+                    //cerr << "string: " << token.str << endl;
+                }
+            } else {
+                double f;
+                string s;
+                //cerr << "parsing operand" << endl;
+                if (convert(token.value, f)) {
+                    token.type = RuleToken::NUMERIC_VARIABLE;
+                    token.number = f;
+                    //cerr << "number: " << token.number << endl;
+                } else if (convert(token.value, s)) {
+                    token.type = RuleToken::STRING_VARIABLE;
+                    token.str = s;
+                    //cerr << "string: " << token.str << endl;
+                } else {
+                    cerr << "could not parse non-variable operand " << token.value << endl;
+                    exit(1);
+                }
+            }
+            results.push(token);
+        }
+        // apply operators to the first n elements on the stack and push the result back onto the stack
+        else if (isOperator(token)) {
+            //cerr << "is operator: " << token.value << endl;
+            RuleToken a, b, r;
+            // is it a not-operator?
+            switch (token.type) {
+                case ( RuleToken::NOT_OPERATOR ):
+                    a = results.top();
+                    results.pop();
+                    if (!isBoolean(a)) {
+                        cerr << "cannot negate a non-boolean" << endl;
+                    } else {
+                        a.state = !a.state;
+                        results.push(a);
+                    }
+                    break;
+
+                case ( RuleToken::EQUAL_OPERATOR ):
+                    a = results.top(); results.pop();
+                    b = results.top(); results.pop();
+                    if (a.type == b.type) {
+                        switch (a.type) {
+                            case (RuleToken::STRING_VARIABLE):
+                                r.state = (a.str == b.str);
+                                break;
+                            case (RuleToken::NUMERIC_VARIABLE):
+                                r.state = (a.number == b.number);
+                                break;
+                            case (RuleToken::BOOLEAN_VARIABLE):
+                                r.state = (a.state == b.state);
+                                break;
+                            default:
+                                cerr << "should not get here" << endl; exit(1);
+                                break;
+                        }
+                    } else if (a.type == RuleToken::STRING_VARIABLE && b.type == RuleToken::NUMERIC_VARIABLE) {
+                        r.state = (convert(b.number) == a.str);
+                    } else if (b.type == RuleToken::STRING_VARIABLE && a.type == RuleToken::NUMERIC_VARIABLE) {
+                        r.state = (convert(a.number) == b.str);
+                    }
+                    results.push(r);
+                    break;
+
+                case ( RuleToken::GREATER_THAN_OPERATOR ):
+                    a = results.top(); results.pop();
+                    b = results.top(); results.pop();
+                    if (a.type == b.type && a.type == RuleToken::NUMERIC_VARIABLE) {
+                        r.state = (b.number > a.number);
+                    } else {
+                        cerr << "cannot compare (>) objects of dissimilar types" << endl;
+                        cerr << a.type << " " << b.type << endl;
+                        exit(1);
+                    }
+                    results.push(r);
+                    break;
+
+                case ( RuleToken::LESS_THAN_OPERATOR ):
+                    a = results.top(); results.pop();
+                    b = results.top(); results.pop();
+                    if (a.type == b.type && a.type == RuleToken::NUMERIC_VARIABLE) {
+                        r.state = (b.number < a.number);
+                    } else {
+                        cerr << "cannot compare (<) objects of dissimilar types" << endl;
+                        cerr << a.type << " " << b.type << endl;
+                        exit(1);
+                    }
+                    results.push(r);
+                    break;
+
+                case ( RuleToken::ADD_OPERATOR ):
+                    a = results.top(); results.pop();
+                    b = results.top(); results.pop();
+                    if (a.type == b.type && a.type == RuleToken::NUMERIC_VARIABLE) {
+                        r.number = (b.number + a.number);
+                        r.type = RuleToken::NUMERIC_VARIABLE;
+                    } else {
+                        cerr << "cannot add objects of dissimilar types" << endl;
+                        cerr << a.type << " " << b.type << endl;
+                        exit(1);
+                    }
+                    results.push(r);
+                    break;
+
+                case ( RuleToken::SUBTRACT_OPERATOR ):
+                    a = results.top(); results.pop();
+                    b = results.top(); results.pop();
+                    if (a.type == b.type && a.type == RuleToken::NUMERIC_VARIABLE) {
+                        r.number = (b.number - a.number);
+                        r.type = RuleToken::NUMERIC_VARIABLE;
+                    } else {
+                        cerr << "cannot subtract objects of dissimilar types" << endl;
+                        cerr << a.type << " " << b.type << endl;
+                        exit(1);
+                    }
+                    results.push(r);
+                    break;
+
+                case ( RuleToken::MULTIPLY_OPERATOR ):
+                    a = results.top(); results.pop();
+                    b = results.top(); results.pop();
+                    if (a.type == b.type && a.type == RuleToken::NUMERIC_VARIABLE) {
+                        r.number = (b.number * a.number);
+                        r.type = RuleToken::NUMERIC_VARIABLE;
+                    } else {
+                        cerr << "cannot multiply objects of dissimilar types" << endl;
+                        cerr << a.type << " " << b.type << endl;
+                        exit(1);
+                    }
+                    results.push(r);
+                    break;
+
+                case ( RuleToken::DIVIDE_OPERATOR):
+                    a = results.top(); results.pop();
+                    b = results.top(); results.pop();
+                    if (a.type == b.type && a.type == RuleToken::NUMERIC_VARIABLE) {
+                        r.number = (b.number / a.number);
+                        r.type = RuleToken::NUMERIC_VARIABLE;
+                    } else {
+                        cerr << "cannot divide objects of dissimilar types" << endl;
+                        cerr << a.type << " " << b.type << endl;
+                        exit(1);
+                    }
+                    results.push(r);
+                    break;
+
+                case ( RuleToken::AND_OPERATOR ):
+                case ( RuleToken::OR_OPERATOR ):
+                    a = results.top(); results.pop();
+                    b = results.top(); results.pop();
+                    if (a.type == b.type && a.type == RuleToken::BOOLEAN_VARIABLE) {
+                        if (token.type == RuleToken::AND_OPERATOR) {
+                            r.state = (a.state && b.state);
+                        } else {
+                            r.state = (a.state || b.state);
+                        }
+                    } else {
+                        cerr << "cannot compare (& or |) objects of dissimilar types" << endl;
+                        exit(1);
+                    }
+                    results.push(r);
+                    break;
+                default:
+                    cerr << "should not get here!" << endl; exit(1);
+                    break;
+            }
+        }
+    }
+    // at the end you should have only one value on the stack, return it as a boolean
+    if (results.size() == 1) {
+        if (isBoolean(results.top())) {
+            return results.top().state;
+        } else {
+            cerr << "error, non-boolean value left on stack" << endl;
+            //cerr << results.top().value << endl;
+            exit(1);
+        }
+    } else if (results.size() > 1) {
+        cerr << "more than one value left on results stack!" << endl;
+        while (!results.empty()) {
+            cerr << results.top().value << endl;
+            results.pop();
+        }
+        exit(1);
+    } else {
+        cerr << "results stack empty" << endl;
+        exit(1);
+    }
+}
+
+void VariantFilter::removeFilteredGenotypes(Variant& var, bool keepInfo) {
+
+    for (vector<string>::iterator s = var.sampleNames.begin(); s != var.sampleNames.end(); ++s) {
+        string& name = *s;
+        if (!passes(var, name)) {
+        	if (keepInfo) {
+				var.samples[name]["GT"].clear();
+				var.samples[name]["GT"].push_back("./.");
+        	}
+        	else {
+			    var.samples.erase(name);
+        	}
+        }
+    }
+}
+
+/*
+bool VariantCallFile::openVCF(string& filename) {
+    file.open(filename.c_str(), ifstream::in);
+    if (!file.is_open()) {
+        cerr << "could not open " << filename << endl;
+        return false;
+    } else {
+        return parseHeader();
+    }
+}
+
+bool VariantCallFile::openVCF(ifstream& stream) {
+    file = stream;
+    if (!file.is_open()) {
+        cerr << "provided file is not open" << endl;
+        return false;
+    } else {
+        return parseHeader();
+    }
+}
+*/
+
+void VariantCallFile::updateSamples(vector<string>& newSamples) {
+    sampleNames = newSamples;
+    // regenerate the last line of the header
+    vector<string> headerLines = split(header, '\n');
+    vector<string> colnames = split(headerLines.at(headerLines.size() - 1), '\t'); // get the last, update the samples
+    vector<string> newcolnames;
+    newcolnames.resize(9 + sampleNames.size());
+    copy(colnames.begin(), colnames.begin() + 9, newcolnames.begin());
+    copy(sampleNames.begin(), sampleNames.end(), newcolnames.begin() + 9);
+    headerLines.at(headerLines.size() - 1) = join(newcolnames, "\t");
+    header = join(headerLines, "\n");
+}
+
+// non-destructive version of above
+string VariantCallFile::headerWithSampleNames(vector<string>& newSamples) {
+    // regenerate the last line of the header
+    if (newSamples.empty()) return header;
+    vector<string> headerLines = split(header, '\n');
+    vector<string> colnames = split(headerLines.at(headerLines.size() - 1), '\t'); // get the last, update the samples
+    vector<string> newcolnames;
+    unsigned int colCount = colnames.size(); // used to be hard-coded 9, hopefully the dynamic colCount isn't an issue
+    if (colCount < 8)
+    {
+        cout << "VCF file is not suitable for use because it does not have a format field." << endl;
+        exit(0);
+    }
+    newcolnames.resize(colCount + newSamples.size());
+    copy(colnames.begin(), colnames.begin() + colCount, newcolnames.begin());
+    copy(newSamples.begin(), newSamples.end(), newcolnames.begin() + colCount);
+    headerLines.at(headerLines.size() - 1) = join(newcolnames, "\t");
+    return join(headerLines, "\n");
+}
+
+// TODO cleanup, store header lines instead of bulk header
+void VariantCallFile::addHeaderLine(string line) {
+    vector<string> headerLines = split(header, '\n');
+    headerLines.insert(headerLines.end() - 1, line);
+    header = join(unique(headerLines), "\n");
+}
+
+// helper to addHeaderLine
+vector<string>& unique(vector<string>& strings) {
+    set<string> uniq;
+    vector<string> res;
+    for (const auto& s : strings) {
+        if (uniq.find(s) == uniq.end()) {
+            res.push_back(s);
+            uniq.insert(s);
+        }
+    }
+    strings = res;
+    return strings;
+}
+
+vector<string> VariantCallFile::infoIds(void) {
+    vector<string> tags;
+    vector<string> headerLines = split(header, '\n');
+    for (const auto& line : headerLines) {
+        if (line.find("##INFO") == 0) {
+            size_t pos = line.find("ID=");
+            if (pos != string::npos) {
+                pos += 3;
+                size_t tagend = line.find(",", pos);
+                if (tagend != string::npos) {
+                    tags.push_back(line.substr(pos, tagend - pos));
+                }
+            }
+        }
+    }
+    return tags;
+}
+
+vector<string> VariantCallFile::formatIds(void) {
+    vector<string> tags;
+    vector<string> headerLines = split(header, '\n');
+    for (const auto& line : headerLines) {
+        if (line.find("##FORMAT") == 0) {
+            size_t pos = line.find("ID=");
+            if (pos != string::npos) {
+                pos += 3;
+                size_t tagend = line.find(",", pos);
+                if (tagend != string::npos) {
+                    tags.push_back(line.substr(pos, tagend - pos));
+                }
+            }
+        }
+    }
+    return tags;
+}
+
+void VariantCallFile::removeInfoHeaderLine(string const & tag) {
+    vector<string> headerLines = split(header, '\n');
+    vector<string> newHeader;
+    string id = "ID=" + tag + ",";
+    for (const auto& line : headerLines) {
+        if (line.find("##INFO") == 0) {
+            if (line.find(id) == string::npos) {
+                newHeader.push_back(line);
+            }
+        } else {
+            newHeader.push_back(line);
+        }
+    }
+    header = join(newHeader, "\n");
+}
+
+void VariantCallFile::removeGenoHeaderLine(string const & tag) {
+    vector<string> headerLines = split(header, '\n');
+    vector<string> newHeader;
+    string id = "ID=" + tag + ",";
+    for (const auto& headerLine : headerLines) {
+        if (headerLine.find("##FORMAT") == 0) {
+            if (headerLine.find(id) == string::npos) {
+                newHeader.push_back(headerLine);
+            }
+        } else {
+            newHeader.push_back(headerLine);
+        }
+    }
+    header = join(newHeader, "\n");
+}
+
+vector<string> VariantCallFile::getHeaderLinesFromFile()
+{
+    string headerStr = "";
+
+    if (usingTabix) {
+        tabixFile->getHeader(headerStr);
+        if (headerStr.empty()) {
+            cerr << "error: no VCF header" << endl;
+            exit(1);
+        }
+        tabixFile->getNextLine(line);
+        firstRecord = true;
+    } else {
+        while (std::getline(*file, line)) {
+            if (line.substr(0,1) == "#") {
+                headerStr += line + '\n';
+            } else {
+                // done with header
+                if (headerStr.empty()) {
+                    cerr << "error: no VCF header" << endl;
+                    return vector<string>();
+                }
+                firstRecord = true;
+                break;
+            }
+        }
+    }
+    return split(headerStr, "\n");
+}
+
+bool VariantCallFile::parseHeader(void) {
+
+    string headerStr = "";
+
+    if (usingTabix) {
+        tabixFile->getHeader(headerStr);
+        if (headerStr.empty()) {
+            cerr << "error: no VCF header" << endl;
+            exit(1);
+        }
+        tabixFile->getNextLine(line);
+        firstRecord = true;
+    } else {
+        while (std::getline(*file, line)) {
+            if (line.substr(0,1) == "#") {
+                headerStr += line + '\n';
+            } else {
+                // done with header
+                if (headerStr.empty()) {
+                    cerr << "error: no VCF header" << endl;
+                    return false;
+                }
+                firstRecord = true;
+                break;
+            }
+        }
+    }
+    this->vcf_header = headerStr;
+
+    return parseHeader(headerStr);
+
+}
+
+bool VariantCallFile::parseHeader(string& hs) {
+
+    if (hs.empty()) return false;
+    if (hs.substr(hs.size() - 1, 1) == "\n") {
+	hs.erase(hs.size() - 1, 1); // remove trailing newline
+    }
+    header = hs; // stores the header in the object instance
+
+    vector<string> headerLines = split(header, "\n");
+    for (const auto& headerLine : headerLines) {
+        if (headerLine.substr(0,2) == "##") {
+            // meta-information headerLines
+            // TODO parse into map from info/format key to type
+            // ##INFO=<ID=DP,Number=1,Type=Integer,Description="Total Depth">
+            // ##FORMAT=<ID=CB,Number=1,Type=String,Description="Called by S(Sanger), M(UMich), B(BI)">
+            size_t found = headerLine.find_first_of("=");
+            string entryType = headerLine.substr(2, found - 2);
+            // handle reference here, no "<" and ">" given
+                //} else if (entryType == "reference") {
+            size_t dataStart = headerLine.find_first_of("<");
+            size_t dataEnd = headerLine.find_first_of(">");
+            if (dataStart != string::npos && dataEnd != string::npos) {
+                string entryData = headerLine.substr(dataStart + 1, dataEnd - dataStart - 1);
+                // this will break if it includes a "long form" string
+                // including either a = or , in the first or second
+                // field
+                if (entryType == "INFO" || entryType == "FORMAT") {
+                    vector<string> fields = split(entryData, "=,");
+                    if (fields.size() < 8) {
+                        cerr << "header line does not have all of the required fields: ID, Number, Type, and Description" << endl
+                             << headerLine << endl;
+                        exit(1);
+                    }
+                    // get the required fields from the header line
+                    auto id_field = find(fields.begin(), fields.begin() + 8, "ID");
+                    auto num_field = find(fields.begin(), fields.begin() + 8, "Number");
+                    auto type_field = find(fields.begin(), fields.begin() + 8, "Type");
+                    auto desc_field = find(fields.begin(), fields.begin() + 8, "Description");
+                    for (auto it : {id_field, num_field, type_field, desc_field}) {
+                        // make sure we found the field and that all of the keys have a value associated
+                        if (it == fields.begin() + 8 || ((it - fields.begin()) % 2 == 1)) {
+                            if (it == desc_field) {
+                                // we don't actually record / use the description, so we'll just give a warning
+                                cerr << "warning: ";
+                            }
+                            cerr << "header line does not have all of the required fields (ID, Number, Type, and Description) in the first 4 fields" << endl
+                                 << headerLine << endl;
+                            if (it != desc_field) {
+                                exit(1);
+                            }
+                        }
+                    }
+                    string id = *(id_field + 1);
+                    int number;
+                    string numberstr = *(num_field + 1);
+                    // string numberstr = mapper["Number"].c_str();
+
+                    // XXX TODO VCF has variable numbers of fields...
+                    if (numberstr == "A") {
+                        number = ALLELE_NUMBER;
+                    } else if (numberstr == "G") {
+                        number = GENOTYPE_NUMBER;
+                    } else if (numberstr == ".") {
+                        number = 1;
+                    } else {
+                        convert(numberstr, number);
+                    }
+                    VariantFieldType type = typeStrToVariantFieldType(*(type_field + 1));
+                    // VariantFieldType type = typeStrToVariantFieldType(mapper["TYPE"]);
+                    if (entryType == "INFO") {
+                        infoCounts[id] = number;
+                        infoTypes[id] = type;
+                    } else if (entryType == "FORMAT") {
+                        formatCounts[id] = number;
+                        formatTypes[id] = type;
+                    }
+                }
+            }
+        } else if (headerLine.substr(0,1) == "#") {
+            // field name headerLine
+            vector<string> fields = split(headerLine, '\t');
+            if (fields.size() > 8) {
+                sampleNames.resize(fields.size() - 9);
+                copy(fields.begin() + 9, fields.end(), sampleNames.begin());
+            }
+        }
+    }
+
+    return true;
+}
+
+bool VariantCallFile::getNextVariant(Variant& var) {
+        if (firstRecord && !justSetRegion) {
+            if (!line.empty() && line.substr(0,1) != "#") {
+                var.parse(line, parseSamples);
+                firstRecord = false;
+                _done = false;
+                return true;
+            } else {
+                return false;
+            }
+        }
+        if (usingTabix) {
+            if (justSetRegion && !line.empty() && line.substr(0,1) != "#") {
+                if (firstRecord) {
+                    firstRecord = false;
+                }
+                var.parse(line, parseSamples);
+                line.clear();
+                justSetRegion = false;
+                _done = false;
+                return true;
+            } else if (tabixFile->getNextLine(line)) {
+                var.parse(line, parseSamples);
+                _done = false;
+                return true;
+            } else {
+                _done = true;
+                return false;
+            }
+        } else {
+            if (std::getline(*file, line)) {
+                var.parse(line, parseSamples);
+                _done = false;
+                return true;
+            } else {
+                _done = true;
+                return false;
+            }
+        }
+}
+
+bool VariantCallFile::setRegion(const string& seq, long int start, long int end) {
+    stringstream regionstr;
+    if (end) {
+        regionstr << seq << ":" << start << "-" << end;
+    } else {
+        regionstr << seq << ":" << start;
+    }
+    return setRegion(regionstr.str());
+}
+
+bool VariantCallFile::setRegion(const string& region) {
+    if (!usingTabix) {
+        cerr << "cannot setRegion on a non-tabix indexed file" << endl;
+        exit(1);
+    }
+    // convert between bamtools/freebayes style region string and tabix/samtools style
+    regex txt_regex("(\\d+)\\.\\.(\\d+)$");
+    string tabix_region = regex_replace(region, txt_regex, "$1-$2");
+
+    if (tabixFile->setRegion(tabix_region)) {
+        if (tabixFile->getNextLine(line)) {
+	    justSetRegion = true;
+            return true;
+        } else {
+            return false;
+        }
+    } else {
+        return false;
+    }
+}
+
+
+// genotype manipulation
+/*
+map<string, int> decomposeGenotype(string& genotype) {
+    string splitter = "/";
+    if (genotype.find("|") != string::npos) {
+        splitter = "|";
+    }
+    vector<string> haps = split(genotype, splitter);
+    map<string, int> decomposed;
+    for (vector<string>::iterator h = haps.begin(); h != haps.end(); ++h) {
+        ++decomposed[*h];
+    }
+    return decomposed;
+}
+*/
+
+map<int, int> decomposeGenotype(const string& genotype) {
+    string splitter = "/";
+    if (genotype.find('|') != string::npos) {
+        splitter = "|";
+    }
+    vector<string> haps = split(genotype, splitter);
+    map<int, int> decomposed;
+    for (const auto& h : haps) {
+        int alt;
+        if (h == ".") {
+            ++decomposed[NULL_ALLELE];
+        } else {
+            convert(h, alt);
+            ++decomposed[alt];
+        }
+    }
+    return decomposed;
+}
+
+vector<int> decomposePhasedGenotype(const string& genotype) {
+    string splitter = "/";
+    if (genotype.find('|') != string::npos) {
+        splitter = "|";
+    }
+    vector<string> haps = split(genotype, splitter);
+    if (haps.size() > 1 && splitter == "/") {
+        cerr << "could not find '|' in genotype, cannot decomposePhasedGenotype on unphased genotypes" << endl;
+        exit(1);
+    }
+    vector<int> decomposed;
+    for (const auto& h : haps) {
+        int alt;
+        if (h == ".") {
+            decomposed.push_back(NULL_ALLELE);
+        } else {
+            convert(h, alt);
+            decomposed.push_back(alt);
+        }
+    }
+    return decomposed;
+}
+
+string genotypeToString(const map<int, int>& genotype) {
+    vector<int> s;
+    for (const auto& g : genotype) {
+        int a = g.first;
+        int c = g.second;
+        for (int i = 0; i < c; ++i) s.push_back(a);
+    }
+    sort(s.begin(), s.end());
+    vector<string> r;
+    r.reserve(s.size());
+
+    for (const auto& i : s) {
+        if (i == NULL_ALLELE) r.push_back(".");
+        else r.push_back(convert(i));
+    }
+    return join(r, "/"); // TODO adjust for phased/unphased
+}
+
+string phasedGenotypeToString(const vector<int>& genotype) {
+    vector<string> r;
+    for (const auto& i : genotype) {
+        if (i == NULL_ALLELE) r.push_back(".");
+        else r.push_back(convert(i));
+    }
+    return join(r, "|");
+}
+
+bool isHet(const map<int, int>& genotype) {
+    return genotype.size() > 1;
+}
+
+bool isHom(const map<int, int>& genotype) {
+    return genotype.size() == 1;
+}
+
+bool hasNonRef(const map<int, int>& genotype) {
+    for (const auto& g : genotype) {
+        if (g.first != 0) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool isHomRef(const map<int, int>& genotype) {
+    return isHom(genotype) && !hasNonRef(genotype);
+}
+
+bool isHomNonRef(const map<int, int>& genotype) {
+    return isHom(genotype) && hasNonRef(genotype);
+}
+
+bool isNull(const map<int, int>& genotype) {
+    return genotype.find(NULL_ALLELE) != genotype.end();
+}
+
+int ploidy(const map<int, int>& genotype) {
+    int i = 0;
+    for (const auto& g : genotype) {
+        i += g.second;
+    }
+    return i;
+}
+
+
+
+
+map<string, vector<VariantAllele> > Variant::flatAlternates(void) {
+    map<string, vector<VariantAllele> > variantAlleles;
+    for (const auto& alternate: alt) {
+        vector<VariantAllele>& variants = variantAlleles[alternate];
+        variants.push_back(VariantAllele(ref, alternate, position));
+    }
+    return variantAlleles;
+}
+
+set<string> Variant::altSet(void) {
+    set<string> altset(alt.begin(), alt.end());
+    return altset;
+}
+
+map<pair<int, int>, int> Variant::getGenotypeIndexesDiploid(void) {
+
+    map<pair<int, int>, int> genotypeIndexes;
+    //map<int, map<Genotype*, int> > vcfGenotypeOrder;
+    vector<int> indexes;
+    indexes.reserve(alleles.size());
+    for (int i = 0; i < alleles.size(); ++i) {
+        indexes.push_back(i);
+    }
+    int ploidy = 2; // ONLY diploid
+    vector<vector<int> > genotypes = multichoose(ploidy, indexes);
+    for (auto& g : genotypes) {
+        sort(g.begin(), g.end());  // enforce e.g. 0/1, 0/2, 1/2 ordering over reverse
+        // XXX this does not handle non-diploid!!!!
+        int j = g.front();
+        int k = g.back();
+        genotypeIndexes[make_pair(j, k)] = (k * (k + 1) / 2) + j;
+    }
+    return genotypeIndexes;
+}
+
+void Variant::updateAlleleIndexes(void) {
+    // adjust the allele index
+    altAlleleIndexes.clear();
+    int m = 0;
+    for (vector<string>::iterator a = alt.begin();
+            a != alt.end(); ++a, ++m) {
+        altAlleleIndexes[*a] = m;
+    }
+}
+
+// TODO only works on "A"llele variant fields
+  void Variant::removeAlt(const string& altAllele) {
+
+    int altIndex = getAltAlleleIndex(altAllele);  // this is the alt-relative index, 0-based
+
+    for (const auto& c: vcf->infoCounts) {
+      int count = c.second;
+      if (count == ALLELE_NUMBER) {
+	const string& key = c.first;
+	map<string, vector<string> >::iterator v = info.find(key);
+	if (v != info.end()) {
+	  vector<string>& vals = v->second;
+	  vector<string> tokeep;
+	  int i = 0;
+	  for (vector<string>::iterator a = vals.begin();
+	       a != vals.end(); ++a, ++i) {
+	    if (i != altIndex) {
+	      tokeep.push_back(*a);
+	    }
+	  }
+	  vals = tokeep;
+	}
+      }
+    }
+
+    for (const auto& c : vcf->formatCounts) {
+      int count = c.second;
+      if (count == ALLELE_NUMBER) {
+      	const string& key = c.first;
+      	for (auto& [_, sample] : samples) {
+      		map<string, vector<string> >::iterator v = sample.find(key);
+      		if (v != sample.end()) {
+      			vector<string>& vals = v->second;
+      			vector<string> tokeep;
+      			int i = 0;
+      			for (vector<string>::iterator a = vals.begin();
+                    a != vals.end(); ++a, ++i) {
+      				if (i != altIndex) {
+      					tokeep.push_back(*a);
+      				}
+                    }
+      			vals = tokeep;
+      		}
+      	}
+      }
+    }
+
+    int altSpecIndex = altIndex + 1; // this is the genotype-spec index, ref=0, 1-based for alts
+
+    vector<string> newalt;
+    map<int, int> alleleIndexMapping;
+    // setup the new alt string
+    alleleIndexMapping[0] = 0; // reference allele remains the same
+    alleleIndexMapping[NULL_ALLELE] = NULL_ALLELE; // null allele remains the same
+    int i = 1; // current index
+    int j = 1; // new index
+    for (vector<string>::iterator a = alt.begin(); a != alt.end(); ++a, ++i) {
+        if (i != altSpecIndex) {
+            newalt.push_back(*a);
+            // get the mapping between new and old allele indexes
+            alleleIndexMapping[i] = j;
+            ++j;
+        } else {
+            alleleIndexMapping[i] = NULL_ALLELE;
+        }
+    }
+
+    // fix the sample genotypes, removing reference to the old allele
+    map<string, int> samplePloidy;
+    for (map<string, map<string, vector<string> > >::iterator s = samples.begin(); s != samples.end(); ++s) {
+        map<string, vector<string> >& sample = s->second;
+        if (sample.find("GT") != sample.end()) {
+            string& gt = sample["GT"].front();
+            string splitter = "/";
+            if (gt.find('|') != string::npos) {
+                splitter = "|";
+            }
+
+            if (splitter == "/") {
+                samplePloidy[s->first] = split(gt, splitter).size();
+                map<int, int> genotype = decomposeGenotype(sample["GT"].front());
+                map<int, int> newGenotype;
+                for (map<int, int>::iterator g = genotype.begin(); g != genotype.end(); ++g) {
+                    newGenotype[alleleIndexMapping[g->first]] += g->second;
+                }
+                sample["GT"].clear();
+                sample["GT"].push_back(genotypeToString(newGenotype));
+            } else {
+                samplePloidy[s->first] = split(gt, splitter).size();
+                vector<int> genotype = decomposePhasedGenotype(sample["GT"].front());
+                vector<int> newGenotype;
+                for (vector<int>::iterator g = genotype.begin(); g != genotype.end(); ++g) {
+                    newGenotype.push_back(alleleIndexMapping[*g]);
+                }
+                sample["GT"].clear();
+                sample["GT"].push_back(phasedGenotypeToString(newGenotype));
+            }
+        }
+    }
+
+    set<int> ploidies;
+    for (map<string, int>::iterator p = samplePloidy.begin(); p != samplePloidy.end(); ++p) {
+        ploidies.insert(p->second);
+    }
+
+    // fix the sample genotype likelihoods, removing reference to the old allele
+    // which GL fields should we remove?
+    vector<int> toRemove;
+    toRemove.push_back(altSpecIndex);
+    map<int, map<int, int> > glMappingByPloidy;
+    for (const auto p : ploidies) {
+        glMappingByPloidy[p] = glReorder(p, alt.size() + 1, alleleIndexMapping, toRemove);
+    }
+
+    for (auto& s : samples) {
+        auto& sample = s.second;
+        auto glsit = sample.find("GL");
+        if (glsit != sample.end()) {
+            vector<string>& gls = glsit->second; // should be split already
+            map<int, string> newgls;
+            map<int, int>& newOrder = glMappingByPloidy[samplePloidy[s.first]];
+            int i = 0;
+            for (vector<string>::iterator g = gls.begin(); g != gls.end(); ++g, ++i) {
+                int j = newOrder[i];
+                if (j != -1) {
+                    newgls[i] = *g;
+                }
+            }
+            // update the gls
+            gls.clear();
+            for (const auto& g : newgls) {
+                gls.push_back(g.second);
+            }
+        }
+    }
+
+    // reset the alt
+    alt = newalt;
+
+    // and the alleles
+    alleles.clear();
+    alleles.push_back(ref);
+    alleles.insert(alleles.end(), alt.begin(), alt.end());
+
+    updateAlleleIndexes();
+
+}
+
+// union of lines in headers of input files
+string unionInfoHeaderLines(string& s1, string& s2) {
+    vector<string> lines1 = split(s1, "\n");
+    vector<string> lines2 = split(s2, "\n");
+    vector<string> result;
+    set<string> l2;
+    string lastHeaderLine; // this one needs to be at the end
+    for (const auto& s : lines2) {
+        if (s.substr(0,6) == "##INFO") {
+            l2.insert(s);
+        }
+    }
+    for (const auto& s : lines1) {
+        if (l2.count(s)) {
+            l2.erase(s);
+        }
+        if (s.substr(0,6) == "#CHROM") {
+            lastHeaderLine = s;
+        } else {
+            result.push_back(s);
+        }
+    }
+    for (const auto& s : l2) {
+        result.push_back(s);
+    }
+    if (lastHeaderLine.empty()) {
+        cerr << "could not find CHROM POS ... header line" << endl;
+        exit(1);
+    }
+    result.push_back(lastHeaderLine);
+    return join(result, "\n");
+}
+
+list<list<int> > _glorder(int ploidy, int alts) {
+    if (ploidy == 1) {
+        list<list<int> > results;
+        for (int n = 0; n < alts; ++n) {
+            list<int> v;
+            v.push_back(n);
+            results.push_back(v);
+        }
+        return results;
+    } else {
+        list<list<int> > results;
+        for (int n = 0; n < alts; ++n) {
+            list<list<int> > x = _glorder(ploidy - 1, alts);
+            for (auto& v : x) {
+                if (v.front() <= n) {
+                    v.push_front(n);
+                    results.push_back(v);
+                }
+            }
+        }
+        return results;
+    }
+}
+
+// genotype likelihood-ordering of genotypes, where each genotype is a
+// list of integers (as written in the GT field)
+list<list<int> > glorder(int ploidy, int alts) {
+    list<list<int> > results = _glorder(ploidy, alts);
+    for (auto& v : results) {
+        v.reverse();
+    }
+    return results;
+}
+
+// which genotype likelihoods would include this alternate allele
+list<int> glsWithAlt(int alt, int ploidy, int numalts) {
+    list<int> gls;
+    list<list<int> > orderedGenotypes = glorder(ploidy, numalts);
+    int i = 0;
+    for (list<list<int> >::iterator v = orderedGenotypes.begin(); v != orderedGenotypes.end(); ++v, ++i) {
+        for (const auto& q : *v) {
+            if (q == alt) {
+                gls.push_back(i);
+                break;
+            }
+        }
+    }
+    return gls;
+}
+
+// describes the mapping between the old gl ordering and and a new
+// one in which the GLs including the old alt have been removed
+// a map to -1 means "remove"
+map<int, int> glReorder(int ploidy, int numalts, map<int, int>& alleleIndexMapping, vector<int>& altsToRemove) {
+    map<int, int> mapping;
+    list<list<int> > orderedGenotypes = glorder(ploidy, numalts);
+    for (auto& v : orderedGenotypes) {
+        for (auto& n : v) {
+            n = alleleIndexMapping[n];
+        }
+    }
+    list<list<int> > newOrderedGenotypes = glorder(ploidy, numalts - altsToRemove.size());
+    map<list<int>, int> newOrderedGenotypesMapping;
+    int i = 0;
+    // mapping is wrong...
+    for (list<list<int> >::iterator v = newOrderedGenotypes.begin(); v != newOrderedGenotypes.end(); ++v, ++i) {
+        newOrderedGenotypesMapping[*v] = i;
+    }
+    i = 0;
+    for (list<list<int> >::iterator v = orderedGenotypes.begin(); v != orderedGenotypes.end(); ++v, ++i) {
+        map<list<int>, int>::iterator m = newOrderedGenotypesMapping.find(*v);
+        if (m != newOrderedGenotypesMapping.end()) {
+            //cout << "new gl order of " << i << " is " << m->second << endl;
+            mapping[i] = m->second;
+        } else {
+            //cout << i << " will be removed" << endl;
+            mapping[i] = -1;
+        }
+    }
+    return mapping;
+}
+
+string Variant::getGenotype(const string& sample) {
+    map<string, map<string, vector<string> > >::iterator s = samples.find(sample);
+    if (s != samples.end()) {
+        map<string, vector<string> >::iterator f = s->second.find("GT");
+        if (f != s->second.end()) {
+            return f->second.front();
+        }
+    }
+    return "";
+}
+
+bool Variant::isPhased(void) {
+    for (map<string, map<string, vector<string> > >::iterator s = samples.begin(); s != samples.end(); ++s) {
+        map<string, vector<string> >& sample = s->second;
+        map<string, vector<string> >::iterator g = sample.find("GT");
+        if (g != sample.end()) {
+            string gt = g->second.front();
+            if (gt.size() > 1 && gt.find('|') == string::npos) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+long Variant::zeroBasedPosition(void) const {
+    return position - 1;
+}
+
+string Variant::vrepr(void) {
+    return sequenceName + "\t" + convert(position) + "\t" + join(alleles, ",");
+}
+
+// TODO
+/*
+vector<Variant*> Variant::matchingHaplotypes() {
+
+    int haplotypeStart = var.position;
+    int haplotypeEnd = var.position + var.ref.size();
+
+    for (vector<Variant*>::iterator v = overlapping.begin(); v != overlapping.end(); ++v) {
+        haplotypeStart = min((*v)->position, (long int) haplotypeStart);
+        haplotypeEnd = max((*v)->position + (*v)->ref.size(), (long unsigned int) haplotypeEnd);
+    }
+
+    // for everything overlapping and the current variant, construct the local haplotype within the bounds
+    // if there is an exact match, the allele in the current VCF does intersect
+
+    string referenceHaplotype = reference.getSubSequence(var.sequenceName, haplotypeStart - 1, haplotypeEnd - haplotypeStart);
+    map<string, vector<pair<Variant*, int> > > haplotypes; // map to variant and alt index
+
+    for (vector<Variant*>::iterator v = overlapping.begin(); v != overlapping.end(); ++v) {
+        Variant& variant = **v;
+        int altindex = 0;
+        for (vector<string>::iterator a = variant.alt.begin(); a != variant.alt.end(); ++a, ++altindex) {
+            string haplotype = referenceHaplotype;
+            // get the relative start and end coordinates for the variant alternate allele
+            int relativeStart = variant.position - haplotypeStart;
+            haplotype.replace(relativeStart, variant.ref.size(), *a);
+            haplotypes[haplotype].push_back(make_pair(*v, altindex));
+        }
+    }
+
+    Variant originalVar = var;
+
+    // determine the non-intersecting alts
+    vector<string> altsToRemove;
+    vector<int> altIndexesToRemove;
+    for (vector<string>::iterator a = var.alt.begin(); a != var.alt.end(); ++a) {
+        string haplotype = referenceHaplotype;
+        int relativeStart = var.position - haplotypeStart;
+        haplotype.replace(relativeStart, var.ref.size(), *a);
+        map<string, vector<pair<Variant*, int> > >::iterator h = haplotypes.find(haplotype);
+        if ((intersecting && !invert && h == haplotypes.end())
+            || (intersecting && invert && h != haplotypes.end())
+            || (unioning && h != haplotypes.end())) {
+            if (tag.empty() && mergeToTag.empty()) {
+                altsToRemove.push_back(*a);
+            } else {
+                if (!tag.empty()) {
+                    var.info[tag].push_back(".");
+                }
+                if (!mergeToTag.empty()) {
+                    var.info[mergeToTag].push_back(".");
+                }
+            }
+        } else {
+            if (!tag.empty()) {
+                var.info[tag].push_back(tagValue);
+            }
+            // NB: just take the first value for the mergeFromTag
+            if (!mergeToTag.empty()) {
+                Variant* v = h->second.front().first;
+                int index = h->second.front().second;
+                if (v->info.find(mergeFromTag) != v->info.end()) {
+                    // now you have to find the exact allele...
+                    string& otherValue = v->info[mergeFromTag].at(index);
+                    var.info[mergeToTag].push_back(otherValue);
+                } else if (mergeFromTag == "QUAL") {
+                    var.info[mergeToTag].push_back(convert(v->quality));
+                } else {
+                    var.info[mergeToTag].push_back(".");
+                }
+            }
+        }
+    }
+
+    // remove the non-overlapping (intersecting) or overlapping (unioning) alts
+    if (intersecting && loci && altsToRemove.size() != var.alt.size()) {
+        // we have a match in loci mode, so we should output the whole loci, not just the matching sequence
+    } else {
+        for (vector<string>::iterator a = altsToRemove.begin(); a != altsToRemove.end(); ++a) {
+            var.removeAlt(*a);
+        }
+    }
+
+    if (unioning) {
+
+        // somehow sort the records and combine them?
+        map<long int, vector<Variant*> > variants;
+        for (vector<Variant*>::iterator o = overlapping.begin(); o != overlapping.end(); ++o) {
+            if ((*o)->position <= var.position && // check ensures proper ordering of variants on output
+                outputVariants.find(*o) == outputVariants.end()) {
+                outputVariants.insert(*o);
+                variants[(*o)->position].push_back(*o);
+            }
+        }
+        // add in the current variant, if it has alts left
+        if (!var.alt.empty()) {
+            vector<Variant*>& vars = variants[var.position];
+            int numalts = 0;
+            for (vector<Variant*>::iterator v = vars.begin(); v != vars.end(); ++v) {
+                numalts += (*v)->alt.size();
+            }
+            if (numalts + var.alt.size() == originalVar.alt.size()) {
+                variants[var.position].clear();
+                variants[var.position].push_back(&originalVar);
+            } else {
+                variants[var.position].push_back(&var);
+            }
+        }
+
+        for (map<long int, vector<Variant*> >::iterator v = variants.begin(); v != variants.end(); ++v) {
+            for (vector<Variant*>::iterator o = v->second.begin(); o != v->second.end(); ++o) {
+                cout << **o << endl;
+                lastOutputPosition = max(lastOutputPosition, (*o)->position);
+            }
+        }
+    } else {
+        // if any alts remain, output the variant record
+        if (!var.alt.empty()) {
+            cout << var << endl;
+            lastOutputPosition = max(lastOutputPosition, var.position);
+        }
+    }
+
+}
+*/
+
+
+    VCFHeader::VCFHeader()
+    {
+
+        // add manditory fields
+        this->header_columns.push_back("#CHROM");
+        this->header_columns.push_back("POS");
+        this->header_columns.push_back("ID");
+        this->header_columns.push_back("REF");
+        this->header_columns.push_back("ALT");
+        this->header_columns.push_back("QUAL");
+        this->header_columns.push_back("FILTER");
+        this->header_columns.push_back("INFO");
+
+        // add the line names in order
+        // the order is used when outputting as a string
+        this->header_line_names_ordered.push_back("##fileFormat");
+        this->header_line_names_ordered.push_back("##fileDate");
+        this->header_line_names_ordered.push_back("##source");
+        this->header_line_names_ordered.push_back("##reference");
+        this->header_line_names_ordered.push_back( "##contig");
+        this->header_line_names_ordered.push_back("##phasing");
+        this->header_line_names_ordered.push_back( "##assembly");
+
+        // add the list names in order
+        // the order is used when outputting as a string (getHeaderString)
+        this->header_list_names_ordered.push_back("##info");
+        this->header_list_names_ordered.push_back("##filter");
+        this->header_list_names_ordered.push_back("##format");
+        this->header_list_names_ordered.push_back("##alt");
+        this->header_list_names_ordered.push_back("##sample");
+        this->header_list_names_ordered.push_back("##pedigree");
+        this->header_list_names_ordered.push_back("##pedigreedb");
+
+        // initialize the header_lines with the above vector.
+        // Set the key as the ##_type_ and the value as an empty string
+        // Empty strings are ignored when outputting as string (getHeaderString)
+        for (const auto& header_lines_iter : this->header_line_names_ordered)
+        {
+            this->header_lines[header_lines_iter] = "";
+        }
+
+        // initialize the header_lines with the above vector.
+        // Set the key as the ##_type_ and the value as an empty vector<string>
+        // Empty vectors are ignored when outputting as string (getHeaderString)
+        for (const auto& header_lists_iter : header_list_names_ordered)
+        {
+            this->header_lists[header_lists_iter] = vector<string>(0);
+        }
+
+    }
+
+    void VCFHeader::addMetaInformationLine(const string& meta_line)
+    {
+        // get the meta_line unique key (first chars before the =)
+        unsigned int meta_line_index = meta_line.find('=', 0);
+        string meta_line_prefix = meta_line.substr(0, meta_line_index);
+
+        // check if the meta_line_prefix is in the header_lines, if so add it to the appropirate list
+        if (this->header_lines.find(meta_line_prefix) != header_lines.end()) // the meta_line is a header line so replace what was there
+        {
+            this->header_lines[meta_line_prefix] = meta_line;
+        }
+        else if (header_lists.find(meta_line_prefix) != header_lists.end() &&
+            !metaInfoIdExistsInVector(meta_line, this->header_lists[meta_line_prefix])) // check if the metalineprefix is in the headerLists, if so add it to the appropirate list
+        {
+            this->header_lists[meta_line_prefix].push_back(meta_line);
+        }
+    }
+
+    string VCFHeader::getHeaderString()
+    {
+        // getHeaderString generates the string each time it is called
+        string header_string;
+
+        // start by adding the header_lines
+        for (const auto& header_lines_iter : header_line_names_ordered)
+        {
+            if (this->header_lines[header_lines_iter] != "")
+            {
+                header_string += this->header_lines[header_lines_iter] + "\n";
+            }
+        }
+
+        // next add header_lists
+        for (const auto& header_lists_iter : header_list_names_ordered)
+        {
+            vector<string> tmp_header_lists = this->header_lists[header_lists_iter];
+            for (const auto& header_list : tmp_header_lists)
+            {
+                header_string += header_list + "\n";
+            }
+        }
+
+        // last add header columns
+        const auto last_element = this->header_columns.end() - 1;
+        for (auto header_column_iter = this->header_columns.begin(); header_column_iter != this->header_columns.end(); ++header_column_iter)
+        {
+            string delimiter = (header_column_iter == last_element) ? "\n" : "\t";
+            header_string += (*header_column_iter) + delimiter;
+        }
+        return header_string;
+    }
+
+    bool VCFHeader::metaInfoIdExistsInVector(const string& meta_line, vector<string>& meta_lines)
+    {
+        // extract the id from meta_line
+        size_t meta_line_id_start_idx = meta_line.find("ID=", 0); // used for the start of the substring index
+        size_t meta_line_id_end_idx = meta_line.find(',', meta_line_id_start_idx); // used for end of the substring index
+        string meta_line_id = (meta_line_id_start_idx < meta_line_id_end_idx) ? meta_line.substr(meta_line_id_start_idx, meta_line_id_end_idx - meta_line_id_start_idx) : "";
+
+        for (const auto& meta_line : meta_lines)
+        {
+            // extract the id from meta_line string
+            size_t meta_line_id_start_idx = meta_line.find("ID=", 0);
+            size_t meta_line_id_end_idx = meta_line.find(",", meta_line_id_start_idx);
+            string meta_line_id = (meta_line_id_start_idx < meta_line_id_end_idx) ? meta_line.substr(meta_line_id_start_idx, meta_line_id_end_idx - meta_line_id_start_idx) : "";
+            // compare the meta_line_id with the meta_line_id
+            if (strcasecmp(meta_line_id.c_str(), meta_line_id.c_str()) == 0)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void VCFHeader::addHeaderColumn(const string& header_column)
+    {
+        // don't add duplicates
+        //  vector<string>::iterator test = find(this->header_columns.begin(), this->header_columns.end(), header_column);
+        if (find(this->header_columns.begin(), this->header_columns.end(), header_column) == this->header_columns.end())
+        {
+            this->header_columns.push_back(header_column);
+        }
+    }
+
+
+// Reintroduce old version for freebayes
+
+map<string, vector<VariantAllele> > Variant::parsedAlternates(bool includePreviousBaseForIndels,
+                                                              bool useMNPs,
+                                                              bool useEntropy,
+                                                              float matchScore,
+                                                              float mismatchScore,
+                                                              float gapOpenPenalty,
+                                                              float gapExtendPenalty,
+                                                              float repeatGapExtendPenalty,
+                                                              const string& flankingRefLeft,
+                                                              const string& flankingRefRight) {
+
+    map<string, vector<VariantAllele> > variantAlleles;
+
+    if (isSymbolicSV()){
+        // Don't ever align SVs. It just wrecks things.
+        return this->flatAlternates();
+    }
+    // add the reference allele
+    variantAlleles[ref].push_back(VariantAllele(ref, ref, position));
+
+    // single SNP case, no ambiguity possible, no need to spend a lot of
+    // compute aligning ref and alt fields
+    if (alt.size() == 1 && ref.size() == 1 && alt.front().size() == 1) {
+        variantAlleles[alt.front()].push_back(VariantAllele(ref, alt.front(), position));
+        return variantAlleles;
+    }
+
+    // padding is used to ensure a stable alignment of the alternates to the reference
+    // without having to go back and look at the full reference sequence
+    int paddingLen = max(10, (int) (ref.size()));  // dynamically determine optimum padding length
+    for (const auto& alternate : alt) {
+        paddingLen = max(paddingLen, (int) (alternate.size()));
+    }
+    char padChar = 'Z';
+    char anchorChar = 'Q';
+    string padding(paddingLen, padChar);
+
+    // this 'anchored' string is done for stability
+    // the assumption is that there should be a positional match in the first base
+    // this is true for VCF 4.1, and standard best practices
+    // using the anchor char ensures this without other kinds of realignment
+    string reference_M;
+    if (flankingRefLeft.empty() && flankingRefRight.empty()) {
+        reference_M = padding + ref + padding;
+        reference_M[paddingLen] = anchorChar;
+    } else {
+        reference_M = flankingRefLeft + ref + flankingRefRight;
+        paddingLen = flankingRefLeft.size();
+    }
+
+    // passed to sw.Align
+    unsigned int referencePos;
+
+    string cigar;
+
+    for (const auto& alternate : alt) {
+
+      vector<VariantAllele>& variants = variantAlleles[alternate];
+      string alternateQuery_M;
+      if (flankingRefLeft.empty() && flankingRefRight.empty()) {
+	alternateQuery_M = padding + alternate + padding;
+	alternateQuery_M[paddingLen] = anchorChar;
+      } else {
+	alternateQuery_M = flankingRefLeft + alternate + flankingRefRight;
+      }
+      //const unsigned int alternateLen = alternate.size();
+
+      if (true) {
+	CSmithWatermanGotoh sw(matchScore,
+			       mismatchScore,
+			       gapOpenPenalty,
+			       gapExtendPenalty);
+	if (useEntropy) sw.EnableEntropyGapPenalty(1);
+	if (repeatGapExtendPenalty != 0){
+	  sw.EnableRepeatGapExtensionPenalty(repeatGapExtendPenalty);
+	}
+	sw.Align(referencePos, cigar, reference_M, alternateQuery_M);
+      } else {  // disabled for now
+	StripedSmithWaterman::Aligner aligner;
+	StripedSmithWaterman::Filter sswFilter;
+	StripedSmithWaterman::Alignment alignment;
+	aligner.Align(alternateQuery_M.c_str(),
+		      reference_M.c_str(),
+		      reference_M.size(), sswFilter, &alignment);
+	cigar = alignment.cigar_string;
+      }
+
+      // left-realign the alignment...
+
+      vector<pair<int, string> > cigarData = old_splitCigar(cigar);
+
+      if (cigarData.front().second != "M"
+	  || cigarData.back().second != "M"
+	  || cigarData.front().first < paddingLen
+	  || cigarData.back().first < paddingLen) {
+	cerr << "parsedAlternates: alignment does not start with match over padded sequence" << endl;
+	cerr << cigar << endl;
+	cerr << reference_M << endl;
+	cerr << alternateQuery_M << endl;
+	exit(1);
+      } else {
+	cigarData.front().first -= paddingLen;
+	cigarData.back().first -= paddingLen;;
+      }
+      //cigarData = cleanCigar(cigarData);
+      cigar = old_joinCigar(cigarData);
+
+      int altpos = 0;
+      int refpos = 0;
+
+      for (const auto& e : cigarData) {
+
+	int len = e.first;
+	const string& type = e.second;
+
+	switch (type.at(0)) {
+	case 'I':
+	  if (includePreviousBaseForIndels) {
+	    if (!variants.empty() &&
+		variants.back().ref != variants.back().alt) {
+	      VariantAllele a =
+		VariantAllele("",
+			      alternate.substr(altpos, len),
+			      refpos + position);
+	      variants.back() = variants.back() + a;
+	    } else {
+	      VariantAllele a =
+		VariantAllele(ref.substr(refpos - 1, 1),
+			      alternate.substr(altpos - 1, len + 1),
+			      refpos + position - 1);
+	      variants.push_back(a);
+	    }
+	  } else {
+	    variants.push_back(VariantAllele("",
+					     alternate.substr(altpos, len),
+					     refpos + position));
+	  }
+	  altpos += len;
+	  break;
+	case 'D':
+	  if (includePreviousBaseForIndels) {
+	    if (!variants.empty() &&
+		variants.back().ref != variants.back().alt) {
+	      VariantAllele a
+		= VariantAllele(ref.substr(refpos, len)
+				, "", refpos + position);
+	      variants.back() = variants.back() + a;
+	      } else {
+	      VariantAllele a
+		= VariantAllele(ref.substr(refpos - 1, len + 1),
+				alternate.substr(altpos - 1, 1),
+				refpos + position - 1);
+	      variants.push_back(a);
+	    }
+	  } else {
+	    variants.push_back(VariantAllele(ref.substr(refpos, len),
+					     "", refpos + position));
+	  }
+	  refpos += len;
+	  break;
+
+	  // zk has added (!variants.empty()) solves the seg fault in
+          // vcfstats, but need to test
+	case 'M':
+	  {
+	    for (int i = 0; i < len; ++i) {
+	      VariantAllele a
+		= VariantAllele(ref.substr(refpos + i, 1),
+				alternate.substr(altpos + i, 1),
+				(refpos + i + position));
+	      if (useMNPs && (!variants.empty()) &&
+		  variants.back().ref.size() == variants.back().alt.size()
+		  && variants.back().ref != variants.back().alt) {
+		  variants.back() = variants.back() + a;
+	      } else {
+		variants.push_back(a);
+	      }
+	    }
+	  }
+	  refpos += len;
+	  altpos += len;
+	  break;
+	case 'S':
+	  {
+	    refpos += len;
+	    altpos += len;
+	    break;
+	  }
+	default:
+	  {
+	    break;
+	  }
+	}
+      }
+    }
+    return variantAlleles;
+}
+
+
+} // end namespace vcf
diff --git a/contrib/vcflib-min/src/allele.cpp b/contrib/vcflib-min/src/allele.cpp
new file mode 100644
index 0000000..ada9ce6
--- /dev/null
+++ b/contrib/vcflib-min/src/allele.cpp
@@ -0,0 +1,73 @@
+#include "vcflib/allele.hpp"
+
+#include <iostream>
+#include <tuple> // for std::tie
+
+namespace vcflib {
+
+using namespace std;
+
+ostream& operator<<(ostream& out, const VariantAllele& var) {
+    out << var.position << " " << var.ref << " -> " << var.alt;
+    return out;
+}
+
+VariantAllele operator+(const VariantAllele& a, const VariantAllele& b) {
+    return VariantAllele(a.ref + b.ref, a.alt + b.alt, a.position);
+}
+
+bool operator<(const VariantAllele& a, const VariantAllele& b) {
+    return std::tie(a.position, a.ref, a.alt) < std::tie(b.position, b.ref, b.alt);
+
+}
+
+bool operator==(const VariantAllele& a, const VariantAllele& b) {
+    return a.ref == b.ref && a.alt == b.alt && a.position == b.position;
+}
+
+bool VariantAllele::is_pure_indel(void) {
+    return !ref.empty() && alt.empty() || !alt.empty() && ref.empty();
+}
+
+// shift 1bp in between the two variants to be in the "left" (first) allele
+void shift_mid_left(VariantAllele& a, VariantAllele& b) {
+    if (!b.is_pure_indel()) {
+        a.alt.append(b.alt.substr(0,1));
+        a.ref.append(b.ref.substr(0,1));
+        b.alt = b.alt.substr(1);
+        b.ref = b.ref.substr(1);
+        ++b.position;
+    } else {
+        a.alt.append(b.alt);
+        a.ref.append(b.ref);
+        b.alt.clear();
+        b.ref.clear();
+        b.position = 0;
+    }
+}
+
+// shift 1bp in between the two variants to be in the "right" (second) allele
+void shift_mid_right(VariantAllele& a, VariantAllele& b) {
+    if (!a.is_pure_indel()) {
+        b.alt = a.alt.substr(a.alt.size()-1,1) + b.alt;
+        b.ref = a.ref.substr(a.ref.size()-1,1) + b.ref;
+        a.alt = a.alt.substr(0,a.alt.size()-1);
+        a.ref = a.ref.substr(0,a.alt.size()-1);
+        --b.position;
+    } else {
+        // a is pure indel
+        // if del, the position will shift when merging
+        if (!a.ref.empty() && a.alt.empty()) {
+            b.position = a.position;
+        }
+        // else if pure ins, no change in position
+        // but in any case we will combine
+        b.alt = a.alt + b.alt;
+        b.ref = a.ref + b.ref;
+        a.alt.clear();
+        a.ref.clear();
+        a.position = 0;
+    }
+}
+
+}
diff --git a/contrib/vcflib-min/src/cigar.cpp b/contrib/vcflib-min/src/cigar.cpp
new file mode 100644
index 0000000..3451990
--- /dev/null
+++ b/contrib/vcflib-min/src/cigar.cpp
@@ -0,0 +1,227 @@
+#include "vcflib/cigar.hpp"
+#include "vcflib/join.h"
+#include "vcflib/convert.h"
+
+#include <iostream>
+
+namespace vcflib {
+
+// generates cigar from allele parsed by parsedAlternates
+// Note: this function is not used in vcflib
+string varCigar(const vector<VariantAllele>& vav, bool xForMismatch) {
+    string cigar;
+    pair<int, string> element;
+    for (const auto& va : vav) {
+        if (va.ref != va.alt) {
+            if (element.second == "M") {
+                cigar += convert(element.first) + element.second;
+                element.second = ""; element.first = 0;
+            }
+            if (va.ref.size() == va.alt.size()) {
+                cigar += convert(va.ref.size()) + (xForMismatch ? "X" : "M");
+            } else if (va.ref.size() > va.alt.size()) {
+                cigar += convert(va.ref.size() - va.alt.size()) + "D";
+            } else {
+                cigar += convert(va.alt.size() - va.ref.size()) + "I";
+            }
+        } else {
+            if (element.second == "M") {
+                element.first += va.ref.size();
+            } else {
+                element = make_pair(va.ref.size(), "M");
+            }
+        }
+    }
+    if (element.second == "M") {
+        cigar += convert(element.first) + element.second;
+    }
+    element.second = ""; element.first = 0;
+    return cigar;
+}
+
+string mergeCigar(const string& c1, const string& c2) {
+    vector<pair<int, char> > cigar1 = splitCigar(c1);
+    vector<pair<int, char> > cigar2 = splitCigar(c2);
+    // check if the middle elements are the same
+    if (cigar1.back().second == cigar2.front().second) {
+        cigar1.back().first += cigar2.front().first;
+        cigar2.erase(cigar2.begin());
+    }
+    for (const auto& c : cigar2) {
+        cigar1.push_back(c);
+    }
+    return joinCigar(cigar1);
+}
+
+vector<pair<int, char> > splitUnpackedCigar(const string& cigarStr) {
+    vector<pair<int, char> > cigar;
+    int num = 0;
+    char type = cigarStr[0];
+    // cerr << "[" << cigarStr << "]" << endl; // 18,12,14
+    for (const char c: cigarStr) {
+        // cerr << "[" << c << "]";
+        if (isdigit(c)) {
+          cerr << "Is this a valid unpacked CIGAR? <" << cigarStr << ">?" << endl;
+          exit(1);
+        }
+        if (c != type) {
+          cigar.emplace_back(num, type);
+          //cerr << num << ":" << type << ", ";
+          type = c;
+          num = 0;
+        }
+        num += 1;
+    }
+    cigar.emplace_back(num, type);
+    //cerr << num << ":" << type << ", ";
+    return cigar;
+}
+
+vector<pair<int, char> > splitCigar(const string& cigarStr) {
+    vector<pair<int, char> > cigar;
+    string number;
+    char type = '\0';
+    // strings go [Number][Type] ...
+    for (const auto c : cigarStr) {
+        if (isdigit(c)) {
+            if (type == '\0') {
+                number += c;
+            } else {
+                // signal for next token, push back the last pair, clean up
+                cigar.push_back(make_pair(atoi(number.c_str()), type));
+                number.clear();
+                type = '\0';
+                number += c;
+            }
+        } else {
+            type = c;
+        }
+    }
+    if (!number.empty() && type != '\0') {
+        cigar.push_back(make_pair(atoi(number.c_str()), type));
+    }
+    return cigar;
+}
+
+list<pair<int, char> > splitCigarList(const string& cigarStr) {
+    list<pair<int, char> > cigar;
+    string number;
+    char type = '\0';
+    // strings go [Number][Type] ...
+    for (const auto c : cigarStr) {
+        if (isdigit(c)) {
+            if (type == '\0') {
+                number += c;
+            } else {
+                // signal for next token, push back the last pair, clean up
+                cigar.push_back(make_pair(atoi(number.c_str()), type));
+                number.clear();
+                type = '\0';
+                number += c;
+            }
+        } else {
+            type = c;
+        }
+    }
+    if (!number.empty() && type != '\0') {
+        cigar.push_back(make_pair(atoi(number.c_str()), type));
+    }
+    return cigar;
+}
+
+vector<pair<int, char> > cleanCigar(const vector<pair<int, char> >& cigar) {
+    vector<pair<int, char> > cigarClean;
+    for (const auto& c : cigar) {
+        if (c.first > 0) {
+            cigarClean.push_back(c);
+        }
+    }
+    return cigarClean;
+}
+
+string joinCigar(const vector<pair<int, char> >& cigar) {
+    string cigarStr;
+    bool has_error = false;
+    for (const auto& [len, c]: cigar) {
+        if (len < 0) has_error = true;
+        if (len != 0) {
+            cigarStr += convert(len) + c;
+        }
+    }
+    if (has_error) {
+        cerr << "ERROR: joinCigar creates illegal cigar " << cigarStr << endl;
+        exit(1);
+    }
+    return cigarStr;
+}
+
+string joinCigarList(const list<pair<int, char> >& cigar) {
+    string cigarStr;
+    for (const auto& [len, c] : cigar) {
+        cigarStr += convert(len) + c;
+    }
+    return cigarStr;
+}
+
+int cigarRefLen(const vector<pair<int, char> >& cigar) {
+    int totalLen = 0;
+    for (const auto& [len, c] : cigar) {
+        if (c == 'M' || c == 'D' || c == 'X') {
+            totalLen += len;
+        }
+    }
+    return totalLen;
+}
+
+bool isEmptyCigarElement(const pair<int, char>& elem) {
+    return elem.first == 0;
+}
+
+vector<pair<int, string> > old_splitCigar(const string& cigarStr) {
+    vector<pair<int, string> > cigar;
+    string number;
+    string type;
+    // strings go [Number][Type] ...
+    for (const auto c : cigarStr) {
+        if (isdigit(c)) {
+            if (type.empty()) {
+                number += c;
+            } else {
+                // signal for next token, push back the last pair, clean up
+                cigar.push_back(make_pair(atoi(number.c_str()), type));
+                number.clear();
+                type.clear();
+                number += c;
+            }
+        } else {
+            type += c;
+        }
+    }
+    if (!number.empty() && !type.empty()) {
+        cigar.push_back(make_pair(atoi(number.c_str()), type));
+    }
+    return cigar;
+}
+
+string old_joinCigar(const vector<pair<int, string> >& cigar) {
+    string cigarStr;
+    for (const auto& [len, c] : cigar) {
+        if (len) {
+            cigarStr += convert(len) + c;
+        }
+    }
+    return cigarStr;
+}
+
+string old_joinCigar(const vector<pair<int, char> >& cigar) {
+    string cigarStr;
+    for (const auto& [len, c] : cigar) {
+        if (len) {
+            cigarStr += convert(len) + string(1, c);
+        }
+    }
+    return cigarStr;
+}
+
+
+}
diff --git a/contrib/vcflib-min/src/ssw_cpp.cpp b/contrib/vcflib-min/src/ssw_cpp.cpp
new file mode 100644
index 0000000..7ab051a
--- /dev/null
+++ b/contrib/vcflib-min/src/ssw_cpp.cpp
@@ -0,0 +1,483 @@
+/*
+    vcflib C++ library for parsing and manipulating VCF files
+
+    Copyright © 2010-2020 Erik Garrison
+    Copyright © 2020      Pjotr Prins
+
+    This software is published under the MIT License. See the LICENSE file.
+*/
+
+#include "vcflib/ssw_cpp.hpp"
+#include "vcflib/ssw.hpp"
+
+#include <sstream>
+
+namespace vcflib {
+
+static const int8_t kBaseTranslation[128] = {
+    4, 4, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4,
+    4, 4, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4,
+    4, 4, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4,
+    4, 4, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4,
+  //   A     C            G
+    4, 0, 4, 1,  4, 4, 4, 2,  4, 4, 4, 4,  4, 4, 4, 4,
+  //             T
+    4, 4, 4, 4,  3, 0, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4,
+  //   a     c            g
+    4, 0, 4, 1,  4, 4, 4, 2,  4, 4, 4, 4,  4, 4, 4, 4,
+  //             t
+    4, 4, 4, 4,  3, 0, 4, 4,  4, 4, 4, 4,  4, 4, 4, 4
+};
+
+void BuildSwScoreMatrix(const uint8_t& match_score,
+                        const uint8_t& mismatch_penalty,
+			int8_t* matrix) {
+
+  // The score matrix looks like
+  //                 // A,  C,  G,  T,  N
+  //  score_matrix_ = { 2, -2, -2, -2, -2, // A
+  //                   -2,  2, -2, -2, -2, // C
+  //                   -2, -2,  2, -2, -2, // G
+  //                   -2, -2, -2,  2, -2, // T
+  //                   -2, -2, -2, -2, -2};// N
+
+  int id = 0;
+  for (int i = 0; i < 4; ++i) {
+    for (int j = 0; j < 4; ++j) {
+      matrix[id] = ((i == j) ? match_score : static_cast<int8_t>(-mismatch_penalty));
+      ++id;
+    }
+    matrix[id] = -mismatch_penalty; // For N
+    ++id;
+  }
+
+  for (int i = 0; i < 5; ++i)
+    matrix[id++] = -mismatch_penalty; // For N
+
+}
+
+void ConvertAlignment(const s_align& s_al,
+                      const int& query_len,
+                      StripedSmithWaterman::Alignment* al) {
+  al->sw_score           = s_al.score1;
+  al->sw_score_next_best = s_al.score2;
+  al->ref_begin          = s_al.ref_begin1;
+  al->ref_end            = s_al.ref_end1;
+  al->query_begin        = s_al.read_begin1;
+  al->query_end          = s_al.read_end1;
+  al->ref_end_next_best  = s_al.ref_end2;
+
+  al->cigar.clear();
+  al->cigar_string.clear();
+
+  if (s_al.cigarLen > 0) {
+    std::ostringstream cigar_string;
+    if (al->query_begin > 0) {
+      uint32_t cigar = to_cigar_int(al->query_begin, 'S');
+      al->cigar.push_back(cigar);
+      cigar_string << al->query_begin << 'S';
+    }
+
+    for (int i = 0; i < s_al.cigarLen; ++i) {
+      al->cigar.push_back(s_al.cigar[i]);
+      cigar_string << cigar_int_to_len(s_al.cigar[i]) << cigar_int_to_op(s_al.cigar[i]);
+    }
+
+    int end = query_len - al->query_end - 1;
+    if (end > 0) {
+      uint32_t cigar = to_cigar_int(end, 'S');
+      al->cigar.push_back(cigar);
+      cigar_string << end << 'S';
+    }
+
+    al->cigar_string = cigar_string.str();
+  } // end if
+}
+
+// @Function:
+//     Calculate the length of the previous cigar operator
+//     and store it in new_cigar and new_cigar_string.
+//     Clean up in_M (false), in_X (false), length_M (0), and length_X(0).
+void CleanPreviousMOperator(
+    bool* in_M,
+    bool* in_X,
+    uint32_t* length_M,
+    uint32_t* length_X,
+    std::vector<uint32_t>* new_cigar,
+    std::ostringstream* new_cigar_string) {
+  if (*in_M) {
+    uint32_t match = to_cigar_int(*length_M, '=');
+    new_cigar->push_back(match);
+    (*new_cigar_string) << *length_M << '=';
+  } else if (*in_X){ //in_X
+    uint32_t match = to_cigar_int(*length_X, 'X');
+    new_cigar->push_back(match);
+    (*new_cigar_string) << *length_X << 'X';
+  }
+
+  // Clean up
+  *in_M = false;
+  *in_X = false;
+  *length_M = 0;
+  *length_X = 0;
+}
+
+// @Function:
+//     1. Calculate the number of mismatches.
+//     2. Modify the cigar string:
+//         differentiate matches (M) and mismatches(X).
+//         Note that SSW does not differentiate matches and mismatches.
+// @Return:
+//     The number of mismatches.
+int CalculateNumberMismatch(
+    StripedSmithWaterman::Alignment* al,
+    int8_t const *ref,
+    int8_t const *query,
+    const int& query_len) {
+
+  ref   += al->ref_begin;
+  query += al->query_begin;
+  int mismatch_length = 0;
+
+  std::vector<uint32_t> new_cigar;
+  std::ostringstream new_cigar_string;
+
+  if (al->query_begin > 0) {
+    uint32_t cigar = to_cigar_int(al->query_begin, 'S');
+    new_cigar.push_back(cigar);
+    new_cigar_string << al->query_begin << 'S';
+  }
+
+  bool in_M = false; // the previous is match
+  bool in_X = false; // the previous is mismatch
+  uint32_t length_M = 0;
+  uint32_t length_X = 0;
+
+  for (unsigned int i = 0; i < al->cigar.size(); ++i) {
+    char op = cigar_int_to_op(al->cigar[i]);
+    uint32_t length = cigar_int_to_len(al->cigar[i]);
+    if (op == 'M') {
+      for (uint32_t j = 0; j < length; ++j) {
+	if (*ref != *query) {
+	  ++mismatch_length;
+          if (in_M) { // the previous is match; however the current one is mismatche
+	    uint32_t match = to_cigar_int(length_M, '=');
+	    new_cigar.push_back(match);
+	    new_cigar_string << length_M << '=';
+	  }
+	  length_M = 0;
+	  ++length_X;
+	  in_M = false;
+	  in_X = true;
+	} else { // *ref == *query
+	  if (in_X) { // the previous is mismatch; however the current one is matche
+	    uint32_t match = to_cigar_int(length_X, 'X');
+	    new_cigar.push_back(match);
+	    new_cigar_string << length_X << 'X';
+	  }
+	  ++length_M;
+	  length_X = 0;
+	  in_M = true;
+	  in_X = false;
+	} // end of if (*ref != *query)
+	++ref;
+	++query;
+      }
+    } else if (op == 'I') {
+      query += length;
+      mismatch_length += length;
+      CleanPreviousMOperator(&in_M, &in_X, &length_M, &length_X, &new_cigar, &new_cigar_string);
+      new_cigar.push_back(al->cigar[i]);
+      new_cigar_string << length << 'I';
+    } else if (op == 'D') {
+      ref += length;
+      mismatch_length += length;
+      CleanPreviousMOperator(&in_M, &in_X, &length_M, &length_X, &new_cigar, &new_cigar_string);
+      new_cigar.push_back(al->cigar[i]);
+      new_cigar_string << length << 'D';
+    }
+  }
+
+  CleanPreviousMOperator(&in_M, &in_X, &length_M, &length_X, &new_cigar, &new_cigar_string);
+
+  int end = query_len - al->query_end - 1;
+  if (end > 0) {
+    uint32_t cigar = to_cigar_int(end, 'S');
+    new_cigar.push_back(cigar);
+    new_cigar_string << end << 'S';
+  }
+
+  al->cigar_string.clear();
+  al->cigar.clear();
+  al->cigar_string = new_cigar_string.str();
+  al->cigar = new_cigar;
+
+  return mismatch_length;
+}
+
+void SetFlag(const StripedSmithWaterman::Filter& filter, uint8_t* flag) {
+  if (filter.report_begin_position) *flag |= 0x08;
+  if (filter.report_cigar) *flag |= 0x0f;
+}
+
+// http://www.cplusplus.com/faq/sequences/arrays/sizeof-array/#cpp
+template <typename T, size_t N>
+inline size_t SizeOfArray( const T(&)[ N ] )
+{
+  return N;
+}
+
+namespace StripedSmithWaterman {
+
+Aligner::Aligner(void)
+    : score_matrix_(NULL)
+    , score_matrix_size_(5)
+    , translation_matrix_(NULL)
+    , match_score_(2)
+    , mismatch_penalty_(2)
+    , gap_opening_penalty_(3)
+    , gap_extending_penalty_(1)
+    , translated_reference_(NULL)
+    , reference_length_(0)
+{
+  BuildDefaultMatrix();
+}
+
+Aligner::Aligner(
+    const uint8_t& match_score,
+    const uint8_t& mismatch_penalty,
+    const uint8_t& gap_opening_penalty,
+    const uint8_t& gap_extending_penalty)
+
+    : score_matrix_(NULL)
+    , score_matrix_size_(5)
+    , translation_matrix_(NULL)
+    , match_score_(match_score)
+    , mismatch_penalty_(mismatch_penalty)
+    , gap_opening_penalty_(gap_opening_penalty)
+    , gap_extending_penalty_(gap_extending_penalty)
+    , translated_reference_(NULL)
+    , reference_length_(0)
+{
+  BuildDefaultMatrix();
+}
+
+Aligner::Aligner(const int8_t* score_matrix,
+                 const int&    score_matrix_size,
+	         const int8_t* translation_matrix,
+		 const int&    translation_matrix_size)
+
+    : score_matrix_(NULL)
+    , score_matrix_size_(score_matrix_size)
+    , translation_matrix_(NULL)
+    , match_score_(2)
+    , mismatch_penalty_(2)
+    , gap_opening_penalty_(3)
+    , gap_extending_penalty_(1)
+    , translated_reference_(NULL)
+    , reference_length_(0)
+{
+  score_matrix_ = new int8_t[score_matrix_size_ * score_matrix_size_];
+  memcpy(score_matrix_, score_matrix, sizeof(int8_t) * score_matrix_size_ * score_matrix_size_);
+  translation_matrix_ = new int8_t[translation_matrix_size];
+  memcpy(translation_matrix_, translation_matrix, sizeof(int8_t) * translation_matrix_size);
+}
+
+
+Aligner::~Aligner(void){
+  Clear();
+}
+
+int Aligner::SetReferenceSequence(const char* seq, const int& length) {
+
+  int len = 0;
+  if (translation_matrix_) {
+    // calculate the valid length
+    //int calculated_ref_length = static_cast<int>(strlen(seq));
+    //int valid_length = (calculated_ref_length > length)
+    //                   ? length : calculated_ref_length;
+    int valid_length = length;
+    // delete the current buffer
+    CleanReferenceSequence();
+    // allocate a new buffer
+    translated_reference_ = new int8_t[valid_length];
+
+    len = TranslateBase(seq, valid_length, translated_reference_);
+  } else {
+    // nothing
+  }
+
+  reference_length_ = len;
+  return len;
+
+
+}
+
+int Aligner::TranslateBase(const char* bases, const int& length,
+    int8_t* translated) const {
+
+  const char* ptr = bases;
+  int len = 0;
+  for (int i = 0; i < length; ++i) {
+    translated[i] = translation_matrix_[(int) *ptr];
+    ++ptr;
+    ++len;
+  }
+
+  return len;
+}
+
+
+bool Aligner::Align(const char* query, const Filter& filter,
+                    Alignment* alignment) const
+{
+  if (!translation_matrix_) return false;
+  if (reference_length_ == 0) return false;
+
+  int query_len = strlen(query);
+  if (query_len == 0) return false;
+  int8_t* translated_query = new int8_t[query_len];
+  TranslateBase(query, query_len, translated_query);
+
+  const int8_t score_size = 2;
+  s_profile* profile = ssw_init(translated_query, query_len, score_matrix_,
+                                score_matrix_size_, score_size);
+
+  uint8_t flag = 0;
+  SetFlag(filter, &flag);
+  s_align* s_al = ssw_align(profile, translated_reference_, reference_length_,
+                                 static_cast<int>(gap_opening_penalty_),
+				 static_cast<int>(gap_extending_penalty_),
+				 flag, filter.score_filter, filter.distance_filter, query_len);
+
+  alignment->Clear();
+  ConvertAlignment(*s_al, query_len, alignment);
+  alignment->mismatches = CalculateNumberMismatch(&*alignment, translated_reference_, translated_query, query_len);
+
+
+  // Free memory
+  delete [] translated_query;
+  align_destroy(s_al);
+  init_destroy(profile);
+
+  return true;
+}
+
+
+bool Aligner::Align(const char* query, const char* ref, const int& ref_len,
+                    const Filter& filter, Alignment* alignment) const
+{
+  if (!translation_matrix_) return false;
+
+  int query_len = strlen(query);
+  if (query_len == 0) return false;
+  int8_t* translated_query = new int8_t[query_len];
+  TranslateBase(query, query_len, translated_query);
+
+  // calculate the valid length
+  //int calculated_ref_length = static_cast<int>(strlen(ref));
+  //int valid_ref_len = (calculated_ref_length > ref_len)
+  //                    ? ref_len : calculated_ref_length;
+  int valid_ref_len = ref_len;
+  int8_t* translated_ref = new int8_t[valid_ref_len];
+  TranslateBase(ref, valid_ref_len, translated_ref);
+
+
+  const int8_t score_size = 2;
+  s_profile* profile = ssw_init(translated_query, query_len, score_matrix_,
+                                score_matrix_size_, score_size);
+
+  uint8_t flag = 0;
+  SetFlag(filter, &flag);
+  s_align* s_al = ssw_align(profile, translated_ref, valid_ref_len,
+                                 static_cast<int>(gap_opening_penalty_),
+				 static_cast<int>(gap_extending_penalty_),
+				 flag, filter.score_filter, filter.distance_filter, query_len);
+
+  alignment->Clear();
+  ConvertAlignment(*s_al, query_len, alignment);
+  alignment->mismatches = CalculateNumberMismatch(&*alignment, translated_ref, translated_query, query_len);
+
+  // Free memory
+  delete [] translated_query;
+  delete [] translated_ref;
+  align_destroy(s_al);
+  init_destroy(profile);
+
+  return true;
+}
+
+void Aligner::Clear(void) {
+  ClearMatrices();
+  CleanReferenceSequence();
+}
+
+void Aligner::SetAllDefault(void) {
+  score_matrix_size_     = 5;
+  match_score_           = 2;
+  mismatch_penalty_      = 2;
+  gap_opening_penalty_   = 3;
+  gap_extending_penalty_ = 1;
+  reference_length_      = 0;
+}
+
+bool Aligner::ReBuild(void) {
+  if (translation_matrix_) return false;
+
+  SetAllDefault();
+  BuildDefaultMatrix();
+
+  return true;
+}
+
+bool Aligner::ReBuild(
+    const uint8_t& match_score,
+    const uint8_t& mismatch_penalty,
+    const uint8_t& gap_opening_penalty,
+    const uint8_t& gap_extending_penalty) {
+  if (translation_matrix_) return false;
+
+  SetAllDefault();
+
+  match_score_           = match_score;
+  mismatch_penalty_      = mismatch_penalty;
+  gap_opening_penalty_   = gap_opening_penalty;
+  gap_extending_penalty_ = gap_extending_penalty;
+
+  BuildDefaultMatrix();
+
+  return true;
+}
+
+bool Aligner::ReBuild(
+    const int8_t* score_matrix,
+    const int&    score_matrix_size,
+    const int8_t* translation_matrix,
+    const int&    translation_matrix_size) {
+
+  ClearMatrices();
+  score_matrix_ = new int8_t[score_matrix_size_ * score_matrix_size_];
+  memcpy(score_matrix_, score_matrix, sizeof(int8_t) * score_matrix_size_ * score_matrix_size_);
+  translation_matrix_ = new int8_t[translation_matrix_size];
+  memcpy(translation_matrix_, translation_matrix, sizeof(int8_t) * translation_matrix_size);
+
+  return true;
+}
+
+void Aligner::BuildDefaultMatrix(void) {
+  ClearMatrices();
+  score_matrix_ = new int8_t[score_matrix_size_ * score_matrix_size_];
+  BuildSwScoreMatrix(match_score_, mismatch_penalty_, score_matrix_);
+  translation_matrix_ = new int8_t[SizeOfArray(kBaseTranslation)];
+  memcpy(translation_matrix_, kBaseTranslation, sizeof(int8_t) * SizeOfArray(kBaseTranslation));
+}
+
+void Aligner::ClearMatrices(void) {
+  delete [] score_matrix_;
+  score_matrix_ = NULL;
+
+  delete [] translation_matrix_;
+  translation_matrix_ = NULL;
+}
+} // namespace StripedSmithWaterman
+} // namespace vcflib
diff --git a/guix.scm b/guix.scm
index 5f73ed7..83b1dcd 100644
--- a/guix.scm
+++ b/guix.scm
@@ -6,50 +6,175 @@
 ;;
 ;;   guix shell -C -D -F -f guix.scm
 ;;
-;; For the tests you need /usr/bin/env. Inside the container:
+;; Build with
 ;;
-;;   mkdir -p /usr/bin ; ln -s $GUIX_ENVIRONMENT/bin/env /usr/bin/env
-;;
-;;   meson setup build/ --buildtype debug
+;;   meson setup build/ --buildtype debug --wipe
 ;;   cd build
 ;;   ninja
-;;   ninja test
+;;   meson test -t 2
+;;
+;; For a static build use
+;;
+;;   guix build -L . freebayes-static-git [--tune=native]
+;;   meson setup build/ --buildtype debug --wipe -Dprefer_system_deps=false -Dstatic=true
+;;
+;; and for debugging
+;;
+;;   guix shell -C -D -F -L . freebayes-debug
 
-(use-modules
-  ((guix licenses) #:prefix license:)
-  (guix gexp)
-  (guix packages)
-  (guix git-download)
-  (guix build-system meson)
-  (gnu packages algebra)
-  (gnu packages assembly)
-  (gnu packages base)
-  (gnu packages compression)
-  (gnu packages bioinformatics)
-  (gnu packages build-tools)
-  (gnu packages curl)
-  (gnu packages gcc)
-  (gnu packages gdb)
-  (gnu packages llvm)
-  (gnu packages ninja)
-  (gnu packages parallel)
-  (gnu packages perl)
-  (gnu packages perl6)
-  (gnu packages pkg-config)
-  (gnu packages python)
-  (srfi srfi-1)
-  (ice-9 popen)
-  (ice-9 rdelim))
+(define-module (guix)
+  #:use-module (ice-9 popen)
+  #:use-module (ice-9 rdelim)
+  #:use-module (srfi srfi-1)
+  #:use-module ((guix licenses) #:prefix license:)
+  #:use-module (guix build-system cmake) ; for vcflib
+  #:use-module (guix build-system meson)
+  #:use-module (guix download)
+  #:use-module (guix gexp)
+  #:use-module (guix git-download)
+  #:use-module (guix packages)
+  #:use-module (guix utils)
+  #:use-module (gnu packages algebra)
+  #:use-module (gnu packages assembly)
+  #:use-module (gnu packages base)
+  #:use-module (gnu packages bioinformatics)
+  #:use-module (gnu packages build-tools)
+  #:use-module (gnu packages check)
+  #:use-module (gnu packages compression)
+  #:use-module (gnu packages curl)
+  #:use-module (gnu packages gcc)
+  #:use-module (gnu packages gdb)
+  #:use-module (gnu packages haskell-xyz) ; pandoc for help files
+  #:use-module (gnu packages llvm)
+  #:use-module (gnu packages ninja)
+  #:use-module (gnu packages parallel)
+  #:use-module (gnu packages perl)
+  #:use-module (gnu packages perl6)
+  #:use-module (gnu packages pkg-config)
+  #:use-module (gnu packages python)
+  #:use-module (gnu packages python-xyz) ; for pybind11
+  #:use-module (gnu packages ruby)
+  #:use-module (gnu packages time)
+  #:use-module (gnu packages tls)
+  #:use-module (gnu packages zig))
 
 (define %source-dir (dirname (current-filename)))
 
 (define %git-commit
     (read-string (open-pipe "git show HEAD | head -1 | cut -d ' ' -f 2" OPEN_READ)))
 
+(define-public vcflib-github ;; should update upstream
+  (let ((commit
+         "9e8c0192d677bddd68eb2743ff9ec194995e92b7"
+                ))
+    (package
+     (name "vcflib-github")
+     (version (string-append "1.0.14-" (string-take commit 7)))
+     (source
+      (origin
+       (method git-fetch)
+       (uri (git-reference
+             (url "https://github.com/vcflib/vcflib/")
+             (commit commit)
+             (recursive? #t)))
+       (file-name (string-append name "-" version "-checkout"))
+       (sha256
+        (base32
+         "0jvy98q4zy7md14c2h40xpd9hng8a2070g8b3lw0rigjnfkwziyl"
+        ))))
+    (build-system cmake-build-system)
+    (arguments
+     `(#:tests? #f
+       #:configure-flags
+       ,#~(list
+           "-DCMAKE_BUILD_TYPE=Debug"
+           "-DZIG=OFF")))
+    (inputs
+     (list
+       curl
+       fastahack  ;; dev version not in Debian
+       htslib ;; disable to test local build - with local package below
+       pandoc ; for man pages
+       perl
+       python
+       python-pytest
+       pybind11
+       ruby ; for man pages
+       smithwaterman
+       tabixpp
+       time ; for tests
+       wfa2-lib ; alternative:  cmake  -DCMAKE_BUILD_TYPE=Debug -DWFA_GITMODULE=ON -DZIG=ON ..
+       xz
+       ;; zig-0.14
+       ))
+    (native-inputs
+     `(("pkg-config" ,pkg-config)))
+    (home-page "https://github.com/vcflib/vcflib/")
+    (synopsis "Library for parsing and manipulating VCF files")
+    (description "Vcflib")
+    (license license:expat))))
+
+;; Guix does not come with a static version of libdeflate
+(define-public libdeflate-static
+  (package
+    (inherit libdeflate)
+    (name "libdeflate-static")
+    (version "1.19")
+    (arguments
+     (list #:configure-flags
+           #~(list "-DLIBDEFLATE_BUILD_STATIC_LIB=YES"
+                   "-DLIBDEFLATE_BUILD_TESTS=YES")))))
+
+;; A minimal static version of htslib that does not depend on curl and openssl. This
+;; reduces the number of higher order dependencies in static linking.
+(define-public htslib-static
+  (package
+    (inherit htslib)
+    (name "htslib-static")
+    (version "1.19")
+    (source (origin
+            (method url-fetch)
+            (uri (string-append
+                  "https://github.com/samtools/htslib/releases/download/"
+                  version "/htslib-" version ".tar.bz2"))
+            (sha256
+             (base32
+              "0dh79lwpspwwfbkmllrrhbk8nkvlfc5b5ib4d0xg5ld79w6c8lc7"))))
+    (arguments
+     (substitute-keyword-arguments (package-arguments htslib)
+       ((#:configure-flags flags ''())
+        ''())))
+    (inputs
+     (list bzip2 xz))))
+
+
+(define-public vcflib-static-github
+  "Optimized for latest AMD architecture build and static deployment. These binaries can be copied to HPC."
+  (package
+    (inherit vcflib-github)
+    (name "vcflib-static-github")
+    (arguments
+     `(#:tests? #f
+       #:configure-flags
+       ,#~(list
+           "-DBUILD_STATIC=ON"
+           "-DZIG=OFF"
+           "-DCMAKE_BUILD_TYPE=Generic" ;; to optimize use guix -- tune=march-type (e.g. --tune=native)
+           "-DCMAKE_INSTALL_RPATH=")))   ; force cmake static build and do not rewrite RPATH
+    (inputs
+     (modify-inputs (package-inputs vcflib-github)
+                    (prepend
+                     `(,bzip2 "static")
+                     `(,zlib "static")
+                     `(,xz "static")
+                     libdeflate-static
+                     htslib-static)))))
+
+
 (define-public freebayes-git
   (package
     (name "freebayes-git")
-    (version (git-version "1.3.6" "HEAD" %git-commit))
+    (version (git-version "1.3.10" "HEAD" %git-commit))
     (source (local-file %source-dir #:recursive? #t))
     (build-system meson-build-system)
     (propagated-inputs
@@ -66,11 +191,9 @@
        ("simde" ,simde)
        ("smithwaterman" ,smithwaterman) ; vcflib shared lib dependency ; bundle for Debian
        ("tabixpp" ,tabixpp)    ; for htslib
-       ("vcflib" ,vcflib)      ; for testing freebayes-parallel
+       ("vcflib-github" ,vcflib-github)  ; for includes and testing freebayes-parallel
        ("wfa2-lib" ,wfa2-lib)  ; vcflib shared lib dependency
-       ("which" ,which)        ; for version
-       ("xz-static" ,xz "static")     ; for static builds
-       ("zlib-static" ,zlib "static")))
+       ("which" ,which)))        ; for version
     (native-inputs
      `(
        ("meson" ,meson)
@@ -93,19 +216,86 @@
        ("bzip2" ,bzip2)    ; libz2 part of htslib
        ))
     (arguments
-     `(#:phases (modify-phases %standard-phases
-       ;; add timeout extension for slower processors
-       (replace 'check
-                (lambda _
-                 (invoke "meson" "test" "--timeout-multiplier" "5"))))))
-     (synopsis "freebayes haplotype-based genetic variant caller")
-     (description
-      "freebayes is a Bayesian genetic variant detector designed to find small
+     (list
+      #:configure-flags
+      #~(list
+         ;; "--buildtype debug"
+         ;; "--buildtype release"
+         "-Dprefer_system_deps=true" ; we use local files
+         )
+      #:phases
+           #~(modify-phases %standard-phases
+                  (add-after 'unpack 'includes
+                    (lambda _
+                      (substitute* "meson.build"
+                                   (("vcflib_inc = files\\(\\)")
+                                    (string-append "vcflib_inc = include_directories('" #$vcflib-github "/include/vcflib')")))))
+                  ;; add timeout extension for slower processor
+                  (replace 'check
+                    (lambda _
+                      (invoke "meson" "test" "--timeout-multiplier" "5"))))))
+    (synopsis "freebayes haplotype-based genetic variant caller")
+    (description
+     "freebayes is a Bayesian genetic variant detector designed to find small
 polymorphisms, specifically SNPs (single-nucleotide polymorphisms), indels
 (insertions and deletions), MNPs (multi-nucleotide polymorphisms), and complex
 events (composite insertion and substitution events) smaller than the length of
 a short-read sequencing alignment.")
-     (home-page "https://github.com/freebayes/freebayes")
-     (license license:expat)))
+    (home-page "https://github.com/freebayes/freebayes")
+    (license license:expat)))
+
+(define-public freebayes-static-git
+  "Optimized for latest AMD architecture build and static deployment. These binaries can be copied to HPC."
+  (package
+    (inherit freebayes-git)
+    (name "freebayes-static-git")
+    (arguments
+     (list
+      #:tests? #f
+      #:configure-flags
+      #~(list
+         ;; "--buildtype debug"
+         ;; "--buildtype release"
+         "-Dprefer_system_deps=false" ; we use local files
+         "-Dstatic=true")  ; force static build and do not rewrite RPATH
+      #:phases
+      #~(modify-phases %standard-phases
+                  (delete 'shrink-runpath)
+                  (add-after 'unpack 'includes
+                    (lambda _
+                      (substitute* "meson.build"
+                                   (("vcflib_inc = files\\(\\)")
+                                    (string-append "vcflib_inc = include_directories('" #$vcflib-static-github "/include/vcflib')"))))))))
+    (inputs
+     (modify-inputs (package-inputs freebayes-git)
+                    (delete (list
+                             htslib
+                             vcflib
+                             vcflib-github))
+                    (prepend
+                     `(,bzip2 "static")
+                     `(,zlib "static")
+                     `(,xz "static")
+                     libdeflate-static
+                     ;; vcflib-static-github -- no longer used
+                     htslib-static)))))
+
+(define-public freebayes-debug
+  (package
+    (inherit freebayes-static-git)
+    (name "freebayes-debug")
+    (build-system meson-build-system)
+    ;; (inputs
+    ;;  (modify-inputs (package-inputs freebayes-git)
+    ;;                 (prepend
+    ;;                  meson)))
+    (arguments
+     `(#:tests? #f
+       #:phases (modify-phases %standard-phases
+                               (delete 'configure)
+                               (delete 'build)
+                               (delete 'package)
+                               (delete 'check)
+                               (delete 'install))))))
 
-freebayes-git
+freebayes-static-git
diff --git a/meson.build b/meson.build
index d7b253a..6a2aac9 100644
--- a/meson.build
+++ b/meson.build
@@ -9,26 +9,33 @@
 #
 # to build static binary for release
 #
-#     meson build -Dstatic=true -Dprefer_system_deps=false --buildtype release
+#      meson setup build/ -Dstatic=true -Dprefer_system_deps=false --buildtype release
+#
+# or for debugging
+#
+#      meson setup build/ --buildtype debug --wipe -Dprefer_system_deps=false -Dstatic=true
 #
 # See also meson_options.txt and contrib/README.md
+#
+# We use guix.scm for development -- see the header of the guix.scm file
 
 project('freebayes', ['cpp', 'c'],
-        version : '1.3.9',
+        version : '1.3.10',
         default_options : ['warning_level=1', 'cpp_std=c++17', 'optimization=3'])
+add_global_arguments('-Wno-maybe-uninitialized', language : 'cpp') # for ttmath warnings
+
 static_build = get_option('static')
 
 # used by Debian: add_global_arguments('-D_GLIBCXX_ASSERTIONS=1', language : 'cpp')
 
 cc = meson.get_compiler('cpp')
 
-# fastahack_dep = cc.find_library('libfastahack')
 lzma_dep = dependency('liblzma', static: static_build)
 thread_dep = dependency('threads', static: static_build)
-wfa2lib_dep = cc.find_library('libwfa2') # need to link for vcflib code
+wfa2lib_dep = cc.find_library('wfa2') # need to link for vcflib code
 zlib_dep = dependency('zlib', static: static_build)
 htslib_dep = dependency('htslib', static: static_build, required: false)
-vcflib_dep = cc.find_library('libvcflib', required: false)
+vcflib_dep = cc.find_library('vcflib', required: false, static: static_build)
 seqlib_dep = dependency('libseqlib', static: static_build, required: false)
 tabixpp_dep = cc.find_library('tabixpp', required: false, static: static_build)
 
@@ -41,9 +48,9 @@ else
   vcflib_inc = files()
 endif
 
-
-if get_option('prefer_system_deps') # defaults to true, otherwise use git modules and local sources
+if get_option('prefer_system_deps')
   fastahack_dep = cc.find_library('libfastahack', required: true) # also link in for vcflib
+  fastahack_src = files()
   smithwaterman_dep = cc.find_library('libsmithwaterman')
   if fs.is_dir('/usr/include/fastahack')
     message('Using /usr/include/fastahack dir')
@@ -51,20 +58,31 @@ if get_option('prefer_system_deps') # defaults to true, otherwise use git module
   else
     fastahack_inc = files()
   endif
-
+  vcflib_inc = files()
+  vcflib_src = files()
+  smithwaterman_inc = files()
+  smithwaterman_src = files()
 else
   # uses the minimal local git submodules or checkout trees in ./contrib/
   # see `git submodule`
   # htslib and vcflib have to come from the underlying distro
-  fastahack_dep = dependency('', required : false)
+
+  vcflib_dep = dependency('', required : false) # from this tree
+  fastahack_dep = dependency('', required : false) # from this tree
+  smithwaterman_dep = dependency('', required : false) # from this tree
+
+  vcflib_inc = include_directories('contrib/vcflib-min/include')
+  vcflib_src = files(
+      'contrib/vcflib-min/src/Variant.cpp',
+      'contrib/vcflib-min/src/ssw_cpp.cpp',
+      'contrib/vcflib-min/src/cigar.cpp',
+      'contrib/vcflib-min/src/allele.cpp'
+  )
 
   fastahack_inc = include_directories('contrib/fastahack')
   fastahack_src = files(
       'contrib/fastahack/Fasta.cpp',
-  )
-
-  multichoose_inc = include_directories('contrib/multichoose')
-  multichoose_src = files(
+      'contrib/fastahack/split.cpp',
   )
 
   smithwaterman_inc = include_directories('contrib/smithwaterman')
@@ -74,6 +92,7 @@ else
       'contrib/smithwaterman/Repeats.cpp',
       'contrib/smithwaterman/LeftAlign.cpp',
       'contrib/smithwaterman/IndelAllele.cpp',
+      # 'contrib/smithwaterman/SWMain.cpp',
   )
 
 endif # build git modules
@@ -130,9 +149,13 @@ freebayes_common_src = files(
     'src/SegfaultHandler.cpp',
     'src/Utility.cpp',
     )
+
 freebayes_src = files('src/freebayes.cpp')
 bamleftalign_src = files('src/bamleftalign.cpp')
 
+freebayes_common_src += vcflib_src
+freebayes_common_src += smithwaterman_src
+
 # Include paths
 incdir = include_directories(
     'src',
@@ -149,11 +172,12 @@ extra_cpp_args = cc.get_supported_arguments(
 
 freebayes_lib = static_library(
     'freebayes_common',
-    freebayes_common_src,
-    include_directories : [incdir, vcflib_inc, fastahack_inc],
+    [freebayes_common_src, fastahack_src],
+    pic: false,
+    include_directories : [incdir, vcflib_inc, smithwaterman_inc, fastahack_inc],
     cpp_args : extra_cpp_args,
-    dependencies : [zlib_dep, lzma_dep, thread_dep, htslib_dep, tabixpp_dep,
-                    vcflib_dep, wfa2lib_dep, seqlib_dep],
+    dependencies : [vcflib_dep, zlib_dep, lzma_dep, thread_dep, htslib_dep, tabixpp_dep,
+                    wfa2lib_dep, seqlib_dep],
     install : false,
     )
 
@@ -170,15 +194,16 @@ executable('freebayes',
            link_args: link_arguments,
            dependencies: [zlib_dep,
                           lzma_dep,
+                          wfa2lib_dep,
+                          vcflib_dep,
                           thread_dep,
                           htslib_dep,
                           tabixpp_dep,
-                          vcflib_dep,
                           seqlib_dep,
                           fastahack_dep, # for vcflib
-                          smithwaterman_dep, # for vcflib
-                          wfa2lib_dep],
-           link_with : freebayes_lib,
+                          smithwaterman_dep # for vcflib
+                          ],
+           link_with : [freebayes_lib],
            install: true
           )

diff --git a/src/multipermute.h b/src/multipermute.h
new file mode 100644
index 0000000..b0eeec3
--- /dev/null
+++ b/src/multipermute.h
@@ -0,0 +1,185 @@
+/* 
+
+multipermute.h  -- multiset permutations for generic vectors
+
+Follows 'Algorithm 1' from "Loopless Generation of Multiset Permutations using
+a Constant Number of Variables by Prefix Shifts."  Aaron Williams, 2009
+
+author: Erik Garrison <erik.garrison@bc.edu>
+last revised: 2010-04-16
+
+Copyright (c) 2010 by Erik Garrison
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+
+#include <vector>
+#include <algorithm>
+
+template <class T>
+class ListElement { 
+
+public:
+    T value;
+    ListElement<T>* next;
+
+    ListElement<T>() { }
+
+    ListElement<T>(T val, ListElement<T>* n) {
+        value = val;
+        next = n;
+    }
+
+    ListElement<T>* nth(int n) {
+        ListElement<T>* o = this;
+        int i = 0;
+        while (i < n && o->next != NULL) {
+            o = o->next;
+            ++i;
+        }
+        return o;
+    }
+
+    ~ListElement<T>() {
+        if (next != NULL) {
+            delete next;
+        }
+    }
+
+};
+
+template <class T>
+ListElement<T>* list_init(std::vector<T>& multiset) {
+    std::sort(multiset.begin(), multiset.end()); // ensures proper non-increasing order
+    typename std::vector<T>::const_iterator item = multiset.begin();
+    ListElement<T>* h = new ListElement<T>(*item, NULL);
+    ++item;
+    while (item != multiset.end()) {
+        h = new ListElement<T>(*item, h);
+        ++item;
+    }
+    return h;
+}
+
+template <class T>
+std::vector<T> linked_list_to_vector(ListElement<T>* h) {
+    ListElement<T>* o = h;
+    std::vector<T> l;
+    while (o != NULL) {
+        l.push_back(o->value);
+        o = o->next;
+    }
+    return l;
+}
+
+// provides multiset permutations out of the std::vector multiset
+template <class T>
+std::vector< std::vector<T> > multipermute(std::vector<T>& multiset) {
+
+    std::vector< std::vector<T> > results;
+
+    ListElement<T>* h = list_init(multiset);
+    ListElement<T>* i = h->nth(multiset.size() - 2);
+    ListElement<T>* j = h->nth(multiset.size() - 1);
+    ListElement<T>* s;
+    ListElement<T>* t;
+
+    results.push_back(linked_list_to_vector(h));
+
+    while (j->next != NULL || j->value < h->value) {
+        if (j->next != NULL && i->value >= j->next->value) {
+            s = j;
+        } else {
+            s = i;
+        }
+        t = s->next;
+        s->next = t->next;
+        t->next = h;
+        if (t->value < h->value) {
+            i = t;
+        }
+        j = i->next;
+        h = t;
+        results.push_back(linked_list_to_vector(h));
+    }
+
+    delete h;
+
+    return results;
+
+}
+
+
+template <class T>
+class MultisetPermutations {
+
+public:
+
+    std::vector<T> multiset;
+    ListElement<T> *h, *i, *j, *s, *t;
+    bool firstPermutation;
+
+    MultisetPermutations(std::vector<T>& m_multiset)
+        : multiset(m_multiset)
+        , firstPermutation(true)
+    {
+        h = list_init(multiset);
+        i = h->nth(multiset.size() - 2);
+        j = h->nth(multiset.size() - 1);
+    }
+
+    std::vector<T> next(void) {
+
+        if (firstPermutation) {
+            firstPermutation = false;
+            return linked_list_to_vector(h);
+        }
+
+        while (j->next != NULL || j->value < h->value) {
+            if (j->next != NULL && i->value >= j->next->value) {
+                s = j;
+            } else {
+                s = i;
+            }
+            t = s->next;
+            s->next = t->next;
+            t->next = h;
+            if (t->value < h->value) {
+                i = t;
+            }
+            j = i->next;
+            h = t;
+            return linked_list_to_vector(h);
+        }
+
+        std::vector<T> empty;
+        return empty;
+
+    }
+
+    ~MultisetPermutations(void) {
+        delete h;
+    }
+
+};
diff --git a/src/version_git.h b/src/version_git.h
index 7ce468d..66bea7c 100644
--- a/src/version_git.h
+++ b/src/version_git.h
@@ -1,4 +1,4 @@
 #ifndef VERSION_GIT_H
 #define VERSION_GIT_H
-#define VERSION_GIT "v1.3.8"
+#define VERSION_GIT "v1.3.10"
 #endif /* VERSION_GIT_H */
diff --git a/test/regression/NA12878.chr22.tiny.vcf b/test/regression/NA12878.chr22.tiny.vcf
index a7212c7..b3d56be 100644
--- a/test/regression/NA12878.chr22.tiny.vcf
+++ b/test/regression/NA12878.chr22.tiny.vcf
@@ -170,7 +170,7 @@ q	7216	.	G	C	6.93087e-15	.	AB=0;ABP=0;AC=0;AF=0;AN=2;AO=2;CIGAR=1X;DP=35;DPB=35;
 q	7239	.	T	G	3.55643e-15	.	AB=0;ABP=0;AC=0;AF=0;AN=2;AO=2;CIGAR=1X;DP=37;DPB=37;DPRA=0;EPP=7.35324;EPPR=4.56135;GTI=0;LEN=1;MEANALT=1;MQM=60;MQMR=58.4571;NS=1;NUMALT=1;ODDS=46.9933;PAIRED=1;PAIREDR=1;PAO=0;PQA=0;PQR=0;PRO=0;QA=30;QR=1028;RO=35;RPL=0;RPP=7.35324;RPPR=4.56135;RPR=2;RUN=1;SAF=0;SAP=7.35324;SAR=2;SRF=13;SRP=8.03571;SRR=22;TYPE=snp;technology.ILLUMINA=1	GT:DP:AD:RO:QR:AO:QA:GL	0/0:37:35,2:35:1028:2:30:0,-8.28813,-89.1384
 q	7251	.	T	G	1.09014e-11	.	AB=0;ABP=0;AC=0;AF=0;AN=2;AO=5;CIGAR=1X;DP=35;DPB=35;DPRA=0;EPP=3.44459;EPPR=3.08518;GTI=0;LEN=1;MEANALT=2;MQM=60;MQMR=58.1379;NS=1;NUMALT=1;ODDS=26.7111;PAIRED=1;PAIREDR=1;PAO=0;PQA=0;PQR=0;PRO=0;QA=81;QR=733;RO=29;RPL=2;RPP=3.44459;RPPR=9.07545;RPR=3;RUN=1;SAF=1;SAP=6.91895;SAR=4;SRF=11;SRP=6.67934;SRR=18;TYPE=snp;technology.ILLUMINA=1	GT:DP:AD:RO:QR:AO:QA:GL	0/0:35:29,5:29:733:5:81:0,-2.99345,-55.7544
 q	7263	.	C	A	1.9121e-14	.	AB=0;ABP=0;AC=0;AF=0;AN=2;AO=4;CIGAR=1X;DP=34;DPB=34;DPRA=0;EPP=11.6962;EPPR=5.61607;GTI=0;LEN=1;MEANALT=1;MQM=60;MQMR=57;NS=1;NUMALT=1;ODDS=34.1906;PAIRED=1;PAIREDR=1;PAO=0;PQA=0;PQR=0;PRO=0;QA=71;QR=829;RO=30;RPL=0;RPP=11.6962;RPPR=13.4334;RPR=4;RUN=1;SAF=0;SAP=11.6962;SAR=4;SRF=11;SRP=7.64277;SRR=19;TYPE=snp;technology.ILLUMINA=1	GT:DP:AD:RO:QR:AO:QA:GL	0/0:34:30,4:30:829:4:71:0,-3.66769,-65.1422
-q	7264	.	AAG	AAA	3.99445e-14	.	AB=0;ABP=0;AC=0;AF=0;AN=2;AO=4;CIGAR=2M1X;DP=36;DPB=36;DPRA=0;EPP=5.18177;EPPR=5.45321;GTI=0;LEN=1;MEANALT=1;MQM=60;MQMR=55.9375;NS=1;NUMALT=1;ODDS=32.4606;PAIRED=1;PAIREDR=1;PAO=0;PQA=0;PQR=0;PRO=0;QA=78;QR=1103;RO=32;RPL=1;RPP=5.18177;RPPR=5.45321;RPR=3;RUN=1;SAF=0;SAP=11.6962;SAR=4;SRF=12;SRP=7.35324;SRR=20;TYPE=snp;technology.ILLUMINA=1	GT:DP:AD:RO:QR:AO:QA:GL	0/0:36:32,4:32:1103:4:78:0,-3.6223,-88.6792
+q	7264	.	AAG	AAA	3.9952e-14	.	AB=0;ABP=0;AC=0;AF=0;AN=2;AO=4;CIGAR=2M1X;DP=36;DPB=36;DPRA=0;EPP=5.18177;EPPR=5.45321;GTI=0;LEN=1;MEANALT=1;MQM=60;MQMR=55.9375;NS=1;NUMALT=1;ODDS=32.4606;PAIRED=1;PAIREDR=1;PAO=0;PQA=0;PQR=0;PRO=0;QA=78;QR=1103;RO=32;RPL=1;RPP=5.18177;RPPR=5.45321;RPR=3;RUN=1;SAF=0;SAP=11.6962;SAR=4;SRF=12;SRP=7.35324;SRR=20;TYPE=snp;technology.ILLUMINA=1	GT:DP:AD:RO:QR:AO:QA:GL	0/0:36:32,4:32:1103:4:78:0,-3.6223,-88.6792
 q	7275	.	TCAAAAAAAAAAAAAAAAAGACAAAGTAGT	TCAAAAAAAAAAAAAAAGACAAAGTAGT	6.99873	.	AB=0.142857;ABP=26.2761;AC=1;AF=0.5;AN=2;AO=3;CIGAR=2M2D26M;DP=21;DPB=20.6;DPRA=0;EPP=9.52472;EPPR=12.7819;GTI=0;LEN=2;MEANALT=7;MQM=60;MQMR=60;NS=1;NUMALT=1;ODDS=1.38881;PAIRED=1;PAIREDR=1;PAO=0;PQA=0;PQR=0;PRO=0;QA=97;QR=292;RO=8;RPL=2;RPP=3.73412;RPPR=12.7819;RPR=1;RUN=1;SAF=1;SAP=3.73412;SAR=2;SRF=0;SRP=20.3821;SRR=8;TYPE=del;technology.ILLUMINA=1	GT:DP:AD:RO:QR:AO:QA:GL	0/1:21:8,3:8:292:3:97:-5.45203,0,-23.0425
 q	7612	.	G	T	9.93756e-15	.	AB=0;ABP=0;AC=0;AF=0;AN=2;AO=2;CIGAR=1X;DP=38;DPB=38;DPRA=0;EPP=3.0103;EPPR=3.0103;GTI=0;LEN=1;MEANALT=1;MQM=60;MQMR=60;NS=1;NUMALT=1;ODDS=36.6724;PAIRED=1;PAIREDR=1;PAO=0;PQA=0;PQR=0;PRO=0;QA=77;QR=1368;RO=36;RPL=2;RPP=7.35324;RPPR=5.18177;RPR=0;RUN=1;SAF=1;SAP=3.0103;SAR=1;SRF=17;SRP=3.25157;SRR=19;TYPE=snp;technology.ILLUMINA=1	GT:DP:AD:RO:QR:AO:QA:GL	0/0:38:36,2:36:1368:2:77:0,-4.13031,-116.079
 q	7812	.	C	A	8.75099e-15	.	AB=0;ABP=0;AC=0;AF=0;AN=2;AO=2;CIGAR=1X;DP=34;DPB=34;DPRA=0;EPP=3.0103;EPPR=3.28173;GTI=0;LEN=1;MEANALT=1;MQM=60;MQMR=59.4688;NS=1;NUMALT=1;ODDS=41.6197;PAIRED=1;PAIREDR=0.96875;PAO=0;PQA=0;PQR=0;PRO=0;QA=30;QR=1178;RO=32;RPL=1;RPP=3.0103;RPPR=5.45321;RPR=1;RUN=1;SAF=0;SAP=7.35324;SAR=2;SRF=18;SRP=4.09604;SRR=14;TYPE=snp;technology.ILLUMINA=1	GT:DP:AD:RO:QR:AO:QA:GL	0/0:34:32,2:32:1178:2:30:0,-7.38504,-103.367
