=== added file '.gitattributes'
--- old/.gitattributes	1970-01-01 00:00:00 +0000
+++ new/.gitattributes	2015-08-23 15:37:05 +0000
@@ -0,0 +1,22 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+# Custom for Visual Studio
+*.cs     diff=csharp
+*.sln    merge=union
+*.csproj merge=union
+*.vbproj merge=union
+*.fsproj merge=union
+*.dbproj merge=union
+
+# Standard to msysgit
+*.doc	 diff=astextplain
+*.DOC	 diff=astextplain
+*.docx diff=astextplain
+*.DOCX diff=astextplain
+*.dot  diff=astextplain
+*.DOT  diff=astextplain
+*.pdf  diff=astextplain
+*.PDF	 diff=astextplain
+*.rtf	 diff=astextplain
+*.RTF	 diff=astextplain

=== added file '.gitignore'
--- old/.gitignore	1970-01-01 00:00:00 +0000
+++ new/.gitignore	2020-12-10 18:04:42 +0000
@@ -0,0 +1,52 @@
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# =========================
+# Operating System Files
+# =========================
+
+# OSX
+# =========================
+
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon

+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# Object files
+*.o
+
+# Binaries
+bin/
+
+# Editor backup filels
+*~

=== added file 'README.md'
--- old/README.md	1970-01-01 00:00:00 +0000
+++ new/README.md	2021-04-26 21:01:12 +0000
@@ -0,0 +1,113 @@
+KMC
+=
+[![GitHub downloads](https://img.shields.io/github/downloads/refresh-bio/kmc/total.svg?style=flag&label=GitHub%20downloads)](https://github.com/refresh-bio/KMC/releases)
+[![Bioconda downloads](https://img.shields.io/conda/dn/bioconda/kmc.svg?style=flag&label=Bioconda%20downloads)](https://anaconda.org/bioconda/kmc)
+
+KMC is a disk-based programm for counting k-mers from (possibly gzipped) FASTQ/FASTA files.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+For accessing k-mers stored in database produced by KMC there is an API (kmc_api directory). Note that for KMC versions 0.x and 1.x dababase format differs from produced by KMC version 2.x. From version 2.2.0 API is unified for both formats and all new features/bug fixes are present only for 2.x branch (standalone API for older KMC version is not longer under development, so new version of API  should be used even for databases produced by older KMC version).
+
+Installation
+=
+The following libraries come with KMC in a binary (64-bit compiled for x86 platform) form.
+If your system needs other binary formats, you should put the following libraries in kmer_counter/libs:
+* libbzip2 - for support for bzip2-compressed input FASTQ/FASTA files (http://www.bzip.org/)
+* zlib - for support for gzip-compressed input FASTQ/FASTA files (http://www.zlib.net/)
+
+The following libraries come with KMC in a source coude form.
+ * pybind11 - used to create python wrapper of KMC API (https://github.com/pybind/pybind11)
+
+If needed, you can also redefine maximal length of k-mer, which is 256 in the current version.
+
+Note: KMC is highly optimized and spends only as many bytes for k-mer (rounded up to 8) as
+necessary, so using large values of MAX_K does not affect the KMC performance for short k-mers.
+
+Some parts of KMC use C++14 features, so you need a compatible C++ compiler, e.g., gcc 4.9+ or clang 3.4+
+
+After that, you can run make to compile kmc and kmc_dump applications.
+
+##### Additional infromation for MAC OS installation
+
+For compilation under MAC OS there is makefile_mac.
+Usage:
+
+    make -f makefile_mac
+
+There might be a need to change g++ path in makefile_mac.
+If needed we recommend install g++ with brew (http://brew.sh/).
+
+Note that KMC creates a hundreds of temporary files, while default limit for opened files is small for under MAC OS platform.
+To increase this number use following command before running KMC:
+
+    ulimit -n 2048
+
+Directory structure
+=
+ * bin           - main directory of KMC (programs after compilation will be stored here)
+ * kmer_counter  - source code of kmc program
+ * kmer_counter/libs - compiled binary versions of libraries used by KMC
+ * kmc_api       - C++ source codes implementing API; must be used by any program that wants to process databases produced by kmc
+ * kmc_dump      - source codes of kmc_dump program listing k-mers in databases produced by kmc
+ * py_kmc_api    - python wrapper for kmc API
+
+Python wrapper for KMC API
+=
+Python wrapper for KMC API was created using pybind11.
+**Warning:** python binding is experimental. The library used to create binding as well as public interface may change in the future.
+**Warning 2:** python wrapper for C++ KMC API is **much** slower (much, much more than I have been expecting) than native C++ API. In fact the first attempt to create python wrapper was to use [ctypes](https://docs.python.org/2/library/ctypes.html), but it turned out it was even slower than in case when pybind11 is used.
+The wrapper is designed and was tested only for python3. The main goal was to make it as similar to C++ API as possible. For this reason the API may be not [pythonic]
+(https://blog.startifact.com/posts/older/what-is-pythonic.html) enough for regular python programmer. Suggestions or pull requests to make it more robust are welcome.
+
+Python module wrapping KMC API must be compiled.
+ * for windows there is a visual studio project (note that there will be probably the need to change include directories and library directories to point python include and libs location)
+ * for linux one should run ```make py_kmc_api```
+ * for mac on should run ```make -f makefile_mac py_kmc_api```
+
+As a result of pybind11 *.so file (for linux and mac os) or *.pyd (for windows) is created and may be used as a python module. *.pyd file is in fact DLL file, the only difference is its extension.
+  * for windows following file is created: x64/Release/py_kmc_api.pyd
+  * for linux/mac os the following file is created: bin/py_kmc_api`python3-config --extension-suffix`
+
+To be able to use this file one should make it visible for python. One way to do this is to extend PYTHONPATH environment variable.
+For linux/mac os one may just
+```
+source py_kmc_api/set_path.sh
+```
+while, for windows:
+```
+py_kmc_api\set_path.bat
+```
+it will export apropriate file. The example of Python wrapper for KMC API is presented in file:
+py_kmc_api/py_kmc_dump.py
+
+Detailed API describtion is avaiable at [wiki](https://github.com/refresh-bio/KMC/wiki/Python-wrapper-for-KMC-API)
+
+
+Binaries
+=
+After compilation you will obtain two binaries:
+* bin/kmc - the main program for counting k-mer occurrences
+* bin/kmc_dump - the program listing k-mers in a database produced by kmc
+* bin/kmc_tools - the program allowing to manipulate kmc databases (set operations, transformations, etc.)
+
+
+License
+=
+* KMC software distributed under GNU GPL 3 licence.
+
+* libbzip2 is open-source (BSD-style license)
+
+* gzip is free, open-source
+
+* pybind11 (https://github.com/pybind/pybind11) is open-source (BDS-style license)
+
+In case of doubt, please consult the original documentations.
+
+Warranty
+=
+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,
+TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING
+THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+

=== added directory 'doc'
=== added directory 'doc/kmc_tools'
=== added file 'doc/kmc_tools/complex.tex'
--- old/doc/kmc_tools/complex.tex	1970-01-01 00:00:00 +0000
+++ new/doc/kmc_tools/complex.tex	2020-12-10 18:04:42 +0000
@@ -0,0 +1,71 @@
+\clearpage
+\section{complex operation}
+\label{sec:complex}
+
+Complex operation allows to define operations for more than 2 input $k$-mer sets. \\
+Command-line syntax: \\
+kmc\_tools [global\_params] complex $<$operations\_definition\_file$>$ \\
+
+where \textsf{operations\_definition\_file} is a path to the file which defines input sets and operations. It is a text file with the following syntax: \\ \\
+\fbox{
+	\parbox{\textwidth}{
+		INPUT:\\
+		$<$input1$>$ = $<$input1\_db\_path$>$ [params]\\
+		$<$input2$>$ = $<$input2\_db\_path$>$ [params]\\
+		...\\
+		$<$inputN$>$ = $<$inputN\_db\_path$>$ [params]\\
+		OUTPUT:\\
+		$<$out\_db\_path$>$ = $<$ref\_input$>$ $<$oper [c\_mode]$>$ $<$ref\_input$>$ [$<$oper[c\_mode]$>$ $<$ref\_input$>$ [...]] \\
+		\textbf{[}OUTPUT\_PARAMS: \\
+		$<$output\_params$>$]
+		
+	}
+}\\ 
+
+where: 
+\begin{itemize}
+	\item \textsf{input1, input2, ..., inputN} --- names of inputs used to define operation,
+	\item \textsf{input1\_db\_path, input2\_db\_path, inputN\_db\_path} --- paths of $k$-mer sets,
+	\item \textsf{out\_db\_path} --- path of the output database,
+	\item \textsf{ref\_input} is one of \textsf{input1, input2, ..., inputN}, 
+	\item \textsf{oper} is one of \textsf{\{*,-,$\sim$,+\}}, the meaning is as follows:
+	\begin{itemize}
+		\item[\tiny$\bullet$] \textsf{*} --- intersect,
+		\item[\tiny$\bullet$] \textsf{-} --- kmers\_subtract,
+		\item[\tiny$\bullet$] \textsf{$\sim$} --- counters\_subtract,
+		\item[\tiny$\bullet$] \textsf{+} --- union.
+	\end{itemize}
+	\item \textsf{c\_mode} --- redefine default counter calculation mode (available values: min, max, diff, sum, left, right).
+\end{itemize}
+
+For detailed description about operations and counter calculation mode see \hyperref[sec:simple]{section \ref{sec:simple}}
+
+For each input there are additional parameters which can be set:
+\begin{itemize}
+	\item \textsf{-ci<value>} --- exclude $k$-mers occurring less than <value> times,
+	\item \textsf{-cx<value>} --- exclude $k$-mers occurring more than <value> times.	
+\end{itemize}
+
+If additional parameters are not given they are taken from the appropriate input database. 
+Operator \textsf{*} has the highest priority. Other operators has equal priorities. Order of operations can be changed with parentheses.
+
+Available \textsf{output\_params}:
+\begin{itemize}
+	\item \textsf{-ci$<$value$>$} --- exclude $k$-mers occurring less than <value> times,
+	\item \textsf{-cx$<$value$>$} --- exclude $k$-mers occurring more than <value> times,
+	\item \textsf{-cs$<$value$>$} --- maximal value of a counter.
+\end{itemize}
+If the \textsf{output\_params} are not specified they are deduced based on input parameters.
+\subsection*{example}
+\fbox{
+	\parbox{\textwidth}{
+		INPUT:\\
+		set1 = kmc\_o1 -ci5\\
+		set2 = kmc\_o2\\
+		set3 = kmc\_o3 -ci10 -cx100\\
+		OUTPUT:\\
+		result = (set3 + min set1) * set2 \\	
+		OUTPUT\_PARAMS: \\
+		-ci4 -cx80 -cs1000 \\
+	}
+}\\ \\

=== added file 'doc/kmc_tools/filter_operation.tex'
--- old/doc/kmc_tools/filter_operation.tex	1970-01-01 00:00:00 +0000
+++ new/doc/kmc_tools/filter_operation.tex	2020-12-10 18:04:42 +0000
@@ -0,0 +1,50 @@
+\clearpage
+\section{filter}
+\label{sec:filter_operation}
+
+This operation works with input FASTQ/FASTA files and a database produced by \textsf{KMC}.
+It removes from the input read set those reads which does not contain specified number of $k$-mers in the input \textsf{KMC} database.
+Currently, read names are completely ignored by kmc\_tools (though it may change in the future).
+
+Syntax: \\
+kmc\_tools [global\_params] filter [filter\_params] <kmc\_input\_db> [kmc\_input\_db\_params] <input\_read\_set> [input\_read\_set\_params]  <output\_read\_set> [output\_read\_set\_params] \\
+
+where:
+
+\begin{itemize}
+	\item \textsf{kmc\_input\_db} --- path to database generated by \textsf{KMC},
+	\item \textsf{input\_read\_set} --- path to input set of reads,
+	\item \textsf{output\_read\_set} --- path to set output of reads.
+\end{itemize}
+
+filter\_params are:
+\begin{itemize}
+	\item \textsf{-t} --- trim reads on first invalid $k$-mer instead of remove entirely.
+\end{itemize}
+
+For $k$-mer database there are additional parameters:
+
+\begin{itemize}
+	\item \textsf{-ci$<$value$>$} --- exclude $k$-mers occurring less than <value> times,
+	\item \textsf{-cx$<$value$>$} --- exclude $k$-mers occurring more of than <value> times.
+\end{itemize}
+
+For the input set of reads there are additional parameters:
+
+\begin{itemize}
+	\item \textsf{-ci$<$value$>$} --- remove reads containing less $k$-mers than value (but if -t is set the read is trimmed on first $k$-mer with counter lower than value),
+	\item \textsf{-cx$<$value$>$} --- remove reads containing more $k$-mers than value (but if -t is set the read is trimmed on first $k$-mer with counter higher than value),
+	\item \textsf{-f$<$a/q$>$} --- input in FASTA format (-fa), FASTQ format (-fq); default: FASTQ.
+\end{itemize}
+For input set of reads integer or floating number can be given as \textsf{-ci$<$value$>$} and \textsf{-cx$<$value$>$}. Integer values are used to define strict thresholds, which means only reads that contain at least $ci_{value}$ and at most $cx_{value}$ $k$-mers will be kept in the output read set.
+Floating numbers for \textsf{-ci$<$value$>$} and \textsf{-cx$<$value$>$} parameters are used to define thresholds depending on read length. It should be in the range of [0.0;1.0]. Let $r$ be a length of a read. The read will be kept in the output read set only if it contains at least $\lfloor (r-k+1) * ci_{value} \rfloor$ and at most $\lfloor (r-k+1) * cx_{value} \rfloor$ $k$-mers which are present in \textsf{KMC} database. \\ \\
+For the output set of reads there are additional parameters:
+\begin{itemize}
+	\item \textsf{-f$<$a/q$>$} --- output in FASTA format (-fa), FASTQ format (-fq); default: same as the input
+\end{itemize}
+\textsf{input\_read\_set} may be a single file or a file which contains a list of input files (one file per line). 
+
+\section*{example}
+ kmc\_tools filter kmc\_db -ci3 input.fastq -ci0.5 -cx1.0 filtered.fastq \\
+ kmc\_tools filter kmc\_db input.fastq -ci10 -cx100 filtered.fastq \\
+ kmc\_tools filter kmc\_db @input\_files.txt -ci10 -cx100 filtered.fastq
\ No newline at end of file

=== added file 'doc/kmc_tools/intro.tex'
--- old/doc/kmc_tools/intro.tex	1970-01-01 00:00:00 +0000
+++ new/doc/kmc_tools/intro.tex	2020-12-10 18:04:42 +0000
@@ -0,0 +1,4 @@
+\clearpage
+\section*{Introduction}\label{sec:intro}
+\addcontentsline{toc}{section}{\protect\numberline{}Introduction}
+This document contains description of \textsf{kmc\_tools} software. \textsf{kmc\_tools} is a program which allows to work easily with sets of $k$-mers and their counters generated as output of \textsf{KMC}. \textsf{KMC} is an efficient $k$-mer counter described in: \url{http://bioinformatics.oxfordjournals.org/content/early/2015/02/18/bioinformatics.btv022}. \textsf{kmc\_tools} can work with databases produced by \textsf{KMC2} as well as by \textsf{KMC1} (there is a little difference between those formats). \textsf{kmc\_tools} always generates results in \textsf{KMC1} database format as it is a little faster (in sense of searching $k$-mers) than in case of \textsf{KMC2} database.
\ No newline at end of file

=== added file 'doc/kmc_tools/kmc_tools.tex'
--- old/doc/kmc_tools/kmc_tools.tex	1970-01-01 00:00:00 +0000
+++ new/doc/kmc_tools/kmc_tools.tex	2020-12-10 18:04:42 +0000
@@ -0,0 +1,169 @@
+\documentclass[a4paper,11pt]{article}
+
+\usepackage[T1]{fontenc}
+\usepackage{booktabs}
+\usepackage{url}
+\usepackage{palatino}
+\usepackage{graphicx}
+\usepackage{float}
+\usepackage{listings}
+\usepackage{color}
+\usepackage[hidelinks]{hyperref}
+
+
+\usepackage{xcolor}
+\hypersetup{
+	colorlinks,
+	linkcolor={blue!80!black},
+	citecolor={blue!80!black},
+	urlcolor={blue!80!black}
+}
+
+\usepackage{cleveref}
+%% \usepackage{tikz}
+%% \usetikzlibrary{arrows}
+
+%\usepackage{rotating}
+
+\urlstyle{sf}
+
+\setlength\oddsidemargin{-0.5in}
+\setlength\evensidemargin{-0.5in}
+
+\setlength\textheight{\paperheight}
+\addtolength\textheight{-\topmargin}
+\addtolength\textheight{-\headheight}
+\addtolength\textheight{-\headsep}
+\addtolength\textheight{-2in}
+\setlength\textwidth{\paperwidth}
+\addtolength\textwidth{-1in}
+
+\setlength\topmargin{0in} 
+\emergencystretch=5pt 
+
+% ***********************************************************
+\lstset{language=C++}
+\lstset{basicstyle=\scriptsize\sffamily}
+\lstset{keywordstyle=\bfseries}
+\lstset{morekeywords={__shared__,__global__,uint2,uint4}}
+\lstset{numbers=left}
+\lstset{numberstyle=\sffamily\tiny}
+\lstset{tabsize=2}
+
+
+\newcommand{\q}{\phantom0}
+\newcommand{\qq}{\phantom{00}}
+\newcommand{\qqq}{\phantom{000}}
+\newcommand{\qqqq}{\phantom{0000}}
+\newcommand{\qqqqq}{\phantom{00000}}
+\newcommand{\qc}{\phantom{0,}}
+\newcommand{\qcq}{\phantom{0,0}}
+\newcommand{\qcqq}{\phantom{0,00}}
+\newcommand{\qcqqq}{\phantom{0,000}}
+\newcommand{\qcqqqc}{\phantom{0,000,}}
+\newcommand{\qcqqqcq}{\phantom{0,000,0}}
+\newcommand{\qqc}{\phantom{00,}}
+\newcommand{\GDCra}[2]{GDC-ra-$2^{#1}$-$2^{#2}$}
+\newcommand{\qa}{\phantom{$^*$}}
+
+\newcommand{\mcOT}{\multicolumn{3}{c}{\em out of time ($>10$ hours)}}
+\newcommand{\mcOM}{\multicolumn{3}{c}{\em out of memory}}
+\newcommand{\mcOD}{\multicolumn{3}{c}{\em out of disk ($>650$\,GB)}}
+\newcommand{\mcNS}{\multicolumn{3}{c}{\em unsupported $k$}}
+\newcommand{\mcF}{\multicolumn{3}{c}{\em failed}}
+
+\newcommand{\comment}[1]{{\color{red} #1}}
+
+\newcommand{\algname}[1]{\textsc{#1}}
+\newcommand{\algdef}[1]{\textsc{#1}\label{algdef:#1}}
+
+\newcounter{lnoc}
+\newenvironment{algorithm}[1]{%
+\hrule height 0.8pt \vspace{0.6ex} \small#1\vspace{0.6ex}\hrule height 0.5pt \vspace{-2.25ex}
+\setcounter{lnoc}{0}
+\small
+\begin{tabbing}
+00000\=XX\=XX\=XX\=XX\=XX\=XX\=\kill
+}{%
+\end{tabbing}
+\vspace{-2.5ex}\hrule height 0.8pt\vspace{1ex}}
+\newcommand{\lno}[1][0]{{\footnotesize\sffamily 
+\ifnum#1=0
+\stepcounter{lnoc} 
+\ifnum\thelnoc<10
+\phantom0%
+\fi
+\thelnoc
+\else
+\thelnoc.#1
+\fi
+}\>}
+
+\newcommand{\pcfor}{{\bfseries for~}}
+\newcommand{\pcforall}{{\bfseries for all~}}
+\newcommand{\pcforeach}{{\bfseries for each~}}
+\newcommand{\pcto}{{\bfseries to~}}
+\newcommand{\pcdownto}{{\bfseries downto~}}
+\newcommand{\pcdo}{{\bfseries do~}}
+\newcommand{\pcif}{{\bfseries if~}}
+\newcommand{\pcthen}{{\bfseries then~}}
+\newcommand{\pcelse}{{\bfseries else~}}
+\newcommand{\pcelseif}{{\bfseries else if~}}
+\newcommand{\pcwhile}{{\bfseries while~}}
+\newcommand{\pcbreak}{{\bfseries break}}
+\newcommand{\pcand}{{\bfseries and~}}
+\newcommand{\pcor}{{\bfseries or~}}
+\newcommand{\pcnot}{{\bfseries not~}}
+\newcommand{\pcreturn}{{\bfseries return~}}
+\newcommand{\pcphreturn}{\phantom{\pcreturn}}
+\newcommand{\pcfun}[1]{\mbox{\textrm{#1}}}
+\newcommand{\pccomment}[1]{\qqq\{\textit{#1}\}}
+\newcommand{\pctrue}{\mbox{\textrm{\bfseries true}}}
+\newcommand{\pcfalse}{\mbox{\textrm{\bfseries false}}}
+\newcommand{\pcdiv}{\mbox{\bfseries div~}}
+\newcommand{\pcmod}{\mbox{\bfseries mod~}}
+\newcommand{\pcinput}[1]{Input: \textit{#1}}
+\newcommand{\pcinpute}[1]{\phantom{Input:} \textit{#1}}
+\newcommand{\pcoutput}[1]{Output: \textit{#1}}
+\newcommand{\pcoutpute}[1]{\phantom{Output:} \textit{#1}}
+\newcommand{\pcendparam}{%
+\end{tabbing}
+\vspace{-0.7cm}
+\hrule height 0.5pt
+\vspace{-0.3cm}
+\begin{tabbing}
+00000\=XX\=XX\=XX\=XX\=XX\=XX\=\kill
+}
+
+\begin{document}
+
+\centerline{\bfseries\LARGE\itshape kmc\_tools documentation}
+\bigskip
+\bigskip
+\centerline{\bfseries\Large for v. 3.0.0}
+\bigskip
+\bigskip
+\bigskip
+\bigskip
+
+\tableofcontents
+
+\input{intro.tex}
+
+\input{usage.tex}
+
+%\input{set_operations.tex}
+
+\input{simple.tex}
+
+\input{complex.tex}
+
+%\input{one_input_operations}
+
+\input{transform}
+
+\input{filter_operation}
+
+
+
+\end{document}

=== added file 'doc/kmc_tools/simple.tex'
--- old/doc/kmc_tools/simple.tex	1970-01-01 00:00:00 +0000
+++ new/doc/kmc_tools/simple.tex	2020-12-10 18:04:42 +0000
@@ -0,0 +1,90 @@
+\clearpage
+\section{simple operation}
+\label{sec:simple}
+Command-line syntax: \\
+%Syntax of \textbf{simple} operation: \\
+kmc\_tools [global\_params] simple $<$input1 [input1\_params]$>$ $<$input2 [input2\_params]$>$ $<$oper1 output1 [output\_params1]$>$ [$<$oper2 output2 [output\_params2]$>$ ...] \\
+
+
+where: 
+\begin{itemize}
+	\item \textsf{input1, input2} --- paths to the databases generated by \textsf{KMC} (\textsf{KMC} generates 2 files with the same name, but different extensions --- here only name without extension should be given),
+	\item \textsf{oper1, oper2, ...} --- set operations to be performed on input1 and input2,
+	\item \textsf{output1, output2, ...} --- paths of the output databases.
+\end{itemize}
+
+For each \textsf{input} there are additional parameters which can be set:
+\begin{itemize}
+	\item \textsf{-ci$<$value$>$} --- exclude $k$-mers occurring less than <value> times,
+	\item \textsf{-cx$<$value$>$} --- exclude $k$-mers occurring more of than <value> times.
+\end{itemize}
+
+If additional parameters are not given they are taken from the appropriate input database. 
+
+For each output there are also additional parameters:
+\begin{itemize}
+	\item \textsf{-ci$<$value$>$} --- exclude $k$-mers occurring less than <value> times,
+	\item \textsf{-cx$<$value$>$} --- exclude $k$-mers occurring more than <value> times,
+	\item \textsf{-cs$<$value$>$} --- maximal value of a counter,
+	\item \textsf{-oc$<$value$>$} --- redefine counter calculation mode for equal $k$-mers.
+	Available values:
+	\begin{itemize}
+		\item \textsf{min} --- get lower value of a $k$-mer counter,
+		\item \textsf{max} --- get upper value of a $k$-mer counter,
+		\item \textsf{sum} --- get sum of counters from both databases,
+		\item \textsf{diff}  --- get difference between counters,
+		\item \textsf{left} --- get counter from first database (input1),
+		\item \textsf{right} --- get counter from second database (input2)
+	\end{itemize}
+\end{itemize}
+
+If parameters are not given they are deduced based on input databases and specified operation.
+
+
+Valid values for \textsf{oper1, oper2, ...} are:
+\begin{itemize}
+	\item \textsf{intersect} --- output database will contains only $k$-mers that are present in \textbf{both} input sets,
+	\item \textsf{union} --- output database will contains each $k$-mer present in \textbf{any} of input sets,
+	\item \textsf{kmers\_subtract} --- difference of input sets based on k-mers. Output database will contains only $k$-mers that are \textbf{present in the first} input set but \textbf{absent in the second} one,
+	\item \textsf{counters\_subtract} --- difference of input sets based on k-mers and their counters (weaker version of kmers\_subtract). Output database will contains all $k$-mers that are present in the first input, without those for which counter operation will lead to remove such $k$-mer (i.e. counter equal to 0 or negative number),
+	\item \textsf{reverse\_kmers\_subtract} --- same as kmers\_subtract but treat input2 as first and input1 as second,
+	\item \textsf{reverse\_counters\_subtract} --- same as counters\_subtract but treat input2 as first and input1 as second.	
+\end{itemize}
+Each operation may be specified multiple times (which may be useful to produce two output sets with different cutoffs or counter calculation modes).
+
+
+
+\begin{table}[H]
+\centering
+\begin{tabular}{|l|l|}
+\hline
+\textbf{operation} & \textbf{counter calculation mode} \\
+\hline
+\textsf{intersect} & \textsf{min} \\
+\hline
+\textsf{union} & \textsf{sum} \\
+\hline
+\textsf{kmers\_subtract} & NONE \\
+\hline
+\textsf{reverse\_kmers\_subtract} & NONE \\
+\hline
+\textsf{counters\_subtract} & \textsf{diff} \\
+\hline
+\textsf{reverse\_counters\_subtract} & \textsf{diff} (but input1 and input2 are swapped) \\
+\hline
+\end{tabular}
+\caption{Default values of -oc switch for each operation}
+\label{table:defaults_counter_mode}
+\end{table}
+
+For \textsf{kmers\_subtract} and \textsf{reverse\_kmers\_subtract} equal $k$-mers will never be present in the output database, which is the reason of NONE values in \cref{table:defaults_counter_mode}.
+
+\subsection*{example 1}
+kmc -k28 file1.fastq kmers1 tmp \\
+kmc -k28 file2.fastq kmers2 tmp \\
+kmc\_tools simple kmers1 -ci10 -cx200 kmers2 -ci4 -cx100 intersect kmers1\_kmers2\_intersect -ci20 -cx150 \\
+
+\subsection*{example 2}
+kmc -k28 file1.fastq kmers1 tmp \\
+kmc -k28 file2.fastq kmers2 tmp \\
+kmc\_tools simple kmers1 kmers2 intersect inter\_k1\_k2\_max -ocmax intersect inter\_k1\_k2\_min union union\_k1\_k2 -ci10 

=== added file 'doc/kmc_tools/transform.tex'
--- old/doc/kmc_tools/transform.tex	1970-01-01 00:00:00 +0000
+++ new/doc/kmc_tools/transform.tex	2020-12-10 18:04:42 +0000
@@ -0,0 +1,62 @@
+\clearpage
+\section{transform}
+\label{sec:transform}
+
+This operation transforms single \textsf{KMC} database to one or more \textsf{KMC} database(s) or text file(s). \\
+Command-line syntax: \\
+kmc\_tools [global\_params] transform $<$input$>$ [input\_params] $<$oper1 [oper\_params1] output1 [output\_params1]$>$ [$<$oper2 [oper\_params2] output2 [output\_params2]$>$...]
+
+where:
+
+
+\begin{itemize}
+	\item \textsf{oper1, oper2, ...} --- transform operation to be performed on the input,
+	\item \textsf{input} -- path to databases generated by \textsf{KMC} (\textsf{KMC} generates 2 files with the same name, but different extensions --- here only name without extension should be given),
+	\item \textsf{output1, output2, ...} --- paths to the output file(s).
+\end{itemize}
+
+For input there are additional parameters which can be set:
+\begin{itemize}
+	\item \textsf{-ci<value>} --- exclude $k$-mers occurring less than <value> times,
+	\item \textsf{-cx<value>} --- exclude $k$-mers occurring more of than <value> times.
+\end{itemize}
+
+If additional parameters are not given they are taken from the appropriate input database. \\
+
+Valid values for oper1, oper2,... are:
+\begin{itemize}
+	\item \textsf{sort} --- converts database produced by KMC2.x to KMC1.x database format (which contains $k$-mers in sorted order),
+	\item \textsf{reduce} --- exclude too rare and too frequent $k$-mers,
+	\item \textsf{compact} --- remove counters of $k$-mers,
+	\item \textsf{histogram} --- produce histogram of $k$-mers occurrences,
+	\item \textsf{dump} --- produce text dump of \textsf{KMC} database.	
+\end{itemize}
+
+For \textsf{sort}, \textsf{reduce} and \textsf{dump} operations additional \textsf{output\_params} are available:
+\begin{itemize}
+	\item \textsf{-ci<value>} --- exclude $k$-mers occurring less than <value> times,
+	\item \textsf{-cx<value>} --- exclude $k$-mers occurring more than <value> times,
+	\item \textsf{-cs<value>} --- maximal value of a counter.
+\end{itemize}
+
+If these parameters are not specified they are deduced based on input database. \\
+
+For \textsf{histogram} operation additional \textsf{output\_params} are available:
+\begin{itemize}
+	\item \textsf{-ci<vaule>} --- minimum value of a counter to be stored in the output file (default value is a cutoff min stored in the database),
+	\item \textsf{-cx<value>} --- maximum value of a counter to be stored in the output file (default value is a minimum of tree: $10^4$, cutoff max stored in the database, $2^{8\mathrm{CS}}-1$, where CS is the number of bytes used to store counters in the database)
+\end{itemize}
+
+For dump operation there are additional \textsf{oper\_params}:
+\begin{itemize}
+	\item \textsf{-s} --- force sorted output (default: false). \\
+	For \textsf{KMC1.x} this parameter is irrelevant as $k$-mers are stored in sorted order and this order will be preserved in produced text file. For \textsf{KMC2.x} when this parameter is set $k$-mers will be sorted before dumpping to the text file.
+\end{itemize}
+
+
+\subsection *{example 1 - split $k$-mers on valid and invalid}
+Let's suppose $k$-mers with occurrences below 11 are erroneous due to sequencing errors. With \textsf{reduce} we can split $k$-mer set to one set with valid $k$-mers and one with invalid: \\
+kmc\_tools transform kmers reduce valid\_kmers -ci11 reduce erroneous\_kmers -cx10
+
+\subsection*{example 2 - perform all operations}
+kmc\_tools transform kmers reduce -ci10 reduced sort sorted compact without\_counters histogram histo.txt dump kmers.txt

=== added file 'doc/kmc_tools/usage.tex'
--- old/doc/kmc_tools/usage.tex	1970-01-01 00:00:00 +0000
+++ new/doc/kmc_tools/usage.tex	2020-12-10 18:04:42 +0000
@@ -0,0 +1,24 @@
+\clearpage
+\section{kmc\_tools usage}
+\label{sec:usage}
+
+\textsf{kmc\_tools} provides a number of operations that can be used to work with $k$-mer sets. The number of input and output sets is depended on operation itself. Configuration of \textsf{kmc\_tools} is done via command-line parameters. \\
+The general syntax is: \\
+kmc\_tools [global\_params] $<$operation$>$ <operation\_params> \\ 
+
+Global parameters are independent of operation type. There are:
+\begin{itemize}
+	\item \textsf{-t$<$value$>$} --- total number of threads (default: no. of CPU cores),
+	\item \textsf{-v} --- enable verbose mode (shows some information) (default: false),
+	\item \textsf{-hp} --- hide percentage progress (default: false).
+\end{itemize}
+
+Avaiable operations:
+
+\begin{itemize}
+	\item \textsf{simple} --- simple operations for two input sets (can produce multiple output sets),
+	\item \textsf{complex} --- complex set operations for 2 or more input $k$-mer sets (can produce one output set),
+	\item \textsf{transform} --- transform single $k$-mers set to other format (\textsf{KMC} database or text file, can produce multiple output sets),
+	\item \textsf{filter} --- filter out reads with too small number of $k$-mers.
+\end{itemize}
+\textsf{simple} performs typical set operations with two input sets and may produce many output sets (e.g. one output for intersection and another one for union). This operation is described in \hyperref[sec:simple]{next section}. \textsf{complex} is able to perform user defined set operations with many inputs (see \hyperref[sec:complex]{section \ref{sec:complex}}). \textsf{transform} operation converts single input $k$-mer set to another. This operation allows to multiple convertions (resulting multiple output files). For details see \hyperref[sec:transform]{section \ref{sec:transform}}. The last operation takes as input \textsf{KMC} database and set of reads (e.g. FASTQ files) and keep only reads that contains at least specified number of $k$-mers (see \hyperref[sec:filter_operation]{section \ref{sec:filter_operation}}).

=== added directory 'kmc_api'
=== added file 'kmc_api/kmc_file.cpp'
--- old/kmc_api/kmc_file.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_api/kmc_file.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,1465 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "mmer.h"
+#include "kmc_file.h"
+#include <iostream>
+#include <tuple>
+
+
+uint64 CKMCFile::part_size = 1 << 25;
+
+
+// ----------------------------------------------------------------------------------
+// Open files *.kmc_pre & *.kmc_suf, read them to RAM, close files. 
+// The file *.kmc_suf is opened for random access
+// IN	: file_name - the name of kmer_counter's output
+// RET	: true		- if successful
+// ----------------------------------------------------------------------------------
+bool CKMCFile::OpenForRA(const std::string &file_name)
+{
+	uint64 size;
+	size_t result;
+
+	if (file_pre || file_suf)
+		return false;
+
+	if (!OpenASingleFile(file_name + ".kmc_pre", file_pre, size, (char *)"KMCP"))
+		return false;
+
+	ReadParamsFrom_prefix_file_buf(size);
+
+	fclose(file_pre);
+	file_pre = NULL;
+		
+	if (!OpenASingleFile(file_name + ".kmc_suf", file_suf, size, (char *)"KMCS"))
+		return false;
+
+	sufix_file_buf = new uchar[size];
+	result = fread(sufix_file_buf, 1, size, file_suf);
+	if (result != size)
+		return false;
+
+	fclose(file_suf);
+	file_suf = NULL;
+
+	is_opened = opened_for_RA;
+	prefix_index = 0;
+	sufix_number = 0;
+	return true;
+}
+
+//----------------------------------------------------------------------------------
+// Open files *kmc_pre & *.kmc_suf, read *.kmc_pre to RAM, close *kmc.pre
+// *.kmc_suf is buffered
+// IN	: file_name - the name of kmer_counter's output
+// RET	: true		- if successful
+//----------------------------------------------------------------------------------
+bool CKMCFile::OpenForListing(const std::string &file_name)
+{
+	uint64 size;
+
+	if (is_opened)
+		return false;
+	
+	if (file_pre || file_suf)
+		return false;
+
+	if (!OpenASingleFile(file_name + ".kmc_pre", file_pre, size, (char *)"KMCP"))
+		return false;
+
+	ReadParamsFrom_prefix_file_buf(size);
+	fclose(file_pre);
+	file_pre = NULL;
+
+	end_of_file = total_kmers == 0;
+
+	if (!OpenASingleFile(file_name + ".kmc_suf", file_suf, size, (char *)"KMCS"))
+		return false;
+
+	sufix_file_buf = new uchar[part_size];
+
+	suffix_file_total_to_read = size;
+	suf_file_left_to_read = suffix_file_total_to_read;
+	auto to_read = MIN(suf_file_left_to_read, part_size);
+	auto readed = fread(sufix_file_buf, 1, to_read, file_suf);
+	if (readed != to_read)
+	{
+		std::cerr << "Error: some error while reading suffix file\n";
+		return false;
+	}
+
+	suf_file_left_to_read -= readed;
+
+	is_opened = opened_for_listing;
+	prefix_index = 0;
+	sufix_number = 0;
+	index_in_partial_buf = 0;
+	return true;
+}
+//----------------------------------------------------------------------------------
+CKMCFile::CKMCFile()
+{
+	file_pre = NULL;	
+	file_suf = NULL;
+
+	prefix_file_buf = NULL;
+	sufix_file_buf = NULL;
+	signature_map = NULL;
+
+	is_opened = closed;
+	end_of_file = false;
+}
+//----------------------------------------------------------------------------------	
+CKMCFile::~CKMCFile()
+{
+	if (file_pre)
+		fclose(file_pre);
+	if (file_suf)
+		fclose(file_suf);
+	if (prefix_file_buf)
+		delete[] prefix_file_buf;
+	if (sufix_file_buf)
+		delete[] sufix_file_buf;
+	if (signature_map)
+		delete[] signature_map;
+}
+//----------------------------------------------------------------------------------	
+// Open a file, recognize its size and check its marker. Auxiliary function.
+// IN	: file_name - the name of a file to open
+// RET	: true		- if successful
+//----------------------------------------------------------------------------------
+bool CKMCFile::OpenASingleFile(const std::string &file_name, FILE *&file_handler, uint64 &size, char marker[])
+{
+	char _marker[4];
+	size_t result;
+
+	if ((file_handler = my_fopen(file_name.c_str(), "rb")) == NULL)
+		return false;
+
+	my_fseek(file_handler, 0, SEEK_END);
+	size = my_ftell(file_handler);					//the size of a whole file
+
+	my_fseek(file_handler, -4, SEEK_CUR);
+	result = fread(_marker, 1, 4, file_handler);
+	if (result == 0)
+		return false;
+
+	size = size - 4;							//the size of the file without the terminal marker
+	if (strncmp(marker, _marker, 4) != 0)
+	{
+		fclose(file_handler);
+		file_handler = NULL;
+		return false;
+	}
+
+	rewind(file_handler);
+	result = fread(_marker, 1, 4, file_handler);
+	if (result == 0)
+		return false;
+
+	size = size - 4;							//the size of the file without initial and terminal markers 
+
+	if (strncmp(marker, _marker, 4) != 0)
+	{
+		fclose(file_handler);
+		file_handler = NULL;
+		return false;
+	}
+
+	return true;
+}
+//-------------------------------------------------------------------------------------
+// Recognize current parameters from kmc_databese. Auxiliary function.
+// IN	: the size of the file *.kmc_pre, without initial and terminal markers 
+// RET	: true - if succesfull
+//----------------------------------------------------------------------------------
+bool CKMCFile::ReadParamsFrom_prefix_file_buf(uint64 &size)
+{
+	size_t prev_pos = my_ftell(file_pre);
+	my_fseek(file_pre, -12, SEEK_END);
+	size_t result;
+
+	result = fread(&kmc_version, sizeof(uint32), 1, file_pre);
+	if (kmc_version != 0 && kmc_version != 0x200) //only this versions are supported, 0 = kmc1, 0x200 = kmc2
+		return false;
+	my_fseek(file_pre, prev_pos, SEEK_SET);
+
+	if (kmc_version == 0x200)
+	{
+		my_fseek(file_pre, -8, SEEK_END);
+		
+		int64 header_offset;
+		header_offset = fgetc(file_pre);
+		
+		size = size - 4;	//file size without the size of header_offset (and without 2 markers)
+
+		my_fseek(file_pre, (0LL - (header_offset + 8)), SEEK_END);
+		result = fread(&kmer_length, 1, sizeof(uint32), file_pre);
+		result = fread(&mode, 1, sizeof(uint32), file_pre);
+		result = fread(&counter_size, 1, sizeof(uint32), file_pre);
+		result = fread(&lut_prefix_length, 1, sizeof(uint32), file_pre);
+		result = fread(&signature_len, 1, sizeof(uint32), file_pre);
+		result = fread(&min_count, 1, sizeof(uint32), file_pre);
+		original_min_count = min_count;
+		uint32 max_count_uint32;
+		result = fread(&max_count_uint32, 1, sizeof(uint32), file_pre);
+		max_count = max_count_uint32;
+		original_max_count = max_count;
+		result = fread(&total_kmers, 1, sizeof(uint64), file_pre);
+		result = fread(&both_strands, 1, 1, file_pre);
+		both_strands = !both_strands;
+
+		signature_map_size = ((1 << (2 * signature_len)) + 1);
+		uint64 lut_area_size_in_bytes = size - (signature_map_size * sizeof(uint32)+header_offset + 8);
+		single_LUT_size = 1 << (2 * lut_prefix_length);
+		uint64 last_data_index = lut_area_size_in_bytes / sizeof(uint64);
+
+		rewind(file_pre);
+		my_fseek(file_pre, +4, SEEK_CUR);
+		prefix_file_buf_size = (lut_area_size_in_bytes + 8) / sizeof(uint64);		//reads without 4 bytes of a header_offset (and without markers)		
+		prefix_file_buf = new uint64[prefix_file_buf_size];
+		result = fread(prefix_file_buf, 1, (size_t)(lut_area_size_in_bytes + 8), file_pre);
+		if (result == 0)
+			return false;
+		prefix_file_buf[last_data_index] = total_kmers + 1;
+
+		signature_map = new uint32[signature_map_size];
+		result = fread(signature_map, 1, signature_map_size * sizeof(uint32), file_pre);
+		if (result == 0)
+			return false;
+
+		sufix_size = (kmer_length - lut_prefix_length) / 4;		 
+	
+		sufix_rec_size = sufix_size + counter_size;	
+
+		return true;
+	}
+	else if (kmc_version == 0)
+	{
+		prefix_file_buf_size = (size - 4) / sizeof(uint64);		//reads without 4 bytes of a header_offset (and without markers)		
+		prefix_file_buf = new uint64[prefix_file_buf_size];
+		result = fread(prefix_file_buf, 1, (size_t)(size - 4), file_pre);
+		if (result == 0)
+			return false;
+
+		my_fseek(file_pre, -8, SEEK_END);
+
+		uint64 header_offset;
+		header_offset = fgetc(file_pre);
+
+		size = size - 4;
+
+		uint64 header_index = (size - header_offset) / sizeof(uint64);
+		uint64 last_data_index = header_index;
+
+		uint64 d = prefix_file_buf[header_index];
+
+		kmer_length = (uint32)d;			//- kmer's length
+		mode = d >> 32;				//- mode: 0 or 1
+
+		header_index++;
+		counter_size = (uint32)prefix_file_buf[header_index];	//- the size of a counter in bytes; 
+		//- for mode 0 counter_size is 1, 2, 3, or 4 (or 5, 6, 7, 8 for small k values)
+		//- for mode = 1 counter_size is 4;
+		lut_prefix_length = prefix_file_buf[header_index] >> 32;		//- the number of prefix's symbols cut frm kmers; 
+		//- (kmer_length - lut_prefix_length) is divisible by 4
+
+		header_index++;
+		original_min_count = (uint32)prefix_file_buf[header_index];    //- the minimal number of kmer's appearances 
+		min_count = original_min_count;
+		original_max_count = prefix_file_buf[header_index] >> 32;      //- the maximal number of kmer's appearances
+		//max_count = original_max_count;
+
+		header_index++;
+		total_kmers = prefix_file_buf[header_index];					//- the total number of kmers 
+
+		header_index++;
+		both_strands = (prefix_file_buf[header_index] & 0x000000000000000F) == 1;
+		both_strands = !both_strands;
+
+		original_max_count += prefix_file_buf[header_index] & 0xFFFFFFFF00000000;
+		max_count = original_max_count;
+
+		prefix_file_buf[last_data_index] = total_kmers + 1;
+
+		sufix_size = (kmer_length - lut_prefix_length) / 4;
+
+		sufix_rec_size = sufix_size + counter_size;
+
+		return true;
+
+	}
+	return false;
+}
+
+//------------------------------------------------------------------------------------------
+// Check if kmer exists. 
+// IN : kmer  - kmer
+// OUT: count - kmer's counter if kmer exists
+// RET: true  - if kmer exists
+//------------------------------------------------------------------------------------------
+bool CKMCFile::CheckKmer(CKmerAPI &kmer, float &count)
+{
+	uint32 int_counter;
+	if (CheckKmer(kmer, int_counter))
+	{
+		if (mode == 0)
+			count = (float)int_counter;
+		else
+			memcpy(&count, &int_counter, counter_size);
+		return true;
+	}
+	return false;
+}
+
+//------------------------------------------------------------------------------------------
+// Check if kmer exists. 
+// IN : kmer  - kmer
+// OUT: count - kmer's counter if kmer exists
+// RET: true  - if kmer exists
+//------------------------------------------------------------------------------------------
+bool CKMCFile::CheckKmer(CKmerAPI &kmer, uint32 &count)
+{
+	if(is_opened != opened_for_RA)
+		return false;
+	if(end_of_file)
+		return false;
+	
+	//recognize a prefix:
+	uint64 pattern_prefix_value = kmer.kmer_data[0];
+
+	uint32 pattern_offset = (sizeof(pattern_prefix_value)* 8) - (lut_prefix_length * 2) - (kmer.byte_alignment * 2);
+	int64 index_start = 0, index_stop = 0;
+
+	pattern_prefix_value = pattern_prefix_value >> pattern_offset;  //complements with 0
+	if (pattern_prefix_value >= prefix_file_buf_size)
+		return false;
+
+	if (kmc_version == 0x200)
+	{
+		uint32 signature = kmer.get_signature(signature_len);
+		uint32 bin_start_pos = signature_map[signature];
+		bin_start_pos *= single_LUT_size;				
+		//look into the array with data
+		index_start = *(prefix_file_buf + bin_start_pos + pattern_prefix_value);
+		index_stop = *(prefix_file_buf + bin_start_pos + pattern_prefix_value + 1) - 1;
+	}
+	else if (kmc_version == 0)
+	{
+		//look into the array with data
+		index_start = prefix_file_buf[pattern_prefix_value];
+		index_stop = prefix_file_buf[pattern_prefix_value + 1] - 1;
+	}
+	uint64 tmp_count ;
+	bool res = BinarySearch(index_start, index_stop, kmer, tmp_count, pattern_offset);
+	count = (uint32)tmp_count;
+	return res;
+}
+
+//------------------------------------------------------------------------------------------
+// Check if kmer exists. 
+// IN : kmer  - kmer
+// OUT: count - kmer's counter if kmer exists
+// RET: true  - if kmer exists
+//------------------------------------------------------------------------------------------
+bool CKMCFile::CheckKmer(CKmerAPI &kmer, uint64 &count)
+{
+	if (is_opened != opened_for_RA)
+		return false;
+	if (end_of_file)
+		return false;
+
+	//recognize a prefix:
+	uint64 pattern_prefix_value = kmer.kmer_data[0];
+
+	uint32 pattern_offset = (sizeof(pattern_prefix_value)* 8) - (lut_prefix_length * 2) - (kmer.byte_alignment * 2);
+	int64 index_start = 0, index_stop = 0;
+
+	pattern_prefix_value = pattern_prefix_value >> pattern_offset;  //complements with 0
+	if (pattern_prefix_value >= prefix_file_buf_size)
+		return false;
+
+	if (kmc_version == 0x200)
+	{
+		uint32 signature = kmer.get_signature(signature_len);
+		uint32 bin_start_pos = signature_map[signature];
+		bin_start_pos *= single_LUT_size;
+		//look into the array with data
+		index_start = *(prefix_file_buf + bin_start_pos + pattern_prefix_value);
+		index_stop = *(prefix_file_buf + bin_start_pos + pattern_prefix_value + 1) - 1;
+	}
+	else if (kmc_version == 0)
+	{
+		//look into the array with data
+		index_start = prefix_file_buf[pattern_prefix_value];
+		index_stop = prefix_file_buf[pattern_prefix_value + 1] - 1;
+	}
+	return BinarySearch(index_start, index_stop, kmer, count, pattern_offset);
+}
+
+//-----------------------------------------------------------------------------------------------
+// Check if end of file
+// RET: true - all kmers are listed
+//-----------------------------------------------------------------------------------------------
+bool CKMCFile::Eof(void)
+{
+	return end_of_file;	
+}
+
+bool CKMCFile::ReadNextKmer(CKmerAPI &kmer, float &count)
+{
+	uint32 int_counter;
+	if (ReadNextKmer(kmer, int_counter))
+	{
+		if (mode == 0)
+			count = (float)int_counter;
+		else
+			memcpy(&count, &int_counter, counter_size);
+		return true;
+	}
+	return false;
+
+}
+//-----------------------------------------------------------------------------------------------
+// Read next kmer
+// OUT: kmer - next kmer
+// OUT: count - kmer's counter
+// RET: true - if not EOF
+//-----------------------------------------------------------------------------------------------
+bool CKMCFile::ReadNextKmer(CKmerAPI &kmer, uint32 &count)
+{
+	uint64 prefix_mask = (1 << 2 * lut_prefix_length) - 1; //for kmc2 db
+
+	if(is_opened != opened_for_listing)
+		return false;
+	do
+	{
+		if(end_of_file)
+			return false;
+		
+		if(sufix_number == prefix_file_buf[prefix_index + 1]) 
+		{
+			prefix_index++;
+						
+			while (prefix_file_buf[prefix_index] == prefix_file_buf[prefix_index + 1])
+				prefix_index++;
+		}
+	
+		uint32 off = (sizeof(prefix_index) * 8) - (lut_prefix_length * 2) - kmer.byte_alignment * 2;
+			
+		uint64 temp_prefix = (prefix_index & prefix_mask) << off;	// shift prefix towards MSD. "& prefix_mask" necessary for kmc2 db format
+		
+		kmer.kmer_data[0] = temp_prefix;			// store prefix in an object CKmerAPI
+
+		for(uint32 i = 1; i < kmer.no_of_rows; i++)
+			kmer.kmer_data[i] = 0;
+
+		//read sufix:
+		uint32 row_index = 0;
+ 		uint64 suf = 0;
+	
+		off = off - 8;
+				
+ 		for(uint32 a = 0; a < sufix_size; a ++)
+		{
+			if(index_in_partial_buf == part_size)
+				Reload_sufix_file_buf();
+						
+			suf = sufix_file_buf[index_in_partial_buf++];
+			suf = suf << off;
+			kmer.kmer_data[row_index] = kmer.kmer_data[row_index] | suf;
+
+			if (off == 0)				//the end of a word in kmer_data
+			{
+					off = 56;
+					row_index++;
+			}
+			else
+					off -=8;
+		}
+	
+		//read counter:
+		if(index_in_partial_buf == part_size)
+			Reload_sufix_file_buf();
+		
+		count = sufix_file_buf[index_in_partial_buf++];
+
+		for(uint32 b = 1; b < counter_size; b++)
+		{
+			if(index_in_partial_buf == part_size)
+				Reload_sufix_file_buf();
+			
+			uint32 aux = 0x000000ff & sufix_file_buf[index_in_partial_buf++];
+			aux = aux << 8 * ( b);
+			count = aux | count;
+		}
+			
+		sufix_number++;
+	
+		if(sufix_number == total_kmers)
+			end_of_file = true;
+
+		if (mode != 0)
+		{
+			float float_counter;
+			memcpy(&float_counter, &count, counter_size);
+			if ((float_counter < min_count) || (float_counter > max_count))
+				continue;
+			else
+				break;
+		}
+
+	}
+	while((count < min_count) || (count > max_count));
+
+	return true;
+}
+
+
+//-----------------------------------------------------------------------------------------------
+// Read next kmer
+// OUT: kmer - next kmer
+// OUT: count - kmer's counter
+// RET: true - if not EOF
+//-----------------------------------------------------------------------------------------------
+bool CKMCFile::ReadNextKmer(CKmerAPI &kmer, uint64 &count)
+{
+	uint64 prefix_mask = (1 << 2 * lut_prefix_length) - 1; //for kmc2 db
+
+	if (is_opened != opened_for_listing)
+		return false;
+	do
+	{
+		if (end_of_file)
+			return false;
+
+		if (sufix_number == prefix_file_buf[prefix_index + 1])
+		{
+			prefix_index++;
+
+			while (prefix_file_buf[prefix_index] == prefix_file_buf[prefix_index + 1])
+				prefix_index++;
+		}
+
+		uint32 off = (sizeof(prefix_index)* 8) - (lut_prefix_length * 2) - kmer.byte_alignment * 2;
+
+		uint64 temp_prefix = (prefix_index & prefix_mask) << off;	// shift prefix towards MSD. "& prefix_mask" necessary for kmc2 db format
+
+		kmer.kmer_data[0] = temp_prefix;			// store prefix in an object CKmerAPI
+
+		for (uint32 i = 1; i < kmer.no_of_rows; i++)
+			kmer.kmer_data[i] = 0;
+
+		//read sufix:
+		uint32 row_index = 0;
+		uint64 suf = 0;
+
+		off = off - 8;
+
+		for (uint32 a = 0; a < sufix_size; a++)
+		{
+			if (index_in_partial_buf == part_size)
+				Reload_sufix_file_buf();
+
+			suf = sufix_file_buf[index_in_partial_buf++];
+			suf = suf << off;
+			kmer.kmer_data[row_index] = kmer.kmer_data[row_index] | suf;
+
+			if (off == 0)				//the end of a word in kmer_data
+			{
+				off = 56;
+				row_index++;
+			}
+			else
+				off -= 8;
+		}
+
+		//read counter:
+		if (index_in_partial_buf == part_size)
+			Reload_sufix_file_buf();
+
+		count = sufix_file_buf[index_in_partial_buf++];
+
+		for (uint32 b = 1; b < counter_size; b++)
+		{
+			if (index_in_partial_buf == part_size)
+				Reload_sufix_file_buf();
+
+			uint64 aux = 0x000000ff & sufix_file_buf[index_in_partial_buf++];
+			aux = aux << 8 * (b);
+			count = aux | count;
+		}
+
+		sufix_number++;
+
+		if (sufix_number == total_kmers)
+			end_of_file = true;
+
+	} while ((count < min_count) || (count > max_count));
+
+	return true;
+}
+
+//-------------------------------------------------------------------------------
+// Reload a contents of an array "sufix_file_buf" for listing mode. Auxiliary function.
+//-------------------------------------------------------------------------------
+void CKMCFile::Reload_sufix_file_buf()
+{
+	auto to_read = MIN(suf_file_left_to_read, part_size);
+	auto readed = fread(sufix_file_buf, 1, (size_t)to_read, file_suf);
+	suf_file_left_to_read -= readed;
+	if (readed != to_read)
+	{
+		std::cerr << "Error: some error while reading suffix file\n";
+		exit(1);
+	}
+	index_in_partial_buf = 0;
+}
+//-------------------------------------------------------------------------------
+// Release memory and close files in case they were opened 
+// RET: true - if files have been readed
+//-------------------------------------------------------------------------------
+bool CKMCFile::Close()
+{
+	if(is_opened)
+	{
+		if(file_pre)
+		{
+			fclose(file_pre);	
+			file_pre = NULL;
+		}
+		if(file_suf)
+		{
+			fclose(file_suf);
+			file_suf = NULL;
+		}
+	
+		is_opened = closed;
+		end_of_file = false;
+		delete [] prefix_file_buf;
+		prefix_file_buf = NULL;
+		delete [] sufix_file_buf;
+		sufix_file_buf = NULL;
+		delete[] signature_map;
+		signature_map = NULL;
+
+		return true;
+	}
+	else
+		return false;
+}
+//----------------------------------------------------------------------------------
+// Set initial values to enable listing kmers from the begining. Only in listing mode
+// RET: true - if a file has been opened for listing
+//----------------------------------------------------------------------------------
+bool CKMCFile::RestartListing(void)
+{
+	if(is_opened == opened_for_listing)
+	{
+		my_fseek(file_suf , 4 , SEEK_SET);
+		suf_file_left_to_read = suffix_file_total_to_read;
+		auto to_read = MIN(suf_file_left_to_read, part_size);
+		auto readed = fread(sufix_file_buf, 1, to_read, file_suf);
+		if (readed != to_read)
+		{
+			std::cerr << "Error: some error while reading suffix file\n";
+			return false;
+		}
+
+		suf_file_left_to_read -= readed;
+		prefix_index = 0;
+		sufix_number = 0;
+		index_in_partial_buf = 0;
+
+		end_of_file = total_kmers == 0;
+
+		return true;
+	}
+	return false;
+		
+}
+//----------------------------------------------------------------------------------------
+// Set the minimal value for a counter. Kmers with counters below this theshold are ignored
+// IN	: x - minimal value for a counter
+// RET	: true - if successful 
+//----------------------------------------------------------------------------------------
+bool CKMCFile::SetMinCount(uint32 x)
+{
+	if((original_min_count <= x) && (x <= max_count))
+	{
+		min_count = x;
+		return true;
+	} 
+	else
+		return false;
+}
+
+//----------------------------------------------------------------------------------------
+// Return a value of min_count. Kmers with counters below this theshold are ignored 
+// RET	: a value of min_count
+//----------------------------------------------------------------------------------------
+uint32 CKMCFile::GetMinCount(void)
+{
+	return min_count;
+}
+
+//----------------------------------------------------------------------------------------
+// Set the maximal value for a counter. Kmers with counters above this theshold are ignored
+// IN	: x - maximal value for a counter
+// RET	: true - if successful 
+//----------------------------------------------------------------------------------------
+bool CKMCFile::SetMaxCount(uint32 x)
+{
+	if((original_max_count >= x) && (x >= min_count))
+	{
+		max_count = x;
+		return true; 
+	}
+	else
+		return false;
+}
+
+
+//----------------------------------------------------------------------------------------
+// Return a value of max_count. Kmers with counters above this theshold are ignored 
+// RET	: a value of max_count
+//----------------------------------------------------------------------------------------
+uint64 CKMCFile::GetMaxCount(void)
+{
+	return max_count;
+}
+
+//----------------------------------------------------------------------------------------
+// Return true if KMC was run without -b switch
+// RET	: a value of both_strands
+//----------------------------------------------------------------------------------------
+bool CKMCFile::GetBothStrands(void)
+{
+	return both_strands;
+}
+
+
+
+//----------------------------------------------------------------------------------------
+// Set original (readed from *.kmer_pre) values for min_count and max_count
+//----------------------------------------------------------------------------------------
+void CKMCFile::ResetMinMaxCounts(void)
+{
+	min_count = original_min_count;
+	max_count = original_max_count;
+} 
+
+//----------------------------------------------------------------------------------------
+// Return the length of kmers
+// RET	: the length of kmers
+//----------------------------------------------------------------------------------------
+uint32 CKMCFile::KmerLength(void)
+{
+	return kmer_length;			
+}
+
+//----------------------------------------------------------------------------------------
+// Check if kmer exists
+// IN	: kmer - kmer
+// RET	: true if kmer exists
+//----------------------------------------------------------------------------------------
+bool CKMCFile::IsKmer(CKmerAPI &kmer)
+{
+	uint32 _count;
+	if(CheckKmer(kmer, _count))
+		return true;
+	else
+		return false;
+}
+
+//-----------------------------------------------------------------------------------------
+// Check the total number of kmers between current min_count and max_count
+// RET	: total number of kmers or 0 if a database has not been opened
+//-----------------------------------------------------------------------------------------
+uint64 CKMCFile::KmerCount(void)
+{
+	if(is_opened)
+		if((min_count == original_min_count) && (max_count == original_max_count))
+			return total_kmers;
+		else
+		{
+			uint32 count;
+			uint32 int_counter;
+			uint64 aux_kmerCount = 0;
+
+			if(is_opened == opened_for_RA)
+			{
+				uchar *ptr = sufix_file_buf;
+				
+				for(uint64 i = 0; i < total_kmers; i++)		
+				{
+					ptr += sufix_size;
+					int_counter = *ptr;
+					ptr++;
+
+					for(uint32 b = 1; b < counter_size; b ++)
+					{
+						uint32 aux = 0x000000ff & *(ptr);
+						aux = aux << 8 * ( b);
+						int_counter = aux | int_counter;
+						ptr++;
+					}
+					
+					if(mode == 0)
+						count = int_counter;
+					else
+						memcpy(&count, &int_counter, counter_size);
+	
+					if((count >= min_count) && (count <= max_count))
+						aux_kmerCount++;
+				}
+			}
+			else //opened_for_listing
+			{
+				CKmerAPI kmer(kmer_length);
+				float count;
+				RestartListing();
+				for(uint64 i = 0; i < total_kmers; i++)		
+				{
+					ReadNextKmer(kmer, count);
+					if((count >= min_count) && (count <= max_count))
+						aux_kmerCount++;
+				}
+				RestartListing();
+			}
+			return aux_kmerCount;
+		}
+	else
+		return 0 ;
+}
+//---------------------------------------------------------------------------------
+// Get current parameters from kmer_database
+// OUT	:	_kmer_length	- the length of kmers
+//			_mode			- mode
+//			_counter_size	- the size of a counter in bytes 
+//			_lut_prefix_length - the number of prefix's symbols cut from kmers 
+//			_min_count		- the minimal number of kmer's appearances 
+//			_max_count		- the maximal number of kmer's appearances
+//			_total_kmers	- the total number of kmers
+// RET	: true if kmer_database has been opened
+//---------------------------------------------------------------------------------
+bool CKMCFile::Info(uint32 &_kmer_length, uint32 &_mode, uint32 &_counter_size, uint32 &_lut_prefix_length, uint32 &_signature_len, uint32 &_min_count, uint64 &_max_count, uint64 &_total_kmers)
+{
+	if(is_opened)
+	{
+		_kmer_length = kmer_length;
+		_mode = mode;
+		_counter_size = counter_size;
+		_lut_prefix_length = lut_prefix_length;
+		if (kmc_version == 0x200)
+			_signature_len = signature_len;
+		else
+			_signature_len = 0; //for kmc1 there is no signature_len
+		_min_count = min_count;
+		_max_count = max_count;
+		_total_kmers = total_kmers;
+		return true;
+	}
+	return false;
+}
+
+// Get current parameters from kmer_database
+bool CKMCFile::Info(CKMCFileInfo& info)
+{
+	if (is_opened)
+	{
+		info.kmer_length = kmer_length;
+		info.mode = mode;
+		info.counter_size = counter_size;
+		info.lut_prefix_length = lut_prefix_length;
+		if (kmc_version == 0x200)
+			info.signature_len = signature_len;
+		else
+			info.signature_len = 0; //for kmc1 there is no signature_len
+		info.min_count = min_count;
+		info.max_count = max_count;
+		info.total_kmers = total_kmers;
+		info.both_strands = both_strands;
+		return true;
+	}
+	return false;
+}
+
+
+//---------------------------------------------------------------------------------
+// Get counters from read
+// OUT	:	counters    	- vector of counters of each k-mer in read (of size read_len - kmer_len + 1), if some k-mer is invalid (i.e. contains 'N') the counter is equal to 0
+// IN   :   read			- 
+// RET	:   true if success, false if k > read length or some failure 
+//---------------------------------------------------------------------------------
+bool CKMCFile::GetCountersForRead(const std::string& read, std::vector<uint32>& counters)
+{
+	if (is_opened != opened_for_RA)
+		return false;
+
+	if (read.length() < kmer_length)
+	{
+		counters.clear();
+		return false;
+	}
+
+	if (kmc_version == 0x200)
+	{		
+		if (both_strands)
+			return GetCountersForRead_kmc2_both_strands(read, counters);
+		else
+			return GetCountersForRead_kmc2(read, counters);
+	}
+	else if (kmc_version == 0)
+	{
+		if (both_strands)
+			return GetCountersForRead_kmc1_both_strands(read,counters);
+		else
+			return GetCountersForRead_kmc1(read, counters);
+	}
+	else
+		return false; //never should be here
+}
+
+//---------------------------------------------------------------------------------
+// Get counters from read
+// OUT	:	counters    	- vector of counters of each k-mer in read (of size read_len - kmer_len + 1), if some k-mer is invalid (i.e. contains 'N') the counter is equal to 0
+// IN   :   read			- 
+// RET	: true if success
+//---------------------------------------------------------------------------------
+bool CKMCFile::GetCountersForRead(const std::string& read, std::vector<float>& counters)
+{
+	if (is_opened != opened_for_RA)
+		return false;
+	std::vector<uint32> uint32_v;
+	if (GetCountersForRead(read, uint32_v))
+	{
+		counters.clear();
+		counters.resize(uint32_v.size());
+		if (mode == 0)
+		{
+			for (uint32 i = 0; i < uint32_v.size(); ++i)
+				counters[i] = static_cast<float>(uint32_v[i]);
+		}
+		else
+		{
+			for (uint32 i = 0; i < uint32_v.size(); ++i)
+				memcpy(&counters[i], &uint32_v[i], counter_size);
+		}
+
+		return true;
+	}
+	return false;
+}
+
+//---------------------------------------------------------------------------------
+// Auxiliary function.
+//---------------------------------------------------------------------------------
+uint32 CKMCFile::count_for_kmer_kmc1(CKmerAPI& kmer)
+{
+	//recognize a prefix:
+
+	uint64 pattern_prefix_value = kmer.kmer_data[0];
+
+	uint32 pattern_offset = (sizeof(pattern_prefix_value)* 8) - (lut_prefix_length * 2) - (kmer.byte_alignment * 2);
+
+	pattern_prefix_value = pattern_prefix_value >> pattern_offset;  //complements with 0
+	if (pattern_prefix_value >= prefix_file_buf_size)
+		return false;
+	//look into the array with data
+
+	int64 index_start = prefix_file_buf[pattern_prefix_value];
+	int64 index_stop = prefix_file_buf[pattern_prefix_value + 1] - 1;
+
+	uint64 counter = 0;
+	if (BinarySearch(index_start, index_stop, kmer, counter, pattern_offset))
+		return (uint32)counter;
+	return 0;
+}
+
+//---------------------------------------------------------------------------------
+// Auxiliary function.
+//---------------------------------------------------------------------------------
+uint32 CKMCFile::count_for_kmer_kmc2(CKmerAPI& kmer, uint32 bin_start_pos)
+{
+	//recognize a prefix:
+	uint64 pattern_prefix_value = kmer.kmer_data[0];
+
+	uint32 pattern_offset = (sizeof(pattern_prefix_value)* 8) - (lut_prefix_length * 2) - (kmer.byte_alignment * 2);
+
+	pattern_prefix_value = pattern_prefix_value >> pattern_offset;  //complements with 0
+	if (pattern_prefix_value >= prefix_file_buf_size)
+		return false;
+	//look into the array with data
+
+	int64 index_start = *(prefix_file_buf + bin_start_pos + pattern_prefix_value);
+	int64 index_stop = *(prefix_file_buf + bin_start_pos + pattern_prefix_value + 1) - 1;
+
+	uint64 counter = 0;
+	if (BinarySearch(index_start, index_stop, kmer, counter, pattern_offset))
+		return (uint32)counter;
+	return 0;
+}
+
+//---------------------------------------------------------------------------------
+// Auxiliary function.
+//---------------------------------------------------------------------------------
+bool CKMCFile::GetCountersForRead_kmc1_both_strands(const std::string& read, std::vector<uint32>& counters)
+{
+	uint32 read_len = static_cast<uint32>(read.length());
+	counters.resize(read.length() - kmer_length + 1);
+	std::string transformed_read = read;
+	for (char& c : transformed_read)
+		c = CKmerAPI::num_codes[(uchar)c];
+
+	uint32 i = 0;
+	CKmerAPI kmer(kmer_length), kmer_rev(kmer_length);
+	uint32 pos = 0;
+	uint32 rev_pos = kmer_length - 1;
+
+	uint32 counters_pos = 0;
+
+	while (i + kmer_length - 1 < read_len)
+	{
+		bool contains_N = false;
+		while (i < read_len && pos < kmer_length)
+		{
+			if (CKmerAPI::num_codes[(uchar)read[i]] < 0)
+			{
+				pos = 0;
+				rev_pos = kmer_length - 1;
+				kmer.clear();
+				kmer_rev.clear();
+				++i;
+				uint32 wrong_kmers = MIN(i - counters_pos, static_cast<uint32>(counters.size()) - counters_pos);
+				fill_n(counters.begin() + counters_pos, wrong_kmers, 0);
+				counters_pos += wrong_kmers;
+				contains_N = true;
+				break;
+			}
+			else
+			{
+				kmer_rev.insert2bits(rev_pos--, 3 - CKmerAPI::num_codes[(uchar)read[i]]);
+				kmer.insert2bits(pos++, CKmerAPI::num_codes[(uchar)read[i++]]);
+				
+			}
+		}
+		if (contains_N)
+			continue;
+		if (pos == kmer_length)
+		{
+			if(kmer < kmer_rev)
+				counters[counters_pos++] = count_for_kmer_kmc1(kmer);
+			else
+				counters[counters_pos++] = count_for_kmer_kmc1(kmer_rev);
+		}
+		else
+			break;
+
+		while (i < read_len)
+		{
+			if (CKmerAPI::num_codes[(uchar)read[i]] < 0)
+			{
+				pos = 0;
+				break;
+			}
+			kmer_rev.SHR_insert2bits(3 - CKmerAPI::num_codes[(uchar)read[i]]);
+			kmer.SHL_insert2bits(CKmerAPI::num_codes[(uchar)read[i++]]);
+			if(kmer < kmer_rev)
+				counters[counters_pos++] = count_for_kmer_kmc1(kmer);
+			else
+				counters[counters_pos++] = count_for_kmer_kmc1(kmer_rev);
+		}
+	}
+	if (counters_pos < counters.size())
+	{
+		fill_n(counters.begin() + counters_pos, counters.size() - counters_pos, 0);
+		counters_pos = static_cast<uint32>(counters.size());
+	}
+	return true;
+}
+
+//---------------------------------------------------------------------------------
+// Auxiliary function.
+//---------------------------------------------------------------------------------
+bool CKMCFile::GetCountersForRead_kmc1(const std::string& read, std::vector<uint32>& counters)
+{	
+	uint32 read_len = static_cast<uint32>(read.length());
+	counters.resize(read.length() - kmer_length + 1);
+	std::string transformed_read = read;
+	for (char& c : transformed_read)
+		c = CKmerAPI::num_codes[(uchar)c];
+
+	uint32 i = 0;
+	CKmerAPI kmer(kmer_length);
+	uint32 pos = 0;
+	
+	uint32 counters_pos = 0;
+
+	while (i + kmer_length - 1 < read_len)
+	{
+		bool contains_N = false;
+		while (i < read_len && pos < kmer_length)
+		{
+			if (CKmerAPI::num_codes[(uchar)read[i]] < 0)
+			{
+				pos = 0;
+				kmer.clear();
+				++i;
+				uint32 wrong_kmers = MIN(i - counters_pos, static_cast<uint32>(counters.size()) - counters_pos);
+				fill_n(counters.begin() + counters_pos, wrong_kmers, 0);
+				counters_pos += wrong_kmers;
+				contains_N = true;
+				break;
+			}
+			else
+				kmer.insert2bits(pos++, CKmerAPI::num_codes[(uchar)read[i++]]);
+		}
+		if (contains_N)
+			continue;
+		if (pos == kmer_length)
+		{			
+			counters[counters_pos++] = count_for_kmer_kmc1(kmer);
+		}
+		else
+			break;
+
+		while (i < read_len)
+		{
+			if (CKmerAPI::num_codes[(uchar)read[i]] < 0)
+			{
+				pos = 0;
+				break;
+			}
+			kmer.SHL_insert2bits(CKmerAPI::num_codes[(uchar)read[i++]]);
+			counters[counters_pos++] = count_for_kmer_kmc1(kmer);
+		}
+	}
+	if (counters_pos < counters.size())
+	{
+		fill_n(counters.begin() + counters_pos, counters.size() - counters_pos, 0);
+		counters_pos = static_cast<uint32>(counters.size());
+	}
+	return true;
+}
+
+//---------------------------------------------------------------------------------
+// Auxiliary function.
+//---------------------------------------------------------------------------------
+void CKMCFile::GetSuperKmers(const std::string& transformed_read, super_kmers_t& super_kmers)
+{
+	uint32 i = 0;
+	uint32 len = 0; //length of super k-mer
+	uint32 signature_start_pos;
+	CMmer current_signature(signature_len), end_mmer(signature_len);
+
+	while (i + kmer_length - 1 < transformed_read.length())
+	{
+		bool contains_N = false;
+		//building first signature after 'N' or at the read beginning
+		for (uint32 j = 0; j < signature_len; ++j, ++i)
+		{
+			if (transformed_read[i] < 0)//'N'
+			{
+				contains_N = true;
+				break;
+			}
+		}
+		//signature must be shorter than k-mer so if signature contains 'N', k-mer will contains it also
+		if (contains_N)
+		{
+			++i;
+			continue;
+		}
+		len = signature_len;
+		signature_start_pos = i - signature_len;
+		current_signature.insert(transformed_read.c_str() + signature_start_pos);
+		end_mmer.set(current_signature);
+
+		for (; i < transformed_read.length(); ++i)
+		{
+			if (transformed_read[i] < 0)//'N'
+			{
+				if (len >= kmer_length)
+				{
+					super_kmers.push_back(std::make_tuple(i - len, len, signature_map[current_signature.get()]));
+				}
+				len = 0;
+				++i;
+				break;
+			}
+			end_mmer.insert(transformed_read[i]);
+			if (end_mmer < current_signature)//signature at the end of current k-mer is lower than current
+			{
+				if (len >= kmer_length)
+				{
+					super_kmers.push_back(std::make_tuple(i - len, len, signature_map[current_signature.get()]));
+					len = kmer_length - 1;
+				}
+				current_signature.set(end_mmer);
+				signature_start_pos = i - signature_len + 1;
+			}
+			else if (end_mmer == current_signature)
+			{
+				current_signature.set(end_mmer);
+				signature_start_pos = i - signature_len + 1;
+			}
+			else if (signature_start_pos + kmer_length - 1 < i)//need to find new signature
+			{
+				super_kmers.push_back(std::make_tuple(i - len, len, signature_map[current_signature.get()]));
+				len = kmer_length - 1;
+				//looking for new signature
+				++signature_start_pos;
+				//building first signature in current k-mer
+				end_mmer.insert(transformed_read.c_str() + signature_start_pos);
+				current_signature.set(end_mmer);
+				for (uint32 j = signature_start_pos + signature_len; j <= i; ++j)
+				{
+					end_mmer.insert(transformed_read[j]);
+					if (end_mmer <= current_signature)
+					{
+						current_signature.set(end_mmer);
+						signature_start_pos = j - signature_len + 1;
+					}
+				}
+			}
+			++len;
+		}
+	}
+	if (len >= kmer_length)//last one in read
+	{
+		super_kmers.push_back(std::make_tuple(i - len, len, signature_map[current_signature.get()]));
+	}
+}
+
+//---------------------------------------------------------------------------------
+// Auxiliary function.
+//---------------------------------------------------------------------------------
+bool CKMCFile::GetCountersForRead_kmc2_both_strands(const std::string& read, std::vector<uint32>& counters)
+{
+	counters.resize(read.length() - kmer_length + 1);
+	std::string transformed_read = read;
+	for (char& c : transformed_read)
+		c = CKmerAPI::num_codes[(uchar)c];
+
+	super_kmers_t super_kmers;
+	GetSuperKmers(transformed_read, super_kmers);
+
+	uint32 counters_pos = 0;
+	if (super_kmers.empty())
+	{
+		fill_n(counters.begin(), counters.size(), 0);
+		return true;
+	}
+
+	CKmerAPI kmer(kmer_length), rev_kmer(kmer_length);
+
+	uint32 last_end = 0;
+
+	//'N' somewhere in first k-mer
+	if (std::get<0>(super_kmers.front()) > 0)
+	{
+		fill_n(counters.begin(), std::get<0>(super_kmers.front()), 0);
+		last_end = std::get<0>(super_kmers.front());
+		counters_pos = std::get<0>(super_kmers.front());
+	}
+	for (auto& super_kmer : super_kmers)
+	{
+		//'N's between super k-mers
+		if (last_end < std::get<0>(super_kmer))
+		{
+			uint32 gap = std::get<0>(super_kmer) -last_end;
+			fill_n(counters.begin() + counters_pos, kmer_length + gap - 1, 0);
+			counters_pos += kmer_length + gap - 1;
+		}
+		last_end = std::get<0>(super_kmer) +std::get<1>(super_kmer);
+
+		kmer.from_binary(transformed_read.c_str() + std::get<0>(super_kmer));
+		rev_kmer.from_binary_rev(transformed_read.c_str() + std::get<0>(super_kmer));
+
+		uint32 bin_start_pos = std::get<2>(super_kmer) * single_LUT_size;
+		if(kmer < rev_kmer)
+			counters[counters_pos++] = count_for_kmer_kmc2(kmer, bin_start_pos);
+		else
+			counters[counters_pos++] = count_for_kmer_kmc2(rev_kmer, bin_start_pos);
+
+		for (uint32 i = std::get<0>(super_kmer) +kmer_length; i < std::get<0>(super_kmer) +std::get<1>(super_kmer); ++i)
+		{
+			kmer.SHL_insert2bits(transformed_read[i]);
+			rev_kmer.SHR_insert2bits(3 - transformed_read[i]);
+			if(kmer < rev_kmer)
+				counters[counters_pos++] = count_for_kmer_kmc2(kmer, bin_start_pos);
+			else
+				counters[counters_pos++] = count_for_kmer_kmc2(rev_kmer, bin_start_pos);
+		}
+	}
+	//'N's at the end of read
+	if (counters_pos < counters.size())
+	{
+		fill_n(counters.begin() + counters_pos, counters.size() - counters_pos, 0);
+		counters_pos = static_cast<uint32>(counters.size());
+	}
+
+	return true;
+}
+
+
+//---------------------------------------------------------------------------------
+// Auxiliary function.
+//---------------------------------------------------------------------------------
+bool CKMCFile::GetCountersForRead_kmc2(const std::string& read, std::vector<uint32>& counters)
+{	
+	counters.resize(read.length() - kmer_length + 1);
+	std::string transformed_read = read;
+	for (char& c : transformed_read)
+		c = CKmerAPI::num_codes[(uchar)c];
+	
+	super_kmers_t super_kmers;
+	GetSuperKmers(transformed_read, super_kmers);
+	
+	uint32 counters_pos = 0;
+	if (super_kmers.empty())
+	{
+		fill_n(counters.begin(), counters.size(), 0);
+		return true;
+	}
+
+	CKmerAPI kmer(kmer_length);
+
+	uint32 last_end = 0;
+
+	//'N' somewhere in first k-mer
+	if (std::get<0>(super_kmers.front()) > 0)
+	{
+		fill_n(counters.begin(), std::get<0>(super_kmers.front()), 0);
+		last_end = std::get<0>(super_kmers.front());
+		counters_pos = std::get<0>(super_kmers.front());
+	}
+	for (auto& super_kmer : super_kmers)
+	{
+		//'N's between super k-mers
+		if (last_end < std::get<0>(super_kmer))
+		{
+			uint32 gap = std::get<0>(super_kmer) -last_end;
+			fill_n(counters.begin() + counters_pos, kmer_length + gap - 1, 0);
+			counters_pos += kmer_length + gap - 1;
+		}
+		last_end = std::get<0>(super_kmer) + std::get<1>(super_kmer);
+		
+		kmer.from_binary(transformed_read.c_str() + std::get<0>(super_kmer));
+
+		uint32 bin_start_pos = std::get<2>(super_kmer) * single_LUT_size;
+		counters[counters_pos++] = count_for_kmer_kmc2(kmer, bin_start_pos);
+
+		for (uint32 i = std::get<0>(super_kmer) +kmer_length; i < std::get<0>(super_kmer) +std::get<1>(super_kmer); ++i)
+		{
+			kmer.SHL_insert2bits(transformed_read[i]);
+			counters[counters_pos++] = count_for_kmer_kmc2(kmer, bin_start_pos);
+		}
+	}
+	//'N's at the end of read
+	if (counters_pos < counters.size())
+	{
+		fill_n(counters.begin() + counters_pos, counters.size() - counters_pos, 0);
+		counters_pos = static_cast<uint32>(counters.size());
+	}
+
+	return true;
+}
+
+
+//---------------------------------------------------------------------------------
+// Auxiliary function.
+//---------------------------------------------------------------------------------
+bool CKMCFile::BinarySearch(int64 index_start, int64 index_stop, const CKmerAPI& kmer, uint64& counter, uint32 pattern_offset)
+{
+	if (index_start >= static_cast<int64>(total_kmers))
+		return false;
+	uchar *sufix_byte_ptr = nullptr;
+	uint64 sufix = 0;
+
+	//sufix_offset is always 56
+	uint32 sufix_offset = 56;			// the offset of a sufix is for shifting the sufix towards MSB, to compare the sufix with a pattern
+	// Bytes of a pattern to search are always shifted towards MSB
+
+	uint32 row_index = 0;				// the number of a current row in an array kmer_data
+
+	bool found = false;
+
+	while (index_start <= index_stop)
+	{
+		int64 mid_index = (index_start + index_stop) / 2;
+		sufix_byte_ptr = &sufix_file_buf[mid_index * sufix_rec_size];
+
+		uint64 pattern = 0;
+
+		pattern_offset = (lut_prefix_length + kmer.byte_alignment) * 2;
+
+		row_index = 0;
+		for (uint32 a = 0; a < sufix_size; a++)		//check byte by byte
+		{
+			pattern = kmer.kmer_data[row_index];
+			pattern = pattern << pattern_offset;
+			pattern = pattern & 0xff00000000000000;
+
+			sufix = sufix_byte_ptr[a];
+			sufix = sufix << sufix_offset;
+
+			if (pattern != sufix)
+				break;
+
+			pattern_offset += 8;
+
+			if (pattern_offset == 64)				//the end of a word
+			{
+				pattern_offset = 0;
+				row_index++;
+			}
+		}
+
+		if (pattern == sufix)
+		{
+			found = true;
+			break;
+		}
+		if (sufix < pattern)
+			index_start = mid_index + 1;
+		else
+			index_stop = mid_index - 1;
+	}
+
+	if (found)
+	{
+		sufix_byte_ptr += sufix_size;
+
+		counter = *sufix_byte_ptr;
+
+		for (uint32 b = 1; b < counter_size; b++)
+		{
+			uint64 aux = 0x000000ff & *(sufix_byte_ptr + b);
+
+			aux = aux << 8 * (b);
+			counter = aux | counter;
+		}
+		if (mode != 0)
+		{
+			float float_counter;
+			memcpy(&float_counter, &counter, counter_size);
+			return (float_counter >= min_count) && (float_counter <= max_count);
+		}
+		return (counter >= min_count) && (counter <= max_count);
+	}
+	return false;
+}
+
+
+// ***** EOF

=== added file 'kmc_api/kmc_file.h'
--- old/kmc_api/kmc_file.h	1970-01-01 00:00:00 +0000
+++ new/kmc_api/kmc_file.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,175 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+  Version: 3.1.1
+  Date   : 2019-05-19
+ */
+
+#ifndef _KMC_FILE_H
+#define _KMC_FILE_H
+
+#include "kmer_defs.h"
+#include "kmer_api.h"
+#include <string>
+#include <vector>
+
+struct CKMCFileInfo
+{
+	uint32 kmer_length;
+	uint32 mode;
+	uint32 counter_size;
+	uint32 lut_prefix_length;
+	uint32 signature_len;	
+	uint32 min_count;
+	uint64 max_count;
+	bool both_strands;
+	uint64 total_kmers;
+};
+
+class CKMCFile
+{
+	enum open_mode {closed, opened_for_RA, opened_for_listing};
+	open_mode is_opened;
+	uint64 suf_file_left_to_read = 0; // number of bytes that are yet to read in a listing mode
+	uint64 suffix_file_total_to_read = 0; // number of bytes that constitutes records in kmc_suf file
+	bool end_of_file;
+
+	FILE *file_pre;
+	FILE *file_suf;
+
+	uint64* prefix_file_buf;
+	uint64 prefix_file_buf_size;
+	uint64 prefix_index;			// The current prefix's index in an array "prefix_file_buf", readed from *.kmc_pre
+	uint32 single_LUT_size;			// The size of a single LUT (in no. of elements)
+
+	uint32* signature_map;
+	uint32 signature_map_size;
+	
+	uchar* sufix_file_buf;
+	uint64 sufix_number;			// The sufix's number to be listed
+	uint64 index_in_partial_buf;	// The current byte's number in an array "sufix_file_buf", for listing mode
+
+	uint32 kmer_length;
+	uint32 mode;
+	uint32 counter_size;
+	uint32 lut_prefix_length;
+	uint32 signature_len;
+	uint32 min_count;
+	uint64 max_count;
+	uint64 total_kmers;
+	bool both_strands;
+
+	uint32 kmc_version;
+	uint32 sufix_size;		// sufix's size in bytes 
+	uint32 sufix_rec_size;  // sufix_size + counter_size
+
+	uint32 original_min_count;
+	uint64 original_max_count;
+
+	static uint64 part_size; // the size of a block readed to sufix_file_buf, in listing mode 
+	
+	bool BinarySearch(int64 index_start, int64 index_stop, const CKmerAPI& kmer, uint64& counter, uint32 pattern_offset);
+
+	// Open a file, recognize its size and check its marker. Auxiliary function.
+	bool OpenASingleFile(const std::string &file_name, FILE *&file_handler, uint64 &size, char marker[]);	
+
+	// Recognize current parameters. Auxiliary function.
+	bool ReadParamsFrom_prefix_file_buf(uint64 &size);	
+
+	// Reload a contents of an array "sufix_file_buf" for listing mode. Auxiliary function. 
+	void Reload_sufix_file_buf();
+
+	// Implementation of GetCountersForRead for kmc1 database format for both strands
+	bool GetCountersForRead_kmc1_both_strands(const std::string& read, std::vector<uint32>& counters);
+
+	// Implementation of GetCountersForRead for kmc1 database format without choosing canonical k-mer
+	bool GetCountersForRead_kmc1(const std::string& read, std::vector<uint32>& counters);		
+
+	using super_kmers_t = std::vector<std::tuple<uint32, uint32, uint32>>;//start_pos, len, bin_no
+	void GetSuperKmers(const std::string& transformed_read, super_kmers_t& super_kmers);
+
+	// Implementation of GetCountersForRead for kmc2 database format for both strands
+	bool GetCountersForRead_kmc2_both_strands(const std::string& read, std::vector<uint32>& counters);
+
+	// Implementation of GetCountersForRead for kmc2 database format
+	bool GetCountersForRead_kmc2(const std::string& read, std::vector<uint32>& counters);
+public:
+		
+	CKMCFile();
+	~CKMCFile();
+
+	// Open files *.kmc_pre & *.kmc_suf, read them to RAM, close files. *.kmc_suf is opened for random access
+	bool OpenForRA(const std::string &file_name);
+
+	// Open files *kmc_pre & *.kmc_suf, read *.kmc_pre to RAM, *.kmc_suf is buffered
+	bool OpenForListing(const std::string& file_name);
+
+	// Return next kmer in CKmerAPI &kmer. Return its counter in float &count. Return true if not EOF
+	bool ReadNextKmer(CKmerAPI &kmer, float &count);
+
+	bool ReadNextKmer(CKmerAPI &kmer, uint64 &count); //for small k-values when counter may be longer than 4bytes
+	
+	bool ReadNextKmer(CKmerAPI &kmer, uint32 &count);
+	// Release memory and close files in case they were opened 
+	bool Close();
+
+	// Set the minimal value for a counter. Kmers with counters below this theshold are ignored
+	bool SetMinCount(uint32 x);
+
+	// Return a value of min_count. Kmers with counters below this theshold are ignored 
+	uint32 GetMinCount(void);
+
+	// Set the maximal value for a counter. Kmers with counters above this theshold are ignored
+	bool SetMaxCount(uint32 x);
+
+	// Return a value of max_count. Kmers with counters above this theshold are ignored 
+	uint64 GetMaxCount(void);
+	
+	//Return true if kmc was run without -b switch.
+	bool GetBothStrands(void);
+
+	// Return the total number of kmers between min_count and max_count
+	uint64 KmerCount(void);
+
+	// Return the length of kmers
+	uint32 KmerLength(void);
+
+	// Set initial values to enable listing kmers from the begining. Only in listing mode
+	bool RestartListing(void);
+
+	// Return true if all kmers are listed
+	bool Eof(void);
+
+	// Return true if kmer exists. In this case return kmer's counter in count
+	bool CheckKmer(CKmerAPI &kmer, float &count);
+
+	bool CheckKmer(CKmerAPI &kmer, uint32 &count);
+
+	bool CheckKmer(CKmerAPI &kmer, uint64 &count);
+
+	// Return true if kmer exists
+	bool IsKmer(CKmerAPI &kmer);
+
+	// Set original (readed from *.kmer_pre) values for min_count and max_count
+	void ResetMinMaxCounts(void);
+
+	// Get current parameters from kmer_database
+	bool Info(uint32 &_kmer_length, uint32 &_mode, uint32 &_counter_size, uint32 &_lut_prefix_length, uint32 &_signature_len, uint32 &_min_count, uint64 &_max_count, uint64 &_total_kmers);
+	
+	// Get current parameters from kmer_database
+	bool Info(CKMCFileInfo& info);
+
+	// Get counters for all k-mers in read
+	bool GetCountersForRead(const std::string& read, std::vector<uint32>& counters);
+	bool GetCountersForRead(const std::string& read, std::vector<float>& counters);
+	private:
+		uint32 count_for_kmer_kmc1(CKmerAPI& kmer);
+		uint32 count_for_kmer_kmc2(CKmerAPI& kmer, uint32 bin_start_pos);
+};
+
+#endif
+
+// ***** EOF

=== added file 'kmc_api/kmer_api.cpp'
--- old/kmc_api/kmer_api.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_api/kmer_api.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,48 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+  Authors: Sebastian Deorowicz and Agnieszka Debudaj-Grabysz
+
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+
+#include "stdafx.h"
+#include "kmer_api.h"
+#include <vector>
+#include <math.h>
+
+using namespace std;
+
+const char CKmerAPI::char_codes[] = {'A','C', 'G', 'T'};	
+char CKmerAPI::num_codes[];
+CKmerAPI::_si CKmerAPI::_init; 
+uchar CKmerAPI::rev_comp_bytes_LUT[] = {
+    0xff, 0xbf, 0x7f, 0x3f, 0xef, 0xaf, 0x6f, 0x2f, 0xdf, 0x9f, 0x5f, 0x1f, 0xcf, 0x8f, 0x4f, 0x0f,
+    0xfb, 0xbb, 0x7b, 0x3b, 0xeb, 0xab, 0x6b, 0x2b, 0xdb, 0x9b, 0x5b, 0x1b, 0xcb, 0x8b, 0x4b, 0x0b,
+    0xf7, 0xb7, 0x77, 0x37, 0xe7, 0xa7, 0x67, 0x27, 0xd7, 0x97, 0x57, 0x17, 0xc7, 0x87, 0x47, 0x07,
+    0xf3, 0xb3, 0x73, 0x33, 0xe3, 0xa3, 0x63, 0x23, 0xd3, 0x93, 0x53, 0x13, 0xc3, 0x83, 0x43, 0x03,
+    0xfe, 0xbe, 0x7e, 0x3e, 0xee, 0xae, 0x6e, 0x2e, 0xde, 0x9e, 0x5e, 0x1e, 0xce, 0x8e, 0x4e, 0x0e,
+    0xfa, 0xba, 0x7a, 0x3a, 0xea, 0xaa, 0x6a, 0x2a, 0xda, 0x9a, 0x5a, 0x1a, 0xca, 0x8a, 0x4a, 0x0a,
+    0xf6, 0xb6, 0x76, 0x36, 0xe6, 0xa6, 0x66, 0x26, 0xd6, 0x96, 0x56, 0x16, 0xc6, 0x86, 0x46, 0x06,
+    0xf2, 0xb2, 0x72, 0x32, 0xe2, 0xa2, 0x62, 0x22, 0xd2, 0x92, 0x52, 0x12, 0xc2, 0x82, 0x42, 0x02,
+    0xfd, 0xbd, 0x7d, 0x3d, 0xed, 0xad, 0x6d, 0x2d, 0xdd, 0x9d, 0x5d, 0x1d, 0xcd, 0x8d, 0x4d, 0x0d,
+    0xf9, 0xb9, 0x79, 0x39, 0xe9, 0xa9, 0x69, 0x29, 0xd9, 0x99, 0x59, 0x19, 0xc9, 0x89, 0x49, 0x09,
+    0xf5, 0xb5, 0x75, 0x35, 0xe5, 0xa5, 0x65, 0x25, 0xd5, 0x95, 0x55, 0x15, 0xc5, 0x85, 0x45, 0x05,
+    0xf1, 0xb1, 0x71, 0x31, 0xe1, 0xa1, 0x61, 0x21, 0xd1, 0x91, 0x51, 0x11, 0xc1, 0x81, 0x41, 0x01,
+    0xfc, 0xbc, 0x7c, 0x3c, 0xec, 0xac, 0x6c, 0x2c, 0xdc, 0x9c, 0x5c, 0x1c, 0xcc, 0x8c, 0x4c, 0x0c,
+    0xf8, 0xb8, 0x78, 0x38, 0xe8, 0xa8, 0x68, 0x28, 0xd8, 0x98, 0x58, 0x18, 0xc8, 0x88, 0x48, 0x08,
+    0xf4, 0xb4, 0x74, 0x34, 0xe4, 0xa4, 0x64, 0x24, 0xd4, 0x94, 0x54, 0x14, 0xc4, 0x84, 0x44, 0x04,
+    0xf0, 0xb0, 0x70, 0x30, 0xe0, 0xa0, 0x60, 0x20, 0xd0, 0x90, 0x50, 0x10, 0xc0, 0x80, 0x40, 0x00
+};
+uint64 CKmerAPI::alignment_mask[] = {
+    0xFFFFFFFFFFFFFFFFULL,
+    0x3FFFFFFFFFFFFFFFULL,
+    0x0FFFFFFFFFFFFFFFULL,
+    0x03FFFFFFFFFFFFFFULL,
+    0x00FFFFFFFFFFFFFFULL
+};
+
+// ***** EOF

=== added file 'kmc_api/kmer_api.h'
--- old/kmc_api/kmer_api.h	1970-01-01 00:00:00 +0000
+++ new/kmc_api/kmer_api.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,679 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Sebastian Deorowicz and Agnieszka Debudaj-Grabysz
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _KMER_API_H
+#define _KMER_API_H
+
+
+#include "kmer_defs.h"
+#include <string>
+#include <iostream>
+#include <vector>
+#include "mmer.h"
+class CKMCFile;
+
+class CKmerAPI
+{
+protected:
+
+	uint64 *kmer_data;				// An array to store kmer's data. On 64 bits 32 symbols can be stored
+									// Data are shifted to let sufix's symbols to start with a border of a byte
+
+	
+	uint32 kmer_length;				// Kmer's length, in symbols
+	uchar byte_alignment;			// A number of "empty" symbols placed before prefix to let sufix's symbols to start with a border of a byte
+
+	uint32 no_of_rows;				// A number of 64-bits words allocated for kmer_data 	
+
+	friend class CKMCFile;
+	
+	//----------------------------------------------------------------------------------
+	inline void clear()
+	{
+		memset(kmer_data, 0, sizeof(*kmer_data) * no_of_rows);
+	}
+
+	//----------------------------------------------------------------------------------
+	inline void insert2bits(uint32 pos, uchar val)
+	{
+		kmer_data[(pos + byte_alignment) >> 5] += (uint64)val << (62 - (((pos + byte_alignment) & 31) * 2));
+	}
+
+	inline uchar extract2bits(uint32 pos)
+	{
+		return (kmer_data[(pos + byte_alignment) >> 5] >> (62 - (((pos + byte_alignment) & 31) * 2))) & 3;
+	}
+	//----------------------------------------------------------------------------------
+	inline void SHL_insert2bits(uchar val)
+	{
+		kmer_data[0] <<= 2;
+		if (byte_alignment)
+		{
+			uint64 mask = ~(((1ull << 2 * byte_alignment) - 1) << (64 - 2 * byte_alignment));
+			kmer_data[0] &= mask;
+		}
+		for (uint32 i = 1; i < no_of_rows; ++i)
+		{
+			kmer_data[i - 1] += kmer_data[i] >> 62;
+			kmer_data[i] <<= 2;
+		}
+		kmer_data[no_of_rows - 1] += (uint64)val << (62 - (((kmer_length - 1 + byte_alignment) & 31) * 2));
+	}
+	
+	//----------------------------------------------------------------------------------
+	inline void SHR_insert2bits(uchar val)
+	{
+		for (uint32 i = no_of_rows - 1; i > 0; --i)
+		{
+			kmer_data[i] >>= 2;
+			kmer_data[i] += kmer_data[i - 1] << 62;
+		}
+		kmer_data[0] >>= 2;
+		kmer_data[no_of_rows - 1] &= ~((1ull << ((32 - (kmer_length + byte_alignment - (no_of_rows - 1) * 32)) * 2)) - 1);//mask falling of symbol
+		kmer_data[0] += ((uint64)val << 62) >> (byte_alignment * 2);
+	}
+
+	// ----------------------------------------------------------------------------------
+	inline void from_binary(const char* kmer)
+	{
+		clear();
+		for (uint32 i = 0; i < kmer_length; ++i)
+			insert2bits(i, kmer[i]);
+	}
+
+	// ----------------------------------------------------------------------------------
+	inline void from_binary_rev(const char* kmer)
+	{
+		clear();
+		for (uint32 i = 0; i < kmer_length; ++i)
+			insert2bits(i, 3 - kmer[kmer_length - i - 1]);
+	}
+
+	// ----------------------------------------------------------------------------------
+	template<typename RandomAccessIterator>
+	inline void to_string_impl(RandomAccessIterator iter)
+	{
+		uchar *byte_ptr;
+		uchar c;
+		uchar temp_byte_alignment = byte_alignment;
+		uint32 cur_string_size = 0;
+		for (uint32 row_counter = 0; row_counter < no_of_rows; row_counter++)
+		{
+			byte_ptr = reinterpret_cast<uchar*>(&kmer_data[row_counter]);
+
+			byte_ptr += 7;					// shift a pointer towards a MSB
+
+			for (uint32 i = 0; (i < kmer_length) && (i < 32); i += 4)		// 32 symbols of any "row" in kmer_data
+			{
+				if ((i == 0) && temp_byte_alignment)				// check if a byte_alignment placed before a prefix is to be skipped
+					temp_byte_alignment--;
+				else
+				{
+					c = 0xc0 & *byte_ptr;			//11000000
+					c = c >> 6;
+					*(iter + cur_string_size++) = char_codes[c];
+					if (cur_string_size == kmer_length) break;
+				}
+
+				if ((i == 0) && temp_byte_alignment)				// check if a  byte_alignment placed before a prefix is to be skipped
+					temp_byte_alignment--;
+				else
+				{
+					c = 0x30 & *byte_ptr;			//00110000
+					c = c >> 4;
+					*(iter + cur_string_size++) = char_codes[c];
+					if (cur_string_size == kmer_length) break;
+				}
+
+				if ((i == 0) && temp_byte_alignment)				// check if a  byte_alignment placed before a prefix is to be skipped
+					temp_byte_alignment--;
+				else
+				{
+					c = 0x0c & *byte_ptr;			//00001100
+					c = c >> 2;
+					*(iter + cur_string_size++) = char_codes[c];
+					if (cur_string_size == kmer_length) break;
+				}
+				// no need to check byte alignment as its length is at most 3 
+				c = 0x03 & *byte_ptr;			//00000011
+				*(iter + cur_string_size++) = char_codes[c];
+				if (cur_string_size == kmer_length) break;
+
+				byte_ptr--;
+			}
+		}
+	}
+	
+	// ----------------------------------------------------------------------------------
+	template<typename RandomAccessIterator>
+	inline bool from_string_impl(const RandomAccessIterator iter, uint32 len)
+	{
+		unsigned char c_char;
+		uchar c_binary;
+		uchar temp_byte_alignment;
+		if (kmer_length != len)
+		{
+			if (kmer_length && kmer_data)
+				delete[] kmer_data;
+
+			kmer_length = len;
+
+			if (kmer_length % 4)
+				byte_alignment = 4 - (kmer_length % 4);
+			else
+				byte_alignment = 0;
+
+
+			if (kmer_length != 0)
+			{
+				no_of_rows = (((kmer_length + byte_alignment) % 32) ? (kmer_length + byte_alignment) / 32 + 1 : (kmer_length + byte_alignment) / 32);
+				//no_of_rows = (int)ceil((double)(kmer_length + byte_alignment) / 32);
+				kmer_data = new uint64[no_of_rows];
+				//memset(kmer_data, 0, sizeof(*kmer_data) * no_of_rows);
+			}
+		}
+
+		memset(kmer_data, 0, sizeof(*kmer_data) * no_of_rows);
+		temp_byte_alignment = byte_alignment;
+		uint32 i = 0;
+		uint32 i_in_string = 0;
+		uchar *byte_ptr;
+
+		for (uint32 row_index = 0; row_index < no_of_rows; row_index++)
+		{
+			byte_ptr = reinterpret_cast<uchar*>(&kmer_data[row_index]);
+			byte_ptr += 7;					// shift a pointer towards a MSB
+
+			while (i < kmer_length)
+			{
+				if ((i_in_string == 0) && temp_byte_alignment)				// check if a byte_alignment placed before a prefix is to be skipped
+				{
+					temp_byte_alignment--;
+					i++;
+				}
+				else
+				{
+					c_char = *(iter + i_in_string);
+					c_binary = num_codes[c_char];
+					c_binary = c_binary << 6;		//11000000
+					*byte_ptr = *byte_ptr | c_binary;
+					i++;
+					i_in_string++;
+					if (i_in_string == kmer_length) break;
+				}
+
+				if ((i_in_string == 0) && temp_byte_alignment)				// check if a byte_alignment placed before a prefix is to be skipped
+				{
+					temp_byte_alignment--;
+					i++;
+				}
+				else
+				{
+					c_char = *(iter + i_in_string);
+					c_binary = num_codes[c_char];
+					c_binary = c_binary << 4;
+					*byte_ptr = *byte_ptr | c_binary;
+					i++;
+					i_in_string++;
+					if (i_in_string == kmer_length) break;
+				}
+
+				//!!!if((i == 0) && temp_byte_alignment)	//poprawka zg3oszona przez Maaka D3ugosza			// check if a byte_alignment placed before a prefix is to be skipped
+				if ((i_in_string == 0) && temp_byte_alignment)				// check if a byte_alignment placed before a prefix is to be skipped
+				{
+					temp_byte_alignment--;
+					i++;
+				}
+				else
+				{
+					c_char = *(iter + i_in_string);
+					c_binary = num_codes[c_char];
+					c_binary = c_binary << 2;
+					*byte_ptr = *byte_ptr | c_binary;
+					i++;
+					i_in_string++;
+					if (i_in_string == kmer_length) break;
+				}
+
+				c_char = *(iter + i_in_string);
+				c_binary = num_codes[c_char];
+				*byte_ptr = *byte_ptr | c_binary;
+				i++;
+				i_in_string++;
+				if (i_in_string == kmer_length) break;
+
+				if (i % 32 == 0)
+					break; //check if a new "row" is to be started
+				byte_ptr--;
+			}
+		};
+		return true;
+	}
+public:
+	static const char char_codes[];
+	static char num_codes[256];
+	static uchar rev_comp_bytes_LUT[];
+	static uint64 alignment_mask[];
+	struct _si  
+	{
+		_si()
+		{
+			for (int i = 0; i < 256; i++)
+                num_codes[i] = -1;
+			num_codes['A'] = num_codes['a'] = 0;
+			num_codes['C'] = num_codes['c'] = 1;
+			num_codes['G'] = num_codes['g'] = 2;
+			num_codes['T'] = num_codes['t'] = 3;
+        }
+    } static _init;
+
+
+// ----------------------------------------------------------------------------------
+// The constructor creates kmer for the number of symbols equal to length. 
+// The array kmer_data has the size of ceil((length + byte_alignment) / 32))
+// IN	: length - a number of symbols of a kmer
+// ----------------------------------------------------------------------------------
+	inline CKmerAPI(uint32 length = 0)
+	{
+		if(length)
+		{
+			if(length % 4)
+				byte_alignment = 4 - (length % 4);	
+			else
+				byte_alignment = 0;
+
+			no_of_rows = (((length + byte_alignment) % 32) ? (length + byte_alignment) / 32 + 1 : (length + byte_alignment) / 32); 
+			//no_of_rows = (int)ceil((double)(length + byte_alignment) / 32);
+			kmer_data = new uint64[no_of_rows];
+
+			memset(kmer_data, 0, sizeof(*kmer_data) * no_of_rows);
+		}
+		else
+		{
+			kmer_data = NULL;
+			no_of_rows = 0;
+			byte_alignment = 0;
+		}
+		kmer_length = length;
+	};
+//-----------------------------------------------------------------------
+// The destructor
+//-----------------------------------------------------------------------
+	inline ~CKmerAPI()
+	{
+		if (kmer_data != NULL)
+			delete [] kmer_data;
+	};
+
+//-----------------------------------------------------------------------
+// The copy constructor
+//-----------------------------------------------------------------------
+	inline CKmerAPI(const CKmerAPI &kmer)
+	{
+		kmer_length = kmer.kmer_length;
+		byte_alignment = kmer.byte_alignment;
+		no_of_rows = kmer.no_of_rows;
+		
+		kmer_data = new uint64[no_of_rows];
+			
+		for(uint32 i = 0; i < no_of_rows; i++)
+			kmer_data[i] = kmer.kmer_data[i];
+
+	};
+
+//-----------------------------------------------------------------------
+// The operator =
+//-----------------------------------------------------------------------	
+	inline CKmerAPI& operator=(const CKmerAPI &kmer)
+	{
+		if(kmer.kmer_length != kmer_length)		
+		{
+			if(kmer_length && kmer_data)
+				delete [] kmer_data;
+		
+			kmer_length = kmer.kmer_length;
+			byte_alignment = kmer.byte_alignment;
+			no_of_rows = kmer.no_of_rows;
+		
+			kmer_data = new uint64[no_of_rows];
+		}
+
+		for(uint32 i = 0; i < no_of_rows; i++)
+			kmer_data[i] = kmer.kmer_data[i];
+
+		return *this;
+	};
+
+//-----------------------------------------------------------------------
+// The operator ==
+//-----------------------------------------------------------------------
+	inline bool operator==(const CKmerAPI &kmer) const
+	{
+			if(kmer.kmer_length != kmer_length)
+				return false;
+
+			for(uint32 i = 0; i < no_of_rows; i++)
+				if(kmer.kmer_data[i] != kmer_data[i])
+					return false;
+
+			return true;
+
+	};
+
+//-----------------------------------------------------------------------
+// Operator < . If arguments differ in length a result is undefined
+//-----------------------------------------------------------------------
+	inline bool operator<(const CKmerAPI &kmer) const
+	{
+			if(kmer.kmer_length != kmer_length)
+				return false;					
+
+			for(uint32 i = 0; i < no_of_rows; i++)
+				if(kmer.kmer_data[i] > kmer_data[i])
+					return true;
+				else
+					if(kmer.kmer_data[i] < kmer_data[i])
+						return false;
+				
+			return false;
+	};
+
+//-----------------------------------------------------------------------
+// Return a symbol of a kmer from an indicated position (numbered form 0).
+// The symbol is returned as an ASCI character A/C/G/T
+// IN	: pos - a position of a symbol
+// RET	: symbol - a symbol placed on a position pos
+//-----------------------------------------------------------------------
+	inline char get_asci_symbol(unsigned int pos)
+	{
+		if(pos >= kmer_length)
+			return 0;
+		
+		uint32 current_row = (pos + byte_alignment) / 32;
+		uint32 current_pos = ((pos + byte_alignment) % 32) * 2;
+		uint64 mask = 0xc000000000000000 >> current_pos;
+		uint64 symbol = kmer_data[current_row] & mask;
+		symbol = symbol >> (64 - current_pos - 2);
+		return char_codes[symbol];
+	
+	};
+
+	//-----------------------------------------------------------------------
+	// Return a symbol of a kmer from an indicated position (numbered form 0)
+	// The symbol is returned as a numerical value 0/1/2/3
+	// IN	: pos - a position of a symbol
+	// RET	: symbol - a symbol placed on a position pos
+	//-----------------------------------------------------------------------
+	inline uchar get_num_symbol(unsigned int pos)
+	{
+		if (pos >= kmer_length)
+			return 0;
+
+		uint32 current_row = (pos + byte_alignment) / 32;
+		uint32 current_pos = ((pos + byte_alignment) % 32) * 2;
+		uint64 mask = 0xc000000000000000 >> current_pos;
+		uint64 symbol = kmer_data[current_row] & mask;
+		symbol = symbol >> (64 - current_pos - 2);
+		uchar* byte_ptr = reinterpret_cast<uchar*>(&symbol);
+		return *byte_ptr;
+
+	};
+
+	//-----------------------------------------------------------------------
+	// Convert kmer into string (an alphabet ACGT)
+	// RET	: string kmer
+	//-----------------------------------------------------------------------
+	inline std::string to_string()
+	{
+		std::string string_kmer;		
+		string_kmer.resize(kmer_length);
+		to_string_impl(string_kmer.begin());	
+		return string_kmer;
+	};
+	//-----------------------------------------------------------------------
+	// Convert kmer into string (an alphabet ACGT). The function assumes enough memory was allocated
+	// OUT	: str - string kmer. 
+	//-----------------------------------------------------------------------
+	inline void to_string(char *str)
+	{
+		to_string_impl(str);
+		str[kmer_length] = '\0';
+	};
+
+
+	inline void to_long(std::vector<uint64>& kmer)
+	{
+		kmer.resize(no_of_rows);
+		uint32 offset = 62 - ((kmer_length - 1 + byte_alignment) & 31) * 2;
+		if (offset)
+		{
+			for (int32 i = no_of_rows - 1; i >= 1; --i)
+			{
+				kmer[i] = kmer_data[i] >> offset;
+				kmer[i] += kmer_data[i - 1] << (64 - offset);
+			}
+			kmer[0] = kmer_data[0] >> offset;
+		}
+		else
+		{
+			for (int32 i = no_of_rows - 1; i >= 0; --i)			
+				kmer[i] = kmer_data[i];						
+		}
+	}
+
+	//-----------------------------------------------------------------------
+	// Convert kmer into string (an alphabet ACGT)
+	// OUT 	: str - string kmer
+	//-----------------------------------------------------------------------
+	inline void to_string(std::string &str)
+	{	
+		str.resize(kmer_length);
+		to_string_impl(str.begin());
+	};
+
+	//-----------------------------------------------------------------------
+	// Convert a string of an alphabet ACGT into a kmer of a CKmerAPI
+	// IN	: kmer_string	- a string of an alphabet ACGT
+	// RET	: true			- if succesfull
+	//-----------------------------------------------------------------------
+	inline bool from_string(const char* kmer_string)
+	{
+		uint32 len = 0;
+		for (;  kmer_string[len] != '\0' ; ++len)
+		{
+			if (num_codes[(uchar)kmer_string[len]] == -1)
+				return false;
+		}
+		return from_string_impl(kmer_string, len);
+	}
+
+	//-----------------------------------------------------------------------
+	// Convert a string of an alphabet ACGT into a kmer of a CKmerAPI
+	// IN	: kmer_string	- a string of an alphabet ACGT
+	// RET	: true			- if succesfull
+	//-----------------------------------------------------------------------
+	inline bool from_string(const std::string& kmer_string)
+	{					
+		for (uint32 ii = 0; ii < kmer_string.size(); ++ii)
+		{
+			if (num_codes[(uchar)kmer_string[ii]] == -1)
+				return false;
+		}
+		return from_string_impl(kmer_string.begin(), static_cast<uint32>(kmer_string.length()));		
+	}
+
+	//-----------------------------------------------------------------------
+	// Convert k-mer to its reverse complement
+	//-----------------------------------------------------------------------
+	inline bool reverse()
+	{
+		if (kmer_data == NULL)
+		{
+			return false;
+		}
+
+		// number of bytes used to store the k-mer in the 0-th row
+		const uint32 size_in_byte = ((kmer_length + byte_alignment) / 4) - 8 * (no_of_rows - 1);
+		uchar* byte1;
+		uchar* byte2;
+
+		if (no_of_rows == 1)
+		{
+			*kmer_data <<= 2 * byte_alignment;
+			byte1 = reinterpret_cast<uchar*>(kmer_data) + 8 - size_in_byte;
+			byte2 = reinterpret_cast<uchar*>(kmer_data) + 7;
+
+			for (uint32 i_bytes = 0; i_bytes < size_in_byte / 2; ++i_bytes)
+			{
+				unsigned char temp = rev_comp_bytes_LUT[*byte1];
+				*byte1 = rev_comp_bytes_LUT[*byte2];
+				*byte2 = temp;
+
+				++byte1;
+				--byte2;
+			}
+
+			if (size_in_byte % 2)
+			{
+				*byte1 = rev_comp_bytes_LUT[*byte1];
+			}
+		}
+		else
+		{
+			uint64 to_less_sign_row = 0;
+			if (size_in_byte != 8) {
+				// move bits right
+
+				for (uint32 i_rows = no_of_rows - 1; i_rows > 0; --i_rows)
+				{
+					// more significant rows
+
+					kmer_data[i_rows] >>= 64 - 8 * size_in_byte - 2 * byte_alignment;
+
+					uint64 previous = kmer_data[i_rows - 1];
+					previous <<= 8 * size_in_byte + 2 * byte_alignment;
+					kmer_data[i_rows] |= previous;
+
+					byte1 = reinterpret_cast<uchar*>(kmer_data + i_rows);
+					byte2 = reinterpret_cast<uchar*>(kmer_data + i_rows) + 7;
+
+					for (int i_bytes = 0; i_bytes < 4; ++i_bytes)
+					{
+						unsigned char temp = rev_comp_bytes_LUT[*byte1];
+						*byte1 = rev_comp_bytes_LUT[*byte2];
+						*byte2 = temp;
+
+						++byte1;
+						--byte2;
+					}
+				}
+
+				kmer_data[0] >>= 64 - 8 * size_in_byte - 2 * byte_alignment;
+				kmer_data[0] <<= 64 - 8 * size_in_byte;
+			}
+			else
+			{
+				// move bits left
+
+				for (uint32 i_rows = no_of_rows - 1; i_rows > 0; --i_rows)
+				{
+					// more significant rows
+
+					uint64 from_less_sign_row = to_less_sign_row;
+					if (byte_alignment != 0)
+					{
+						to_less_sign_row = kmer_data[i_rows];
+						to_less_sign_row >>= 64 - 2 * byte_alignment;
+					}
+
+					kmer_data[i_rows] <<= 2 * byte_alignment;
+					kmer_data[i_rows] |= from_less_sign_row;
+
+					byte1 = reinterpret_cast<uchar*>(kmer_data + i_rows);
+					byte2 = reinterpret_cast<uchar*>(kmer_data + i_rows) + 7;
+
+					for (int i_bytes = 0; i_bytes < 4; ++i_bytes)
+					{
+						unsigned char temp = rev_comp_bytes_LUT[*byte1];
+						*byte1 = rev_comp_bytes_LUT[*byte2];
+						*byte2 = temp;
+
+						++byte1;
+						--byte2;
+					}
+				}
+
+				kmer_data[0] <<= 2 * byte_alignment;
+			}
+
+			kmer_data[0] |= to_less_sign_row;
+
+			byte1 = reinterpret_cast<uchar*>(kmer_data) + 8 - size_in_byte;
+			byte2 = reinterpret_cast<uchar*>(kmer_data) + 7;
+
+			for (uint32 i_bytes = 0; i_bytes < size_in_byte / 2; ++i_bytes)
+			{
+				unsigned char temp = rev_comp_bytes_LUT[*byte1];
+				*byte1 = rev_comp_bytes_LUT[*byte2];
+				*byte2 = temp;
+
+				++byte1;
+				--byte2;
+			}
+
+			if (size_in_byte % 2)
+			{
+				*byte1 = rev_comp_bytes_LUT[*byte1];
+			}
+
+			for (uint32 i_rows = 0; i_rows < no_of_rows / 2; ++i_rows)
+			{
+				std::swap(kmer_data[i_rows], kmer_data[no_of_rows - i_rows - 1]);
+			}
+		}
+
+		// clear alignment
+		*kmer_data &= alignment_mask[byte_alignment];
+
+		return true;
+	}
+
+//-----------------------------------------------------------------------
+// Counts a signature of an existing kmer
+// IN	: sig_len	- the length of a signature
+// RET	: signature value
+//-----------------------------------------------------------------------
+	 uint32 get_signature(uint32 sig_len)
+	 {
+		 uchar symb;
+		 CMmer cur_mmr(sig_len);
+		 
+		 for(uint32 i = 0; i < sig_len; ++i)
+		 {
+			 symb = get_num_symbol(i);
+			 cur_mmr.insert(symb);
+		 }
+		 CMmer min_mmr(cur_mmr);
+		 for (uint32 i = sig_len; i < kmer_length; ++i)
+		 {
+			 symb = get_num_symbol(i);
+			 cur_mmr.insert(symb);
+			 
+			 if (cur_mmr < min_mmr)
+				 min_mmr = cur_mmr;
+		 }
+		 return min_mmr.get();
+	 }	
+};
+
+
+#endif
+
+// ***** EOF

=== added file 'kmc_api/kmer_defs.h'
--- old/kmc_api/kmer_defs.h	1970-01-01 00:00:00 +0000
+++ new/kmc_api/kmer_defs.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,55 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+  Authors: Sebastian Deorowicz and Agnieszka Debudaj-Grabysz
+
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+
+#ifndef _KMER_DEFS_H
+#define _KMER_DEFS_H
+
+#define KMC_VER		"3.1.1"
+#define KMC_DATE	"2019-05-19"
+
+#ifndef MIN
+#define MIN(x,y)	((x) < (y) ? (x) : (y))
+#endif
+
+#ifndef WIN32
+	#include <stdint.h>
+	#include <stdio.h>
+	#include <stdlib.h>
+	#include <cmath>
+	#include <string.h>
+
+	#define _TCHAR	char
+	#define _tmain	main
+
+	#define my_fopen    fopen
+	#define my_fseek    fseek
+	#define my_ftell    ftell
+
+
+	#include <stdio.h>
+	#include <ext/algorithm>
+	#include <iostream>
+
+#else
+	#define my_fopen    fopen
+	#define my_fseek    _fseeki64
+	#define my_ftell    _ftelli64
+#endif
+	//typedef unsigned char uchar;
+
+	typedef int int32;
+	typedef unsigned int uint32;
+	typedef long long int64;
+	typedef unsigned long long uint64;
+	typedef unsigned char uchar;
+#endif
+
+// ***** EOF

=== added file 'kmc_api/mmer.cpp'
--- old/kmc_api/mmer.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_api/mmer.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,61 @@
+#include "stdafx.h"
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "../kmc_api/mmer.h"
+
+
+uint32 CMmer::norm5[];
+uint32 CMmer::norm6[];
+uint32 CMmer::norm7[];
+uint32 CMmer::norm8[];
+uint32 CMmer::norm9[];
+uint32 CMmer::norm10[];
+uint32 CMmer::norm11[];
+
+CMmer::_si CMmer::_init;
+
+
+//--------------------------------------------------------------------------
+CMmer::CMmer(uint32 _len)
+{
+	switch (_len)
+	{
+	case 5:
+		norm = norm5;
+		break;
+	case 6:
+		norm = norm6;
+		break;
+	case 7:
+		norm = norm7;
+		break;
+	case 8:
+		norm = norm8;
+		break;
+	case 9:
+		norm = norm9;
+		break;
+	case 10:
+		norm = norm10;
+		break;
+	case 11:
+		norm = norm11;
+		break;
+	default:
+		break;
+	}
+	len = _len;
+	mask = (1 << _len * 2) - 1;
+	str = 0;
+}
+
+//--------------------------------------------------------------------------
+

=== added file 'kmc_api/mmer.h'
--- old/kmc_api/mmer.h	1970-01-01 00:00:00 +0000
+++ new/kmc_api/mmer.h	2021-04-26 21:01:12 +0000
@@ -0,0 +1,197 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _MMER_H
+#define _MMER_H
+#include "kmer_defs.h"
+
+// *************************************************************************
+// *************************************************************************
+
+
+class CMmer
+{
+	uint32 str;
+	uint32 mask;
+	uint32 current_val;
+	uint32* norm;
+	uint32 len;
+	static uint32 norm5[1 << 10];
+	static uint32 norm6[1 << 12];
+	static uint32 norm7[1 << 14];	
+	static uint32 norm8[1 << 16];
+	static uint32 norm9[1 << 18];
+	static uint32 norm10[1 << 20];
+	static uint32 norm11[1 << 22];
+
+	static bool is_allowed(uint32 mmer, uint32 len)
+	{
+		if ((mmer & 0x3f) == 0x3f)            // TTT suffix
+			return false;
+		if ((mmer & 0x3f) == 0x3b)            // TGT suffix
+			return false;
+		if ((mmer & 0x3c) == 0x3c)            // TG* suffix !!!! consider issue #152
+			return false;
+
+		for (uint32 j = 0; j < len - 3; ++j)
+		if ((mmer & 0xf) == 0)                // AA inside
+			return false;
+		else
+			mmer >>= 2;
+
+		if (mmer == 0)            // AAA prefix
+			return false;
+		if (mmer == 0x04)        // ACA prefix
+			return false;
+		if ((mmer & 0xf) == 0)    // *AA prefix
+			return false;
+	
+		return true;
+	}
+
+	friend class CSignatureMapper;
+	struct _si
+	{			
+		static uint32 get_rev(uint32 mmer, uint32 len)
+		{
+			uint32 rev = 0;
+			uint32 shift = len*2 - 2;
+			for(uint32 i = 0 ; i < len ; ++i)
+			{
+				rev += (3 - (mmer & 3)) << shift;
+				mmer >>= 2;
+				shift -= 2;
+			}
+			return rev;
+		}
+
+		
+
+		static void init_norm(uint32* norm, uint32 len)
+		{
+			uint32 special = 1 << len * 2;
+			for(uint32 i = 0 ; i < special ; ++i)
+			{				
+				uint32 rev = get_rev(i, len);
+				uint32 str_val = is_allowed(i, len) ? i : special;
+				uint32 rev_val = is_allowed(rev, len) ? rev : special;
+				norm[i] = MIN(str_val, rev_val);				
+			}
+		}
+
+		_si()
+		{
+			init_norm(norm5, 5);
+			init_norm(norm6, 6);
+			init_norm(norm7, 7);
+			init_norm(norm8, 8);
+			init_norm(norm9, 9);
+			init_norm(norm10, 10);
+			init_norm(norm11, 11);
+		}
+
+	}static _init;
+public:
+	CMmer(uint32 _len);
+	inline void insert(uchar symb);
+	inline uint32 get() const;
+	inline bool operator==(const CMmer& x);
+	inline bool operator<(const CMmer& x);
+	inline void clear();
+	inline bool operator<=(const CMmer& x);
+	inline void set(const CMmer& x);
+	inline void insert(const char* seq);
+	
+};
+
+
+
+//--------------------------------------------------------------------------
+inline void CMmer::insert(uchar symb)
+{
+	str <<= 2;
+	str += symb;
+	str &= mask;
+
+	current_val = norm[str];
+}
+
+//--------------------------------------------------------------------------
+inline uint32 CMmer::get() const
+{
+	return current_val;
+}
+
+//--------------------------------------------------------------------------
+inline bool CMmer::operator==(const CMmer& x)
+{
+	return current_val == x.current_val;
+}
+
+//--------------------------------------------------------------------------
+inline bool CMmer::operator<(const CMmer& x)
+{
+	return current_val < x.current_val;
+}
+
+//--------------------------------------------------------------------------
+inline void CMmer::clear()
+{
+	str = 0;
+}
+
+//--------------------------------------------------------------------------
+inline bool CMmer::operator<=(const CMmer& x)
+{
+	return current_val <= x.current_val;
+}
+
+//--------------------------------------------------------------------------
+inline void CMmer::set(const CMmer& x)
+{
+	str = x.str;
+	current_val = x.current_val;
+}
+
+//--------------------------------------------------------------------------
+inline void CMmer::insert(const char* seq)
+{
+	switch (len)
+	{
+	case 5: 
+		str = (seq[0] << 8) + (seq[1] << 6) + (seq[2] << 4) + (seq[3] << 2) + (seq[4]);
+		break;
+	case 6:
+		str = (seq[0] << 10) + (seq[1] << 8) + (seq[2] << 6) + (seq[3] << 4) + (seq[4] << 2) + (seq[5]);
+		break;
+	case 7:
+		str = (seq[0] << 12) + (seq[1] << 10) + (seq[2] << 8) + (seq[3] << 6) + (seq[4] << 4 ) + (seq[5] << 2) + (seq[6]);
+		break;
+	case 8:
+		str = (seq[0] << 14) + (seq[1] << 12) + (seq[2] << 10) + (seq[3] << 8) + (seq[4] << 6) + (seq[5] << 4) + (seq[6] << 2) + (seq[7]);
+		break;
+	case 9:
+		str = (seq[0] << 16) + (seq[1] << 14) + (seq[2] << 12) + (seq[3] << 10) + (seq[4] << 8) + (seq[5] << 6) + (seq[6] << 4) + (seq[7] << 2) + (seq[8]);
+		break;
+	case 10:
+		str = (seq[0] << 18) + (seq[1] << 16) + (seq[2] << 14) + (seq[3] << 12) + (seq[4] << 10) + (seq[5] << 8) + (seq[6] << 6) + (seq[7] << 4) + (seq[8] << 2) + (seq[9]);
+		break;
+	case 11:
+		str = (seq[0] << 20) + (seq[1] << 18) + (seq[2] << 16) + (seq[3] << 14) + (seq[4] << 12) + (seq[5] << 10) + (seq[6] << 8) + (seq[7] << 6) + (seq[8] << 4) + (seq[9] << 2) + (seq[10]);
+		break;
+	default:
+		break;
+	}
+
+	current_val = norm[str];
+}
+
+
+#endif
\ No newline at end of file

=== added file 'kmc_api/stdafx.h'
--- old/kmc_api/stdafx.h	1970-01-01 00:00:00 +0000
+++ new/kmc_api/stdafx.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,4 @@
+#include <stdio.h>
+//#include <ext/algorithm>
+#include <iostream>
+using namespace std;

=== added directory 'kmc_dump'
=== added file 'kmc_dump/kmc_dump.cpp'
--- old/kmc_dump/kmc_dump.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_dump/kmc_dump.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,174 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+  This file demonstrates the example usage of kmc_api software. 
+  It reads kmer_counter's output and prints kmers to an output file.
+
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include <iostream>
+#include "../kmc_api/kmc_file.h"
+#include "nc_utils.h"
+
+
+void print_info(void);
+
+
+//----------------------------------------------------------------------------------
+// Check if --help or --version was used
+bool help_or_version(int argc, char** argv)
+{
+	const std::string version = "--version";
+	const std::string help = "--help";
+	for (int i = 1; i < argc; ++i)
+	{
+		if (argv[i] == version || argv[i] == help)
+			return true;
+	}
+	return false;
+}
+
+int _tmain(int argc, char* argv[])
+{
+	if (argc == 1 || help_or_version(argc, argv))
+	{
+		print_info();
+		return 0;
+	}
+
+	CKMCFile kmer_data_base;
+	int32 i;
+	uint32 min_count_to_set = 0;
+	uint32 max_count_to_set = 0;
+	std::string input_file_name;
+	std::string output_file_name;
+
+	FILE * out_file;
+	//------------------------------------------------------------
+	// Parse input parameters
+	//------------------------------------------------------------
+	if(argc < 3)
+	{
+		print_info();
+		return EXIT_FAILURE;
+	}
+
+	for(i = 1; i < argc; ++i)
+	{
+		if(argv[i][0] == '-')
+		{	
+			if(strncmp(argv[i], "-ci", 3) == 0)
+				min_count_to_set = atoi(&argv[i][3]);
+			else if(strncmp(argv[i], "-cx", 3) == 0)
+					max_count_to_set = atoi(&argv[i][3]);
+		}
+		else
+			break;
+	}
+
+	if(argc - i < 2)
+	{ 
+		print_info();
+		return EXIT_FAILURE;
+	}
+
+	input_file_name = std::string(argv[i++]);
+	output_file_name = std::string(argv[i]);
+
+	if((out_file = fopen (output_file_name.c_str(),"wb")) == NULL)
+	{
+		print_info();
+		return EXIT_FAILURE;
+	}
+
+	setvbuf(out_file, NULL ,_IOFBF, 1 << 24);
+
+	//------------------------------------------------------------------------------
+	// Open kmer database for listing and print kmers within min_count and max_count
+	//------------------------------------------------------------------------------
+
+	if (!kmer_data_base.OpenForListing(input_file_name))
+	{
+		print_info();
+		return EXIT_FAILURE ;
+	}
+	else
+	{
+		uint32 _kmer_length;
+		uint32 _mode;
+		uint32 _counter_size;
+		uint32 _lut_prefix_length;
+		uint32 _signature_len;
+		uint32 _min_count;
+		uint64 _max_count;
+		uint64 _total_kmers;
+
+		kmer_data_base.Info(_kmer_length, _mode, _counter_size, _lut_prefix_length, _signature_len, _min_count, _max_count, _total_kmers);
+
+		
+		//std::string str;
+		char str[1024];
+		uint32 counter_len;
+		
+		CKmerAPI kmer_object(_kmer_length);
+		
+		if(min_count_to_set)
+		if (!(kmer_data_base.SetMinCount(min_count_to_set)))
+				return EXIT_FAILURE;
+		if(max_count_to_set)
+		if (!(kmer_data_base.SetMaxCount(max_count_to_set)))
+				return EXIT_FAILURE;	
+
+		if (_mode) //quake compatible mode
+		{
+			float counter;
+			while (kmer_data_base.ReadNextKmer(kmer_object, counter))
+			{
+				kmer_object.to_string(str);
+				str[_kmer_length] = '\t';				
+				counter_len = CNumericConversions::Double2PChar(counter, 6, (uchar*)str + _kmer_length + 1);				
+				str[_kmer_length + 1 + counter_len] = '\n';
+				fwrite(str, 1, _kmer_length + counter_len + 2, out_file);			
+			}
+		}
+		else
+		{
+			uint64 counter;
+			while (kmer_data_base.ReadNextKmer(kmer_object, counter))
+			{
+				kmer_object.to_string(str);
+				str[_kmer_length] = '\t';
+				counter_len = CNumericConversions::Int2PChar(counter, (uchar*)str + _kmer_length + 1);
+				str[_kmer_length + 1 + counter_len] = '\n';
+				fwrite(str, 1, _kmer_length + counter_len + 2, out_file);
+			}
+		}
+		
+	
+		fclose(out_file);
+		kmer_data_base.Close();
+	}
+
+	return EXIT_SUCCESS; 
+}
+// -------------------------------------------------------------------------
+// Print execution options 
+// -------------------------------------------------------------------------
+void print_info(void)
+{
+	std::cout << "KMC dump ver. " << KMC_VER << " (" << KMC_DATE << ")\n"
+			  << "\nUsage:\nkmc_dump [options] <kmc_database> <output_file>\n"
+			  << "Parameters:\n"
+			  << "<kmc_database> - kmer_counter's output\n"
+			  << "Options:\n"
+			  << "-ci<value> - exclude k-mers occurring less than <value> times\n"
+			  << "-cx<value> - exclude k-mers occurring more of than <value> times\n";
+}
+
+// ***** EOF

=== added file 'kmc_dump/kmc_dump.vcxproj'
--- old/kmc_dump/kmc_dump.vcxproj	1970-01-01 00:00:00 +0000
+++ new/kmc_dump/kmc_dump.vcxproj	2020-12-10 18:04:42 +0000
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{8939AD12-23D5-469C-806B-DC3F98F8A514}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>kmc_dump</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>NotSet</CharacterSet>
+    <UseOfMfc>Static</UseOfMfc>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>NotSet</CharacterSet>
+    <UseOfMfc>Static</UseOfMfc>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <Optimization>Full</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <Optimization>Full</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\kmc_api\kmc_file.h" />
+    <ClInclude Include="..\kmc_api\kmer_api.h" />
+    <ClInclude Include="..\kmc_api\kmer_defs.h" />
+    <ClInclude Include="..\kmc_api\mmer.h" />
+    <ClInclude Include="nc_utils.h" />
+    <ClInclude Include="stdafx.h" />
+    <ClInclude Include="targetver.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\kmc_api\kmc_file.cpp" />
+    <ClCompile Include="..\kmc_api\kmer_api.cpp" />
+    <ClCompile Include="..\kmc_api\mmer.cpp" />
+    <ClCompile Include="kmc_dump.cpp" />
+    <ClCompile Include="nc_utils.cpp" />
+    <ClCompile Include="stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+    </ClCompile>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file

=== added file 'kmc_dump/nc_utils.cpp'
--- old/kmc_dump/nc_utils.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_dump/nc_utils.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,20 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+  This file demonstrates the example usage of kmc_api software. 
+  It reads kmer_counter's output and prints kmers to an output file.
+
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "nc_utils.h"
+
+
+uchar CNumericConversions::digits[100000*5];
+int CNumericConversions::powOf10[30];
+CNumericConversions::_si CNumericConversions::_init;
\ No newline at end of file

=== added file 'kmc_dump/nc_utils.h'
--- old/kmc_dump/nc_utils.h	1970-01-01 00:00:00 +0000
+++ new/kmc_dump/nc_utils.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,139 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+  This file demonstrates the example usage of kmc_api software. 
+  It reads kmer_counter's output and prints kmers to an output file.
+
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include <string>
+#include <cmath>
+#include "../kmc_api/kmer_defs.h"
+
+#ifndef _NC_UTILS_H
+#define _NC_UTILS_H
+class CNumericConversions {
+public:
+    static uchar digits[100000*5];
+	static int powOf10[30];
+    struct _si {
+        _si()
+        {
+            for(int i = 0; i < 100000; ++i)
+            {
+                int dig = i;
+
+                digits[i*5+4] = '0' + (dig % 10);
+                dig /= 10;
+                digits[i*5+3] = '0' + (dig % 10);
+                dig /= 10;
+                digits[i*5+2] = '0' + (dig % 10);
+                dig /= 10;
+                digits[i*5+1] = '0' + (dig % 10);
+                dig /= 10;
+                digits[i*5+0] = '0' + dig;
+            }
+			powOf10[0] = 1;
+			for(int i = 1 ; i < 30 ; ++i)
+			{
+				powOf10[i] = powOf10[i-1]*10;
+			}
+        }
+    } static _init;
+
+    static int NDigits(uint64 val)
+    {
+        if(val >= 10000)
+            return 5;
+        else if(val >= 1000)
+            return 4;
+        else if(val >= 100)
+            return 3;
+        else if(val >= 10)
+            return 2;
+        else
+            return 1;
+    }
+
+    static int Int2PChar(uint64 val, uchar *str)
+    {
+        if(val >= 1000000000000000ull)
+        {
+            uint64 dig1 = val / 1000000000000000ull;
+            val -= dig1 * 1000000000000000ull;
+            uint64 dig2 = val / 10000000000ull;
+            val -= dig2 * 10000000000ull;
+            uint64 dig3 = val / 100000ull;
+            uint64 dig4 = val - dig3 * 100000ull;
+
+            int ndig = NDigits(dig1);
+
+            memcpy(str, digits+dig1*5+(5-ndig), ndig);
+            memcpy(str+ndig, digits+dig2*5, 5);
+            memcpy(str+ndig+5, digits+dig3*5, 5);
+            memcpy(str+ndig+10, digits+dig4*5, 5);
+
+            return ndig+15;
+        }
+        else if(val >= 10000000000ull)
+        {
+            uint64 dig1 = val / 10000000000ull;
+            val -= dig1 * 10000000000ull;
+            uint64 dig2 = val / 100000ull;
+            uint64 dig3 = val - dig2 * 100000ull;
+
+            int ndig = NDigits(dig1);
+
+            memcpy(str, digits+dig1*5+(5-ndig), ndig);
+            memcpy(str+ndig, digits+dig2*5, 5);
+            memcpy(str+ndig+5, digits+dig3*5, 5);
+
+            return ndig+10;
+        }
+        else if(val >= 100000ull)
+        {
+            uint64 dig1 = val / 100000ull;
+            uint64 dig2 = val - dig1 * 100000ull;
+
+            int ndig = NDigits(dig1);
+
+            memcpy(str, digits+dig1*5+(5-ndig), ndig);
+            memcpy(str+ndig, digits+dig2*5, 5);
+
+            return ndig+5;
+        }
+        else
+        {
+            int ndig = NDigits(val);
+
+            memcpy(str, digits+val*5+(5-ndig), ndig);
+
+            return ndig;
+        }
+	}
+
+	static int Double2PChar(double val, int prec, uchar *str)
+	{
+		double corrector = .5 / powOf10[prec];
+		val += corrector;
+		double ipart;
+		double fractPart = std::modf(val, &ipart);
+		uint32 intPart = (uint32)ipart;
+		uint32 len = Int2PChar(intPart, str);
+		uint32 pos = len;
+		str[pos++] = '.';
+		for(int i = 0 ; i < prec ; ++i)
+		{
+			fractPart *= 10;
+			str[pos++] = '0' + (uint32)fractPart  % 10 ;
+		}
+		return len + prec + 1;
+	}
+};
+
+#endif

=== added file 'kmc_dump/stdafx.cpp'
--- old/kmc_dump/stdafx.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_dump/stdafx.cpp	2015-08-23 15:37:05 +0000
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// kmc_dump.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file

=== added file 'kmc_dump/stdafx.h'
--- old/kmc_dump/stdafx.h	1970-01-01 00:00:00 +0000
+++ new/kmc_dump/stdafx.h	2015-08-23 15:37:05 +0000
@@ -0,0 +1,17 @@
+#ifdef WIN32
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#include <stdio.h>
+#include <tchar.h>
+
+
+
+// TODO: reference additional headers your program requires here
+#endif
\ No newline at end of file

=== added file 'kmc_dump/targetver.h'
--- old/kmc_dump/targetver.h	1970-01-01 00:00:00 +0000
+++ new/kmc_dump/targetver.h	2015-08-23 15:37:05 +0000
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>

=== added directory 'kmc_dump_sample'
=== added file 'kmc_dump_sample/kmc_dump_sample.cpp'
--- old/kmc_dump_sample/kmc_dump_sample.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_dump_sample/kmc_dump_sample.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,162 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+  This file demonstrates the example usage of kmc_api software. 
+  It reads kmer_counter's output and prints kmers to an output file.
+
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include <iostream>
+#include "../kmc_api/kmc_file.h"
+
+void print_info(void);
+
+//----------------------------------------------------------------------------------
+// Check if --help or --version was used
+bool help_or_version(int argc, char** argv)
+{
+	const std::string version = "--version";
+	const std::string help = "--help";
+	for (int i = 1; i < argc; ++i)
+	{
+		if (argv[i] == version || argv[i] == help)
+			return true;
+	}
+	return false;
+}
+
+int _tmain(int argc, char* argv[])
+{
+	if (argc == 1 || help_or_version(argc, argv))
+	{
+		print_info();
+		return 0;
+	}
+
+	CKMCFile kmer_data_base;
+	int32 i;
+	uint32 min_count_to_set = 0;
+	uint32 max_count_to_set = 0;
+	std::string input_file_name;
+	std::string output_file_name;
+
+	FILE * out_file;
+	//------------------------------------------------------------
+	// Parse input parameters
+	//------------------------------------------------------------
+	if(argc < 3)
+	{
+		print_info();
+		return EXIT_FAILURE;
+	}
+
+	for(i = 1; i < argc; ++i)
+	{
+		if(argv[i][0] == '-')
+		{	
+			if(strncmp(argv[i], "-ci", 3) == 0)
+				min_count_to_set = atoi(&argv[i][3]);
+			else if(strncmp(argv[i], "-cx", 3) == 0)
+					max_count_to_set = atoi(&argv[i][3]);
+		}
+		else
+			break;
+	}
+
+	if(argc - i < 2)
+	{ 
+		print_info();
+		return EXIT_FAILURE;
+	}
+
+	input_file_name = std::string(argv[i++]);
+	output_file_name = std::string(argv[i]);
+
+	if((out_file = fopen (output_file_name.c_str(),"wb")) == NULL)
+	{
+		print_info();
+		return EXIT_FAILURE;
+	}
+
+	setvbuf(out_file, NULL ,_IOFBF, 1 << 24);
+
+	//------------------------------------------------------------------------------
+	// Open kmer database for listing and print kmers within min_count and max_count
+	//------------------------------------------------------------------------------
+
+	if (!kmer_data_base.OpenForListing(input_file_name))
+	{
+		print_info();
+		return EXIT_FAILURE ;
+	}
+	else
+	{
+		uint32 _kmer_length;
+		uint32 _mode;
+		uint32 _counter_size;
+		uint32 _lut_prefix_length;
+		uint32 _signature_len;
+		uint32 _min_count;
+		uint64 _max_count;
+		uint64 _total_kmers;
+
+		kmer_data_base.Info(_kmer_length, _mode, _counter_size, _lut_prefix_length, _signature_len, _min_count, _max_count, _total_kmers);
+
+		CKmerAPI kmer_object(_kmer_length);
+		
+		if(min_count_to_set)
+		if (!(kmer_data_base.SetMinCount(min_count_to_set)))
+				return EXIT_FAILURE;
+		if(max_count_to_set)
+		if (!(kmer_data_base.SetMaxCount(max_count_to_set)))
+				return EXIT_FAILURE;	
+
+		
+		std::string str;
+		if (_mode) //quake compatible mode
+		{
+			float counter;			
+			while (kmer_data_base.ReadNextKmer(kmer_object, counter))
+			{
+				kmer_object.to_string(str);	
+				fprintf(out_file, "%s\t%f\n", str.c_str(), counter);			
+			}
+		}
+		else 
+		{
+			uint32 counter;			
+			while (kmer_data_base.ReadNextKmer(kmer_object, counter))
+			{
+				kmer_object.to_string(str);
+				fprintf(out_file, "%s\t%u\n", str.c_str(), counter);
+			}
+		}
+		
+	
+		fclose(out_file);
+		kmer_data_base.Close();
+	}
+
+	return EXIT_SUCCESS; 
+}
+// -------------------------------------------------------------------------
+// Print execution options 
+// -------------------------------------------------------------------------
+void print_info(void)
+{
+	std::cout << "KMC dump ver. " << KMC_VER << " (" << KMC_DATE << ")\n"
+			  << "\nUsage:\nkmc_dump [options] <kmc_database> <output_file>\n"
+			  << "Parameters:\n"
+			  << "<kmc_database> - kmer_counter's output\n"
+			  << "Options:\n"
+			  << "-ci<value> - exclude k-mers occurring less than <value> times\n"
+			  << "-cx<value> - exclude k-mers occurring more of than <value> times\n";
+};
+
+// ***** EOF

=== added file 'kmc_dump_sample/kmc_dump_sample.vcxproj'
--- old/kmc_dump_sample/kmc_dump_sample.vcxproj	1970-01-01 00:00:00 +0000
+++ new/kmc_dump_sample/kmc_dump_sample.vcxproj	2020-12-10 18:04:42 +0000
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{17823F37-86DE-4E58-B354-B84DA9EDA6A1}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>kmc_dump_sample</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>NotSet</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>NotSet</CharacterSet>
+    <UseOfMfc>Static</UseOfMfc>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>NotSet</CharacterSet>
+    <UseOfMfc>Static</UseOfMfc>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <Optimization>Full</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <Optimization>Full</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\kmc_api\kmc_file.h" />
+    <ClInclude Include="..\kmc_api\kmer_api.h" />
+    <ClInclude Include="..\kmc_api\kmer_defs.h" />
+    <ClInclude Include="..\kmc_api\mmer.h" />
+    <ClInclude Include="stdafx.h" />
+    <ClInclude Include="targetver.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\kmc_api\kmc_file.cpp" />
+    <ClCompile Include="..\kmc_api\kmer_api.cpp" />
+    <ClCompile Include="..\kmc_api\mmer.cpp" />
+    <ClCompile Include="kmc_dump_sample.cpp" />
+    <ClCompile Include="stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+    </ClCompile>
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file

=== added file 'kmc_dump_sample/stdafx.cpp'
--- old/kmc_dump_sample/stdafx.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_dump_sample/stdafx.cpp	2015-08-23 15:37:05 +0000
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// kmc_dump_sample.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file

=== added file 'kmc_dump_sample/stdafx.h'
--- old/kmc_dump_sample/stdafx.h	1970-01-01 00:00:00 +0000
+++ new/kmc_dump_sample/stdafx.h	2015-08-23 15:37:05 +0000
@@ -0,0 +1,17 @@
+#ifdef WIN32
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#include <stdio.h>
+#include <tchar.h>
+
+
+
+// TODO: reference additional headers your program requires here
+#endif
\ No newline at end of file

=== added file 'kmc_dump_sample/targetver.h'
--- old/kmc_dump_sample/targetver.h	1970-01-01 00:00:00 +0000
+++ new/kmc_dump_sample/targetver.h	2015-08-23 15:37:05 +0000
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>

=== added directory 'kmc_tools'
=== added file 'kmc_tools/bundle.h'
--- old/kmc_tools/bundle.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/bundle.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,336 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _BUNDLE_H
+#define _BUNDLE_H
+#include "config.h"
+#include "defs.h"
+#include "kmer.h"
+
+//************************************************************************************************************
+// CBundle and CInput are CORE classes of this application. CInputs are nodes of binary tree which 
+// represent operations. Leafs of this tree are kmc database (1 or 2) inputs (sets of k-mers).
+// Each node represents an operation like intersection, subtraction, etc. Because this class is abstract 
+// calling virtual method to get each single k-mer may be costly. To prevent high const, between tree nodes there
+//are instances of CBundle which contains buffer of k-mers and its counters.
+//
+// The algorithm works as follow (conceptually):
+// Build a tree with CBundles and CInputs, as a root take a some output writer (kmc database). 
+// Root has its bundle and get from it k-mers, but at the beginning there is nothing in bundle. Each bundle 
+// contains pointer to CInput below in tree. The CBundle is getting k-mers from its CInput. 
+// This is repeated from top of tree to leafs
+//************************************************************************************************************
+
+//Forward declaration
+template<unsigned SIZE> class CBundle;
+
+//************************************************************************************************************
+// CInput - Base abstract class representing data source for CBundle class
+//************************************************************************************************************
+template<unsigned SIZE> class CInput
+{
+public:
+	virtual void NextBundle(CBundle<SIZE>& bundle) = 0;
+	virtual void IgnoreRest() = 0;
+	bool Finished(){ return finished; }
+	virtual ~CInput(){}
+protected:
+	bool finished = false;
+
+};
+
+template<unsigned SIZE> class CSimpleOperation; 
+template<unsigned SIZE> class CKMC1DbReader; 
+template<unsigned SIZE> class CMergerParent;
+template<unsigned SIZE> class CMergerParentSubthread;
+
+//************************************************************************************************************
+// CBundleData - class containing a buffer of k-mers and its counters. 
+//************************************************************************************************************
+template<unsigned SIZE> class CBundleData
+{
+	struct CKmerWithCounter
+	{
+		CKmer<SIZE> kmer;
+		uint32 counter;
+	};
+
+public:
+	CBundleData(uint32 size) : insert_pos(0), get_pos(0), size(size)
+	{
+		kmers_with_counters = new CKmerWithCounter[size];
+	}
+	CBundleData() : insert_pos(0), get_pos(0), size(BUNDLE_CAPACITY)
+	{
+		kmers_with_counters = new CKmerWithCounter[size];
+	}
+	~CBundleData()
+	{
+		delete[] kmers_with_counters;
+	}
+	CBundleData(CBundleData<SIZE>&& rhs) :
+		insert_pos(rhs.insert_pos), get_pos(rhs.get_pos), size(rhs.size), kmers_with_counters(rhs.kmers_with_counters)
+	{
+		rhs.kmers_with_counters = nullptr;
+		rhs.get_pos = rhs.size = rhs.insert_pos = 0;
+	}
+
+	CBundleData<SIZE>& operator=(CBundleData<SIZE>&& rhs)
+	{
+		if (this != &rhs)
+		{
+			delete[] kmers_with_counters;
+
+			kmers_with_counters = rhs.kmers_with_counters;
+			get_pos = rhs.get_pos;
+			size = rhs.size;
+			insert_pos = rhs.insert_pos;
+
+			rhs.kmers_with_counters = nullptr;
+			rhs.get_pos = rhs.size = rhs.insert_pos = 0;
+		}
+		return *this;
+	}
+
+	//deprecated
+	//void CopyFrom(CBundleData<SIZE>& rhs) //similar to assign operator but I want assign operator deleted, this method should be used carefully. this->size and rhs.size must quals
+	//{
+	//	memcpy(kmers_with_counters, rhs.kmers_with_counters, rhs.insert_pos * sizeof(CKmerWithCounter));
+	//	insert_pos = rhs.insert_pos;
+	//	get_pos = 0;
+	//}
+
+
+	CBundleData(const CBundleData<SIZE>&) = delete;
+	CBundle<SIZE>& operator=(const CBundleData<SIZE>&) = delete;
+
+	CKmer<SIZE>& TopKmer() const
+	{
+		return kmers_with_counters[get_pos].kmer;
+	}
+
+	uint32& TopCounter() const
+	{
+		return kmers_with_counters[get_pos].counter;
+	}
+
+	bool Full()
+	{
+		return insert_pos >= size;
+	}
+
+	bool Empty()
+	{
+		return get_pos >= insert_pos;
+	}
+
+	void Insert(CKmer<SIZE>& kmer, uint32 counter)
+	{
+		kmers_with_counters[insert_pos].kmer = kmer;
+		kmers_with_counters[insert_pos++].counter = counter;
+	}
+	void Pop()
+	{
+		++get_pos;
+	}
+
+	void Clear()
+	{
+		insert_pos = get_pos = 0;
+	}
+
+	uint32 NRecLeft()
+	{
+		return insert_pos - get_pos;
+	}
+
+
+private:
+	friend class CKMC1DbReader<SIZE>; //improve performance, but CKMC1DbReader takes responsibility for CBundleData state!
+	friend class CSimpleOperation<SIZE>; //as above
+	friend class CMergerParent<SIZE>;
+	friend class CMergerParentSubthread<SIZE>;
+	friend class CBundle<SIZE>;
+	uint32 insert_pos, get_pos, size;
+	CKmerWithCounter* kmers_with_counters;
+};
+
+
+//************************************************************************************************************
+// CBundle - connector between CBundleData and CInput
+//************************************************************************************************************
+template<unsigned SIZE> class CBundle
+{
+public:
+	CBundle(CInput<SIZE>* input) : input(input)
+	{
+		
+	}
+
+	//deprecated
+	//void CopyFrom(CBundle<SIZE>& rhs) //Use carefully. Look at comment in called function
+	//{
+	//	data.CopyFrom(rhs.data);
+	//}
+
+	CKmer<SIZE>& TopKmer() const
+	{
+		return data.TopKmer();
+	}
+
+	uint32& TopCounter() const
+	{
+		return data.TopCounter();
+	}
+
+
+	bool Full()
+	{		
+		return data.Full();
+	}
+	void Insert(CKmer<SIZE>& kmer, uint32 counter)
+	{		
+		data.Insert(kmer, counter);
+	}
+	void Pop()
+	{		
+		data.Pop();
+	}
+	~CBundle()
+	{
+		delete input;
+	}
+
+	bool Empty()
+	{
+		return data.Empty();
+	}
+	
+	CBundleData<SIZE>& Data() {
+		return data;
+	}
+
+	inline bool Finished();
+
+	void IgnoreRest()
+	{		
+		input->IgnoreRest();
+	}
+	uint32 Size()
+	{
+		return data.insert_pos;
+	}
+
+	uint32 NRecLeft()
+	{
+		return data.insert_pos - data.get_pos;
+	}
+	
+protected:
+	friend class CSimpleOperation<SIZE>; //improve performance
+	CBundleData<SIZE> data;
+	CInput<SIZE>* input;
+	bool finished = false;
+};
+
+//forward declaration
+template <unsigned SIZE> class CKMC1DbWriter;
+template<unsigned SIZE>
+class COutputBundle : public CBundle<SIZE>
+{
+private:
+	CSimpleOutputDesc::OpType op_type;
+	CounterOpType counter_op;
+	CKMC1DbWriter<SIZE>& db_writer;
+	
+public:
+
+	uint32 GetCounter(uint32 counter1, uint32 counter2)
+	{
+		switch (counter_op)
+		{
+		case CounterOpType::DIFF:
+			return counter1 > counter2 ? counter1 - counter2 : 0;
+		case CounterOpType::MAX:
+			return MAX(counter1, counter2);
+		case CounterOpType::MIN:
+			return MIN(counter1, counter2);
+		case CounterOpType::SUM:
+			return counter1 + counter2;
+		case CounterOpType::FROM_DB1:
+			return counter1;
+		case CounterOpType::FROM_DB2:
+			return counter2;
+		case CounterOpType::NONE://should never be here
+			std::cerr << "Error: trying to use undefined counter calculation mode!\n";
+			exit(1);
+		}
+		return 0;
+	}
+	
+	CSimpleOutputDesc::OpType GetOpType()
+	{
+		return op_type;
+	}
+
+
+	COutputBundle(CSimpleOutputDesc::OpType op_type, CounterOpType counter_op, CKMC1DbWriter<SIZE>& db_writer) :
+		CBundle<SIZE>(nullptr),
+		op_type(op_type),
+		counter_op(counter_op),
+		db_writer(db_writer)
+	{
+
+	}
+
+	void InsertAndSendIfFull(CKmer<SIZE>& kmer, uint32 counter)
+	{
+		this->data.Insert(kmer, counter);
+		if (this->Full())
+		{			
+			db_writer.MultiOptputAddResultPart(*this);
+		}
+	}
+
+	void NotifyFinish()
+	{
+		if (!this->Empty())
+			db_writer.MultiOptputAddResultPart(*this);
+	}
+};
+
+
+//************************************************************************************************************
+template<unsigned SIZE> inline bool CBundle<SIZE>::Finished()
+{	
+	if (finished)
+		return true;
+	if (data.get_pos >= data.insert_pos)
+	{
+		if (input->Finished())
+		{
+			finished = true;
+			return true;
+		}
+		data.get_pos = data.insert_pos = 0;
+		input->NextBundle(*this); 
+		if (data.insert_pos == 0)//Because maybe NextBundle did not add anything, which means there is nothing to take
+		{
+			finished = true;
+			return true;
+		}
+	}
+	return false;
+}
+
+#endif
+
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/check_kmer.h'
--- old/kmc_tools/check_kmer.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/check_kmer.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,243 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _CHECK_KMER_H
+#define _CHECK_KMER_H
+
+#include "config.h"
+#include "kmer.h"
+#include "../kmc_api/mmer.h"
+#include <string>
+
+template<unsigned SIZE>
+class CKmerCheck
+{
+	CConfig& config;
+	const CKMC_header& header;
+	const CInputDesc& input_desc;
+	CKmer<SIZE> mask;
+	uint64 max_prefix;
+	uint32 record_size;
+	FILE* prefix_file = nullptr, *suffix_file = nullptr;
+	
+	std::unique_ptr<uchar[]> rec;
+
+	uint32 check_in_suffix_file(uint64 lower, uint64 upper, CKmer<SIZE>& kmer_suffix)
+	{
+		
+		if (!upper)
+		{			
+			return 0;
+		}
+		upper--;
+
+		if (upper < lower)	
+			return 0;
+		
+		
+
+		
+
+		uint32 cutoff_range = input_desc.cutoff_max - input_desc.cutoff_min;
+
+		auto read_at = [](FILE* file, uint64 pos, uchar* &tmp, uint32 record_size, uint32 counter_size) -> CKmer<SIZE>
+		{
+			my_fseek(file, pos, SEEK_SET);
+			fread(tmp, 1, record_size, file);
+			CKmer<SIZE> res;
+			res.load(tmp, record_size - counter_size);
+			return res;
+		};
+
+		while (upper >= lower)
+		{
+			uint64 middle = (upper + lower) / 2;
+			uchar* tmp = rec.get();
+			auto middle_suffix = read_at(suffix_file, 4 + record_size * middle, tmp, record_size, header.counter_size);
+			if (middle_suffix < kmer_suffix)
+			{
+				lower = middle + 1;
+			}
+			else if (kmer_suffix < middle_suffix)
+			{
+				upper = middle - 1;
+			}
+			else
+			{
+				uint32 counter = 0;
+				for (uint32 i = 0; i < header.counter_size; ++i)
+				{
+					counter += (((uint32)*tmp++) << (i << 3));
+				}
+				if (counter - input_desc.cutoff_min < cutoff_range)
+				{					
+					return counter;
+				}
+				break;
+			}
+		}
+		
+		return 0;
+	}
+
+	void get_lower_upper(uint64 prefix, CKmer<SIZE>& kmer, uint64& lower, uint64& upper)
+	{
+		if (!header.IsKMC2())
+		{
+			uint64 pos = 4 + sizeof(uint64)*prefix;
+			my_fseek(prefix_file, pos, SEEK_SET);
+
+			fread(&lower, sizeof(uint64), 1, prefix_file);
+			if (prefix == max_prefix)
+				upper = header.total_kmers;
+			else
+				fread(&upper, sizeof(uint64), 1, prefix_file);
+		}
+		else
+		{
+			uint32 sig_len = header.signature_len;
+			CMmer cur_mmr(sig_len);
+
+
+			uint32 pos = header.kmer_len * 2 - 2;
+			for (uint32 i = 0; i < sig_len; ++i)
+			{
+				cur_mmr.insert(kmer.get_2bits(pos));
+				pos -= 2;
+			}
+			CMmer min_mmr(cur_mmr);
+			for (uint32 i = sig_len; i < header.kmer_len; ++i)
+			{
+				cur_mmr.insert(kmer.get_2bits(pos));
+				pos -= 2;
+
+				if (cur_mmr < min_mmr)
+					min_mmr = cur_mmr;
+			}
+			uint32 signature = min_mmr.get();
+
+			uint64 map_size = ((1 << 2 * header.signature_len) + 1) * sizeof(uint32);
+			my_fseek(prefix_file, 0ULL - (8 + header.header_offset + map_size) + signature * sizeof(uint32), SEEK_END);
+
+			uint32 prefix_array = 0;
+			uint32 prefix_arry_size = (1 << 2 * header.lut_prefix_len)*(uint32)sizeof(uint64);
+			fread(&prefix_array, sizeof(uint32), 1, prefix_file);
+
+			uint64 prefix_pos = 4 + prefix_arry_size*prefix_array + sizeof(uint64)*prefix;
+			my_fseek(prefix_file, prefix_pos, SEEK_SET);
+
+			fread(&lower, sizeof(uint64), 1, prefix_file);
+			fread(&upper, sizeof(uint64), 1, prefix_file);
+		}
+	}
+
+public:
+	CKmerCheck(const CKMC_header& header, const CInputDesc& input_desc) :
+		config(CConfig::GetInstance()),
+		header(header),
+		input_desc(input_desc)
+	{
+		mask.set_n_1((header.kmer_len - header.lut_prefix_len) * 2);
+
+		std::string file_src = input_desc.file_src;
+		prefix_file = fopen((file_src + ".kmc_pre").c_str(), "rb");
+		if (!prefix_file)
+		{
+			std::cerr << "Error: cannot open file : " << (file_src + ".kmc_pre") << "\n";
+			exit(1);
+		}
+		file_src = input_desc.file_src;
+		suffix_file = fopen((file_src + ".kmc_suf").c_str(), "rb");
+		if (!suffix_file)
+		{
+			std::cerr << "Error: cannot open file : " << (file_src + ".kmc_suf") << "\n";
+			exit(1);
+		}
+
+		setvbuf(prefix_file, NULL, _IOFBF, (1ULL << 26));
+		setvbuf(suffix_file, NULL, _IOFBF, (1ULL << 26));
+
+		max_prefix = (1 << (2 * header.lut_prefix_len)) - 1;
+		record_size = (header.kmer_len - header.lut_prefix_len) / 4 + header.counter_size;
+		rec = std::make_unique<uchar[]>(record_size);
+	}
+
+	~CKmerCheck()
+	{
+		fclose(prefix_file);
+		fclose(suffix_file);
+	}
+
+	
+	uint32 CheckKmer(CKmer<SIZE> kmer)
+	{
+		uint64 prefix = kmer.remove_suffix((header.kmer_len - header.lut_prefix_len) * 2);
+		
+		uint64 lower = 0, upper = 0;		
+		get_lower_upper(prefix, kmer, lower, upper);
+
+		kmer.mask(mask);
+		return check_in_suffix_file(lower, upper, kmer);
+	}
+	
+	bool CheckKmer()
+	{
+		const std::string& kmer = config.check_params.kmer;
+		if (kmer.length() != header.kmer_len)
+		{
+			std::cerr << "Error: invalid k-mer length\n";
+			exit(1);
+		}
+		char codes[255];
+		for (uint32 i = 0; i < 255; ++i)
+			codes[i] = -1;
+		codes['A'] = codes['a'] = 0;
+		codes['C'] = codes['c'] = 1;
+		codes['G'] = codes['g'] = 2;
+		codes['T'] = codes['t'] = 3;
+
+		
+		CKmer<SIZE> _kmer;
+		uint64 prefix = 0;
+		_kmer.clear();
+
+		for (uint32 i = 0; i < header.lut_prefix_len; ++i)
+		{
+			char d = codes[(uchar)kmer[i]];
+			if (d < 0)
+			{
+				std::cerr << "Error: invalid k-mer format\n";
+				exit(1);
+			}
+			prefix <<= 2;
+			prefix += d;
+			_kmer.SHL_insert_2bits(d);
+		}
+		for (uint32 i = header.lut_prefix_len; i<header.kmer_len; ++i)
+		{
+			char d = codes[(uchar)kmer[i]];
+			if (d < 0)
+			{
+				std::cerr << "Error: invalid k-mer format\n";
+				exit(1);
+			}
+			_kmer.SHL_insert_2bits(d);
+		}
+		
+		uint64 lower = 0, upper = 0;
+		get_lower_upper(prefix, _kmer, lower, upper);
+		_kmer.mask(mask);
+		uint32 counter = check_in_suffix_file(lower, upper, _kmer);
+		std::cout << counter << "\n";
+		return true;
+	}
+};
+
+#endif
\ No newline at end of file

=== added file 'kmc_tools/config.h'
--- old/kmc_tools/config.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/config.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,491 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+#include "defs.h"
+#include <string>
+#include <vector>
+#include <memory>
+#include <cstring>
+#include "kmc_header.h"
+#include "percent_progress.h"
+
+enum class CounterOpType { MIN, MAX, SUM, DIFF, FROM_DB1, FROM_DB2, NONE };
+
+class CCounterBuilder
+{
+public:
+	static void build_counter(uint32& counter, uchar *&buffer, uint32 counter_bytes, uint32 counter_mask, bool little_endian)
+	{
+		memcpy(&counter, buffer, sizeof(counter));
+		if (!little_endian)
+			counter = _bswap_uint32(counter);
+		counter &= counter_mask;
+		buffer += counter_bytes;
+	}
+};
+
+struct CDescBase
+{
+	std::string file_src;
+	uint32 cutoff_min = 0; //0 means it is not set yet
+	uint32 cutoff_max = 0; //0 means it is not set yet
+	CDescBase(const std::string& file_src) :
+		file_src(file_src)
+	{
+	}
+	CDescBase() = default;
+};
+
+//************************************************************************************************************
+// CInputDesc - description of a single input KMC database.
+//************************************************************************************************************
+struct CInputDesc : public CDescBase
+{	
+	uint32 threads = 0; //for kmc2 input
+	CInputDesc(const std::string& file_src) :
+		CDescBase(file_src)
+	{
+
+	}	
+	CInputDesc() = default;
+};
+
+//************************************************************************************************************
+// COutputDesc - description of a output KMC database.
+//************************************************************************************************************
+struct COutputDesc : public CDescBase
+{
+	uint32 counter_max = 0; //0 means it is not set yet
+	uint64 counter_value = 0; //only for SET_COUNTER operation, 0 means not set yet
+	COutputDesc(const std::string& file_src) :
+		CDescBase(file_src)		
+	{
+
+	}
+
+	COutputDesc() = default;
+};
+
+struct CSimpleOutputDesc : public COutputDesc
+{
+	enum class OpType { INTERSECT, UNION, KMERS_SUBTRACTION, COUNTERS_SUBTRACTION, REVERSE_KMERS_SUBTRACTION, REVERSE_COUNTERS_SUBTRACTION }; 	
+	OpType op_type;
+	CounterOpType counter_op;
+	CSimpleOutputDesc(OpType op_type) :
+		op_type(op_type)		
+	{		
+		switch (op_type)
+		{
+		case OpType::INTERSECT:
+			counter_op = CounterOpType::MIN; 
+			break;
+		case OpType::UNION:
+			counter_op = CounterOpType::SUM;
+			break;
+		case OpType::KMERS_SUBTRACTION: //irrelevant for KMERS_SUBTRACTION and REVERSE_KMERS_SUBTRACTION
+		case OpType::COUNTERS_SUBTRACTION:
+		case OpType::REVERSE_COUNTERS_SUBTRACTION:			
+		case OpType::REVERSE_KMERS_SUBTRACTION:
+			counter_op = CounterOpType::DIFF;
+			break;
+		}
+	}
+};
+
+struct CTransformOutputDesc : public COutputDesc
+{
+	enum class OpType { HISTOGRAM, DUMP, SORT, REDUCE, COMPACT, SET_COUNTS };
+	OpType op_type;
+	bool sorted_output = false; //only for dump operation, rest is sorted anyway (except histo which does not print k-mers at all)
+	CTransformOutputDesc(OpType op_type) :op_type(op_type)
+	{ }
+};
+
+struct CFilteringParams
+{	
+	enum class file_type { fasta, fastq };
+	enum class FilterMode {normal, trim, hard_mask};
+	uint32 n_readers;
+	uint32 n_filters;
+	int fastq_buffer_size;
+	int64 mem_part_pmm_fastq_reader;
+	int64 mem_tot_pmm_fastq_reader;
+
+	int64 mem_part_pmm_fastq_filter;
+	int64 mem_tot_pmm_fastq_filter;
+
+	uint32 kmer_len;
+	uint32 gzip_buffer_size = 64 << 20;
+	uint32 bzip2_buffer_size = 64 << 20;
+
+	std::vector<std::string> input_srcs;
+	bool use_float_value = false;
+	uint32 n_min_kmers = 2;
+	uint32 n_max_kmers = 1000000000;
+	float f_min_kmers = 0.0f;
+	float f_max_kmers = 1.0f;
+	file_type input_file_type = file_type::fastq;
+	file_type output_file_type = file_type::fastq;
+	std::string output_src;
+	
+	FilterMode filter_mode = FilterMode::normal;
+};
+
+
+struct CCheckParams
+{
+	std::string kmer;
+};
+
+
+//************************************************************************************************************
+// CConfig - configuration of current application run. Singleton class.
+//************************************************************************************************************
+class CConfig
+{
+public:	
+	enum class Mode { UNDEFINED, COMPLEX, COMPARE, FILTER, SIMPLE_SET, TRANSFORM, INFO, CHECK };
+	uint32 avaiable_threads;
+	uint32 kmer_len = 0;
+	Mode mode = Mode::UNDEFINED;
+	bool verbose = false;	
+	std::vector<CInputDesc> input_desc;
+	std::vector<CKMC_header> headers;
+		
+	COutputDesc output_desc; //complex only?
+
+	std::vector<CSimpleOutputDesc> simple_output_desc;
+
+	CFilteringParams filtering_params; //for filter operation only	
+	CCheckParams check_params; // for check operation only
+
+	std::vector<CTransformOutputDesc> transform_output_desc;
+
+	CPercentProgress percent_progress;
+
+	static CConfig& GetInstance()
+	{
+		static CConfig config;
+		return config;
+	}		
+	CConfig(const CConfig&) = delete;
+	CConfig& operator=(const CConfig&) = delete;
+
+
+	bool IsLittleEndian()
+	{
+		uint64 a = 0x0807060504030201;
+		return ((uchar*)&a)[0] == 1 && ((uchar*)&a)[1] == 2 && ((uchar*)&a)[2] == 3 && ((uchar*)&a)[3] == 4 && ((uchar*)&a)[5] == 6 && ((uchar*)&a)[7] == 8;
+	}
+
+	bool IsSeparateThreadForMainProcessingNeeded()
+	{
+		return mode == Mode::SIMPLE_SET || mode == Mode::COMPLEX || mode == Mode::COMPARE;
+	}
+
+	std::string GetOperationName()
+	{
+		switch (mode)
+		{
+		case CConfig::Mode::UNDEFINED:
+			return "";
+		case CConfig::Mode::COMPLEX:
+			return "complex";
+		case CConfig::Mode::COMPARE:
+			return "compare";
+		case CConfig::Mode::FILTER:
+			return "filter";
+		case CConfig::Mode::SIMPLE_SET:
+			return "simple";
+		case CConfig::Mode::TRANSFORM:
+			return "transform";
+		case CConfig::Mode::CHECK:
+			return "check";
+		default:
+			return "";
+		}
+	}
+
+private:
+	CConfig() = default;
+};
+
+
+class CUsageDisplayer
+{
+protected:
+	std::string name;		
+	bool is2ArgOper = false;
+	bool is1ArgOper = false;
+	CUsageDisplayer(const std::string& name) :name(name){}
+	void Display2ArgGeneral() const
+	{
+		std::cout << "The '" << name << "' is two arguments' operation. General syntax:\n"
+				  << " kmc_tools " << name << " <input1 [input1_params]> <input2 [input2_params]> <output [output_params]>\n"
+				  << " input1, input2 - paths to databases generated by KMC \n"
+				  << " output		  - path  to output database\n"
+				  << " For each input there are additional parameters:\n"
+				  << "  -ci<value> - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value> - exclude k-mers occurring more of than <value> times\n"
+				  << " For output there are additional parameters:\n"
+				  << "  -ci<value> - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value> - exclude k-mers occurring more of than <value> times\n"
+				  << "  -cs<value> - maximal value of a counter\n";
+	}
+
+	void Display1ArgGeneral(bool output_params) const
+	{
+		std::cout << " The '" << name << "' is one argument operation. General syntax:\n"
+				  << " kmc_tools " << name << " <input> [input_params] <output> "<< (output_params ? "[output_params]" : "") << "\n"
+				  << " input - path to database generated by KMC \n"
+				  << " For input there are additional parameters:\n"
+				  << "  -ci<value> - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value> - exclude k-mers occurring more of than <value> times\n";
+	}
+public:
+	virtual void Display() const = 0;
+	virtual ~CUsageDisplayer() {}
+};
+
+class CGeneralUsageDisplayer : public CUsageDisplayer
+{
+public:
+	CGeneralUsageDisplayer() :CUsageDisplayer("")
+	{}
+	
+	void Display() const override
+	{
+		std::cout << "kmc_tools ver. " << KMC_VER << " (" << KMC_DATE << ")\n"
+				  << "Usage:\n kmc_tools [global parameters] <operation> [operation parameters]\n"
+				  << "Available operations:\n"
+				  << "  transform            - transforms single KMC's database\n"
+				  << "  simple               - performs set operation on two KMC's databases\n"
+				  << "  complex              - performs set operation on multiple KMC's databases\n"
+				  << "  filter               - filter out reads with too small number of k-mers\n"
+				  << " global parameters:\n"
+				  << "  -t<value>            - total number of threads (default: no. of CPU cores)\n"
+				  << "  -v                   - enable verbose mode (shows some information) (default: false)\n"
+				  << "  -hp                  - hide percentage progress (default: false)\n"
+				  << "Example:\n"
+				  << "kmc_tools simple db1 -ci3 db2 -ci5 -cx300 union db1_union_db2 -ci10\n"
+				  << "For detailed help of concrete operation type operation name without parameters:\n"
+				  << "kmc_tools simple\n";
+	}
+};
+
+class CSimpleOperationUsageDisplayer : public CUsageDisplayer
+{
+public:
+	CSimpleOperationUsageDisplayer() : CUsageDisplayer("simple"){}
+	void Display() const override
+	{
+		std::cout << "simple operation performs set operation on two input KMC's databases\n"
+				  << "General syntax:\n"
+				  << "kmc_tools simple <input1 [input1_params]> <input2 [input2_params]> <oper1 output1 [output_params1]> [<oper2 output2 [output_params2]> ...]\n"
+				  << "input1, input2                    - paths to databases generated by KMC\n"
+				  << "oper1, oper2, ..., operN          - set operations to be performed on input1 and input2\n"
+				  << "output1, output2, ..., outputN    - paths to output k-mer databases\n"
+				  << " Available operations:\n"
+				  << "  intersect                  - output database will contains only k-mers that are present in both input sets\n"
+				  << "  union                      - output database will contains each k-mer present in any of input sets\n"
+				  << "  kmers_subtract             - difference of input sets based on k-mers. \n" 
+				  << "                               Output database will contains only k-mers that are present in first input set but absent in the second one\n"
+				  << "  counters_subtract          - difference of input sets based on k-mers and their counters (weaker version of kmers_subtract).\n"
+				  << "                               Output database will contains all k-mers that are present in first input, \n"
+				  << "                               beyond those for which counter operation will lead to remove (i.e. counter equal to 0 or negative number)\n"
+				  << "  reverse_kmers_subtract     - same as kmers_subtract but treat input2 as first and input1 as second\n"
+				  << "  reverse_counters_subtract  - same as counters_subtract but treat input2 as first and input1 as second\n"
+				  << " For each input there are additional parameters:\n"
+				  << "  -ci<value>  - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value>  - exclude k-mers occurring more of than <value> times\n"
+				  << " For each output there are additional parameters:\n"
+				  << "  -ci<value>  - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value>  - exclude k-mers occurring more of than <value> times\n"
+				  << "  -cs<value>  - maximal value of a counter\n"
+				  << "  -oc<value>  - redefine counter calculation mode for equal k-mers\n"
+				  << "    Available values : \n"
+				  << "      min   - get lower value of a k-mer counter (default value for intersect operation)\n"
+				  << "      max   - get upper value of a k-mer counter\n"
+				  << "      sum   - get sum of counters from both databases\n"
+				  << "      diff  - get difference between counters (default for counters_subtract operation)\n"
+				  << "      left  - get counter from first database (input1)\n"
+				  << "      right - get counter from second database (input2)\n"
+				  << "Example:\n"
+				  << "kmc_tools simple kmers1 -ci3 -cx70000 kmers2 union kmers1_kmers2_union -cs65536 -ocfirst intersect intersect_kmers1_kmers2 intersect intersect_max_kmers1_kmers2 -ocmax\n";
+	}
+};
+
+
+class CTransformOperationUsageDisplayer : public CUsageDisplayer
+{
+public:
+	CTransformOperationUsageDisplayer() : CUsageDisplayer("transform"){}
+	void Display() const override
+	{
+		std::cout << "transform operation transforms single input database to output (text file or KMC database)\n"
+				  << "General syntax:\n"
+				  << "kmc_tools transform <input> [input_params] <oper1 [oper_params1] output1 [output_params1]> [<oper2 [oper_params2] output2 [output_params2]>...]\n"
+				  << "input - path to database generated by KMC \n"
+				  << "oper1, oper2, ..., operN          - transform operation name\n"
+				  << "output1, output2, ..., outputN    - paths to output\n"
+				  
+				  << " Available operations:\n"
+				  << "  sort                       - converts database produced by KMC2.x to KMC1.x database format (which contains k-mers in sorted order)\n"
+				  << "  reduce                     - exclude too rare and too frequent k-mers\n"
+				  << "  compact                    - remove counters of k-mers\n"
+				  << "  histogram                  - produce histogram of k-mers occurrences\n"
+				  << "  dump                       - produce text dump of kmc database\n"
+				  << "  set_counts <value>         - set all k-mer counts to specific value\n"
+				  
+				  << " For input there are additional parameters:\n"
+				  << "  -ci<value> - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value> - exclude k-mers occurring more of than <value> times\n"
+				  
+				  << " For sort and reduce operations there are additional output_params:\n"
+				  << "  -ci<value> - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value> - exclude k-mers occurring more of than <value> times\n"
+				  << "  -cs<value> - maximal value of a counter\n"
+			
+				  << " For histogram operation there are additional output_params:\n"
+				  << "  -ci<value> - minimum value of counter to be stored in the otput file\n"
+				  << "  -cx<value> - maximum value of counter to be stored in the otput file\n"
+
+				  << " For dump operation there are additional oper_params:\n"
+				  << "  -s - sorted output\n"
+				  << "Example:\n"
+				  << "kmc_tools transform db reduce err_kmers -cx10 reduce valid_kmers -ci11 histogram histo.txt dump dump.txt\n";
+	}
+};
+
+class CComplexUsageDisplayer : public CUsageDisplayer
+{
+public:
+	CComplexUsageDisplayer() :CUsageDisplayer("complex")
+	{}
+	void Display() const override
+	{
+		std::cout << "Complex operation allows one to define operations for more than 2 input k-mers sets. Command-line syntax:\n"
+				  << "kmc_tools complex <operations_definition_file>\n"
+				  << " operations_definition_file - path to file which define input sets and operations. It is text file with following syntax:\n"
+				  << " ______________________________________________________________________________ \n"
+				  << "|INPUT:                                                                        |\n"
+				  << "|<input1>=<input1_db_path> [params]                                            |\n"
+				  << "|<input2>=<input2_db_path> [params]                                            |\n"
+				  << "|...                                                                           |\n"
+				  << "|<inputN>=<inputN_db_path> [params]                                            |\n"
+				  << "|OUTPUT:                                                                       |\n"
+				  << "|<out_db>=<ref_input><oper[c_mode]><ref_input>[<oper[c_mode]><ref_input>[...]  |\n"
+				  << "|[OUTPUT_PARAMS:                                                             __|\n"
+				  << "|<output_params>]                                                           |  /\n"
+				  << "|                                                                           | / \n"
+				  << "|___________________________________________________________________________|/  \n"
+				  << "input1, input2, ..., inputN - names of inputs used to define equation\n"
+				  << "input1_db, input2_db_path, ..., inputN_db_path - paths to k-mers sets\n"
+				  << "For each input there are additional parameters which can be set:\n"
+				  << "  -ci<value> - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value> - exclude k-mers occurring more of than <value> times\n"
+				  << "out_db_path - path to output database\n"
+				  << "ref_input - one of input1, input2, ..., inputN\n"
+				  << "oper - one of {*,-,~,+}, which refers to {intersect, kmers_subtract, counters_subtract, union}\n"
+				  << "operator * has the highest priority. Other operators has equal priorities. Order of operations can be changed with parentheses\n"
+				  << "for {*,~,+} it is possible to redefine counter calculation mode ([c_mode]). Available values: min, max, diff, sum, left, right (detailet description available in simple help message)\n"
+				  << "output_params are:\n"
+				  << "  -ci<value> - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value> - exclude k-mers occurring more of than <value> times\n"
+				  << "  -cs<value> - maximal value of a counter\n"
+				  
+				  << "Example:\n"
+				  << " __________________________________________________________________ \n"
+				  << "|INPUT:                                                            |\n"
+				  << "|set1 = kmc_o1 -ci5                                                |\n"
+				  << "|set2 = kmc_o2                                                     |\n"
+				  << "|set3 = kmc_o3 -ci10 -cx100                                      __|\n"
+				  << "|OUTPUT:                                                        |  /\n"
+				  << "|result = (set3 + min set1) * right set2                        | / \n"
+				  << "|_______________________________________________________________|/  \n";
+		
+	}
+};
+
+class CFilterUsageDisplayer : public CUsageDisplayer
+{
+public:
+	CFilterUsageDisplayer() : CUsageDisplayer("filter")
+	{}
+	void Display() const override
+	{
+		std::cout << " The '" << name << "' is two arguments' operation. General syntax:\n"
+				  << " kmc_tools " << name << " [filter_params] <kmc_input_db> [kmc_input_db_params] <input_read_set> [input_read_set_params] <output_read_set> [output_read_set_params]\n"
+				  << " filter_params:\n"
+				  << " -t               - trim reads on first invalid k-mer instead of remove entirely\n"
+				  << " -hm              - hard mask invalid k-mers in a read\n"
+				  << " kmc_input_db     - path to database generated by KMC \n"
+				  << " input_read_set   - path to input set of reads \n"
+				  << " output_read_set  - path to output set of reads \n"
+				  << " For k-mers' database there are additional parameters:\n"
+				  << "  -ci<value> - exclude k-mers occurring less than <value> times \n"
+				  << "  -cx<value> - exclude k-mers occurring more of than <value> times\n"
+				  << " For input set of reads there are additional parameters:\n"
+				  << " -ci<value> - remove reads containing less k-mers than value. It can be integer or floating number in range [0.0;1.0] (default: 2)\n"
+				  << " -cx<value> - remove reads containing more k-mers than value. It can be integer or floating number in range [0.0;1.0] (default: 1e9)\n"
+				  << " -f<a/q>    - input in FASTA format (-fa), FASTQ format (-fq); default: FASTQ\n"
+				  << " For output set of reads there are additional parameters:\n"
+				  << " -f<a/q>    - output in FASTA format (-fa), FASTQ format (-fq); default: same as input\n"
+				  << "Example:\n"
+				  << "kmc_tools filter kmc_db -ci3 input.fastq -ci0.5 -cx1.0 filtered.fastq\n"
+				  << "kmc_tools filter kmc_db input.fastq -ci10 -cx100 filtered.fastq\n";
+	}
+};
+
+class CUsageDisplayerFactory
+{
+	std::unique_ptr<CUsageDisplayer> desc;
+public:
+	CUsageDisplayerFactory(CConfig::Mode mode)
+	{
+		//TODO: add desc of compare, check and info. Check also if others are not missing
+		switch (mode)
+		{
+		case CConfig::Mode::UNDEFINED:
+			desc = std::make_unique<CGeneralUsageDisplayer>();
+			break;
+		case CConfig::Mode::COMPLEX:
+			desc = std::make_unique<CComplexUsageDisplayer>();
+			break;
+		case CConfig::Mode::COMPARE:
+			desc = std::make_unique<CGeneralUsageDisplayer>();
+			break;
+		case CConfig::Mode::FILTER:
+			desc = std::make_unique<CFilterUsageDisplayer>();
+			break;
+		case CConfig::Mode::SIMPLE_SET:
+			desc = std::make_unique<CSimpleOperationUsageDisplayer>();
+			break;
+		case CConfig::Mode::TRANSFORM:
+			desc = std::make_unique<CTransformOperationUsageDisplayer>();
+			break;
+		default:
+			desc = std::make_unique<CGeneralUsageDisplayer>();
+			break;
+		}
+	}
+	const CUsageDisplayer& GetUsageDisplayer()
+	{
+		return *desc;
+	}
+};
+
+#endif
+
+
+// ***** EOF

=== added file 'kmc_tools/defs.h'
--- old/kmc_tools/defs.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/defs.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,103 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _DEFS_H
+#define _DEFS_H
+
+#include <stdio.h>
+#include <stdlib.h>
+
+using uint32 = unsigned int;
+using uint64 = unsigned long long;
+using int32 = int;
+using int64 = long long;
+using uchar = unsigned char;
+
+#define MIN(x,y)	((x) < (y) ? (x) : (y))
+#define MAX(x,y)	((x) > (y) ? (x) : (y))
+#define NORM(x, lower, upper)	((x) < (lower) ? (lower) : (x) > (upper) ? (upper) : (x))
+
+#define BYTE_LOG(x) (((x) < (1 << 8)) ? 1 : ((x) < (1 << 16)) ? 2 : ((x) < (1 << 24)) ? 3 : 4)
+
+
+//#define ENABLE_DEBUG
+//#define ENABLE_LOGGER
+
+#define KMC_VER		"3.1.1"
+#define KMC_DATE	"2019-05-19"
+
+#define DEFAULT_CIRCULAL_QUEUE_CAPACITY (4)
+
+#define SUFFIX_WRITE_QUEUE_CAPACITY (10)
+
+
+#define KMC1_DB_READER_PREFIX_BUFF_BYTES (1 << 24)
+#define KMC1_DB_READER_SUFFIX_BUFF_BYTES  (1 << 24)
+
+#define KMC2_DB_READER_PREFIX_BUFF_BYTES (1 << 24)
+#define KMC2_DB_READER_SUFFIX_BUFF_BYTES  (1 << 24)
+
+#define KMC1_DB_WRITER_PREFIX_BUFF_BYTES (1 << 24)
+#define KMC1_DB_WRITER_SUFFIX_BUFF_BYTES  (1 << 24)
+
+#define HISTOGRAM_MAX_COUNTER_DEFAULT 10000
+
+#define DUMP_BUF_SIZE (1 << 24)
+
+//Increasing this value will lead to more memory consumption, but from preliminary observations it has no performance(is sense of time) impact, so it is recommended to not change this value
+#define BUNDLE_CAPACITY (1 << 12) //in kmers, for kmers and counters. 
+
+#define KMC2_DB_READER_BUNDLE_CAPACITY (1 << 22)
+
+//this value has high impact to used memory, max value of memory is = 2 * SINGLE_BIN_BUFF_SIZE_FOR_DB2_READER * number_of_kmc2_input_dbs * number_of_bins_per_in_db
+//increasing this value can have positive performance impact when running on HDD
+#define SINGLE_BIN_BUFF_SIZE_FOR_DB2_READER (1 << 21) //if less is needed less will be allocated
+
+//default values
+#define CUTOFF_MIN 2 
+#define CUTOFF_MAX 1000000000
+#define COUNTER_MAX 255
+
+#define MAX_K		256
+#define KMER_WORDS		((MAX_K + 31) / 32)
+
+
+#define USE_META_PROG
+
+#ifdef WIN32
+#define my_fopen	fopen
+#define my_fseek	_fseeki64
+#define my_ftell	_ftelli64
+#else
+#define my_fopen	fopen
+#define my_fseek	fseek
+#define my_ftell	ftell
+#endif
+
+
+#ifdef _MSC_VER
+	#define _bswap_uint64(X) _byteswap_uint64(X)
+	#define _bswap_uint32(X) _byteswap_ulong(X)
+#elif defined(__GNUC__)
+	#define _bswap_uint64(X) __builtin_bswap64(X)
+	#define _bswap_uint32(X) __builtin_bswap32(X)
+#else //unknown. Use the fastest "standard" way I've found
+	#define _bswap_uint64(val) \
+		val = ((val << 8) & 0xFF00FF00FF00FF00ULL) + ((val >> 8) & 0x00FF00FF00FF00FFULL); \
+		val = ((val << 16) & 0xFFFF0000FFFF0000ULL) + ((val >> 16) & 0x0000FFFF0000FFFFULL); \
+		val = (val << 32) + (val >> 32);
+	#define _bswap_uint32(val) \
+		val = (val<<24) | ((val<<8) & 0x00ff0000) | ((val >> 8) & 0x0000ff00) | (val >> 24);
+#endif
+
+#endif
+
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/develop.h'
--- old/kmc_tools/develop.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/develop.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,66 @@
+#ifndef _DEVELOP_H
+#define _DEVELOP_H
+
+#include <map>
+#include <vector>
+#include <iostream>
+#include <string>
+#include "defs.h"
+#include "timer.h"
+
+#ifdef ENABLE_LOGGER
+
+
+
+
+class CLoger
+{
+	std::map<std::string, std::map<void*, std::vector<double>>> operations;
+
+public:
+	static CLoger& GetLogger()
+	{
+		static CLoger logger;
+		return logger;
+	}
+
+	void add_to_operators_times(void* addr, double time)
+	{
+		operations["operators_times"][addr].push_back(time);
+	}
+
+	void add_to_wait_for_inputdata(void* addr, double time)
+	{
+		operations["wait_for_input"][addr].push_back(time);
+	}
+
+	void log_operation(const std::string& name, void* addr, double time)
+	{
+		operations[name][addr].push_back(time);
+	}
+
+	void print_stats()
+	{
+		std::cout << "--operators_times--\n";
+
+		for (auto& oper : operations)
+		{
+			std::cout << "operation : -----------" << oper.first << "-----------\n";
+			for (const auto& _operator : oper.second)
+			{
+				std::cout << "operator at " << _operator.first << "\n";
+				double sum = 0;
+				for (const auto& time : _operator.second)
+				{
+					sum += time;
+				}
+				std::cout << "time: " << sum << "\nmean: " << (sum / _operator.second.size()) << ", no of entries: " << _operator.second.size() << "\n";
+			}
+		}
+
+	}
+};
+#endif
+
+
+#endif
\ No newline at end of file

=== added file 'kmc_tools/dump_writer.h'
--- old/kmc_tools/dump_writer.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/dump_writer.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,237 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _DUMP_WRITER_H
+#define _DUMP_WRITER_H
+#include "defs.h"
+#include "kmer.h"
+#include "nc_utils.h"
+#include "config.h"
+#include <fstream>
+
+
+//wrapper to simplify interface
+
+//For kmc1 input and kmc2 input without -s parameter
+template<typename KMCDB, unsigned SIZE, bool SORTED>
+class CKMCDBForDump
+{
+	KMCDB kmcdb;
+public:
+	CKMCDBForDump() :
+		kmcdb(CConfig::GetInstance().headers.front(), CConfig::GetInstance().input_desc.front(), CConfig::GetInstance().percent_progress, KMCDBOpenMode::sequential){}
+	bool NextKmer(CKmer<SIZE>& kmer, uint32& counter)
+	{
+		return kmcdb.NextKmerSequential(kmer, counter);
+	}
+};
+
+
+//specialization for -s parameter nad kmc2 input
+template<unsigned SIZE>
+class CKMCDBForDump<CKMC2DbReader<SIZE>, SIZE, true>
+{
+	CKMC2DbReader<SIZE>* kmcdb;
+	CBundle<SIZE> bundle;
+public:
+	CKMCDBForDump() :
+		kmcdb(new CKMC2DbReader<SIZE>(CConfig::GetInstance().headers.front(), CConfig::GetInstance().input_desc.front(), CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted)), 
+		bundle(kmcdb){}
+	bool NextKmer(CKmer<SIZE>& kmer, uint32& counter)
+	{
+		if(!bundle.Finished())
+		{
+			kmer = bundle.TopKmer();
+			counter = bundle.TopCounter();
+			bundle.Pop();
+			return true;
+		}
+		return false;
+	}
+};
+
+template<unsigned SIZE>
+class CDumpWriterBase
+{
+	static const uint32 OVERHEAD_SIZE = 1000;
+
+	uint32 counter_len;
+	
+	std::string& file_src;
+	uint32 cutoff_max;
+	uint32 cutoff_min;	
+	uint32 counter_max;
+	
+	uint32 kmer_len;
+	uint32 kmer_bytes;
+	CConfig& config;
+	uint32 in_first_byte;
+	char* buf;
+	uint32 buf_size;
+	uint32 buf_pos;
+
+	FILE* file = nullptr;
+
+	struct DumpOpt
+	{
+		char* opt_ACGT;
+		DumpOpt()
+		{
+			opt_ACGT = new char[1024];
+			char codes[] = { 'A', 'C', 'G', 'T' };
+			uint32 pos = 0;
+			for (uint32 kmer = 0; kmer < 256; ++kmer)
+			{
+				opt_ACGT[pos++] = codes[(kmer >> 6) & 3];
+				opt_ACGT[pos++] = codes[(kmer >> 4) & 3];
+				opt_ACGT[pos++] = codes[(kmer >> 2) & 3];
+				opt_ACGT[pos++] = codes[kmer & 3];
+			}
+
+		}
+		~DumpOpt()
+		{
+			delete[]opt_ACGT;
+		}
+
+	}opt;
+
+	void kmerToStr(CKmer<SIZE>& kmer, char* kmer_str)
+	{
+		//first byte
+		char* base = opt.opt_ACGT + 4 * kmer.get_byte(kmer_bytes - 1) + 4 - in_first_byte;
+		for (uint32 i = 0; i < in_first_byte; ++i)
+			*kmer_str++ = *base++;
+		//rest
+		for (int pos = kmer_bytes - 2; pos >= 0; --pos)
+		{
+			base = opt.opt_ACGT + 4 * kmer.get_byte(pos);
+			*kmer_str++ = *base++;
+			*kmer_str++ = *base++;
+			*kmer_str++ = *base++;
+			*kmer_str++ = *base++;
+		}
+	}
+
+protected:
+	void Init()
+	{
+		file = fopen(file_src.c_str(), "wb");
+		if (!file)
+		{
+			std::cerr << "Error: cannot open file: " << file_src << "\n";
+			exit(1);
+		}
+		buf_pos = 0;
+		buf_size = DUMP_BUF_SIZE;
+		buf = new char[buf_size];
+	}
+
+	void ProcessKmer(CKmer<SIZE>& kmer, uint32 counter)
+	{
+		if (counter >= cutoff_min && counter <= cutoff_max)
+		{
+			if (counter > counter_max)
+				counter = counter_max;
+			kmerToStr(kmer, buf + buf_pos);
+			buf[buf_pos + kmer_len] = '\t';
+			counter_len = CNumericConversions::Int2PChar(counter, (uchar*)(buf + buf_pos + kmer_len + 1));
+			buf[buf_pos + kmer_len + 1 + counter_len] = '\n';
+			buf_pos += kmer_len + 2 + counter_len;
+			if (buf_pos + OVERHEAD_SIZE > buf_size)
+			{
+				fwrite(buf, 1, buf_pos, file);
+				buf_pos = 0;
+			}
+		}
+	}
+
+	void Finish()
+	{		
+		if (buf_pos)
+		{
+			fwrite(buf, 1, buf_pos, file);
+			buf_pos = 0;
+		}
+
+		fclose(file);
+		delete[] buf;
+	}
+
+protected:
+	CDumpWriterBase(std::string& file_src, uint32 cutoff_max, uint32 cutoff_min, uint32 counter_max) :
+		file_src(file_src),
+		cutoff_max(cutoff_max),
+		cutoff_min(cutoff_min),
+		counter_max(counter_max),
+		config(CConfig::GetInstance())
+	{
+		kmer_len = config.headers.front().kmer_len;
+		kmer_bytes = (kmer_len + 3) / 4;
+		in_first_byte = kmer_len % 4;
+		if (in_first_byte == 0)
+			in_first_byte = 4;
+	}
+};
+
+template<typename KMCDB, unsigned SIZE>
+class CDumpWriter : public CDumpWriterBase<SIZE>
+{	
+	KMCDB& kmcdb;
+public:
+	CDumpWriter(KMCDB& kmcdb) :
+		CDumpWriterBase<SIZE>(CConfig::GetInstance().output_desc.file_src, CConfig::GetInstance().output_desc.cutoff_max, CConfig::GetInstance().output_desc.cutoff_min, CConfig::GetInstance().output_desc.counter_max),
+		kmcdb(kmcdb)		
+	{
+
+	}
+
+	bool Process()
+	{
+		CKmer<SIZE> kmer;
+		uint32 counter;			
+		this->Init();		
+		while (kmcdb.NextKmer(kmer, counter))
+		{
+			this->ProcessKmer(kmer, counter);
+		}
+		this->Finish();		
+		return true;
+	}
+};
+
+template<unsigned SIZE>
+class CDumpWriterForTransform : public CDumpWriterBase<SIZE>
+{	
+public:
+	CDumpWriterForTransform(CTransformOutputDesc& output_desc)
+		:
+		CDumpWriterBase<SIZE>(output_desc.file_src, output_desc.cutoff_max, output_desc.cutoff_min, output_desc.counter_max)
+	{
+
+	}
+
+	void Init()
+	{
+		CDumpWriterBase<SIZE>::Init();
+	}
+
+	void PutKmer(CKmer<SIZE>& kmer, uint32 counter)
+	{
+		this->ProcessKmer(kmer, counter);
+	}
+
+	void Finish()
+	{
+		CDumpWriterBase<SIZE>::Finish();
+	}
+};
+
+#endif
\ No newline at end of file

=== added file 'kmc_tools/expression_node.h'
--- old/kmc_tools/expression_node.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/expression_node.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,227 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _EXPRESSION_NODE_H
+#define _EXPRESSION_NODE_H
+#include "defs.h"
+#include "operations.h"
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <memory>
+#include "kmc1_db_reader.h"
+#include "kmc2_db_reader.h"
+
+//************************************************************************************************************
+// CExpressionNode - Base abstract class representing expression node. In first stage of algorithm from
+// user input there is created binary tree. Node type represents operation. This tree is only for generating
+// another tree (check out CInput and CBundle)
+//************************************************************************************************************
+template<unsigned SIZE> class CExpressionNode
+{
+public:
+	CExpressionNode() :left(nullptr), right(nullptr)
+	{
+
+	}
+	CExpressionNode* GetLeftChild() const
+	{
+		return left;
+	}
+	CExpressionNode* GetRightChild() const
+	{
+		return right;
+	}
+
+	virtual CBundle<SIZE>* GetExecutionRoot() = 0;
+
+	void AddLeftChild(CExpressionNode* child)
+	{
+#ifdef ENABLE_DEBUG
+		if (left)
+		{
+			std::cout << "This child node already exists\n";
+			exit(1);
+		}
+#endif
+		left = child;
+	}
+
+	void AddRightChild(CExpressionNode* child)
+	{
+#ifdef ENABLE_DEBUG
+		if (right)
+		{
+			std::cout << "This child node already exists\n";
+			exit(1);
+		}
+#endif
+		right = child;
+	}
+
+#ifdef ENABLE_DEBUG	
+	virtual void Info() = 0;
+	void Display(int adient = 0)
+	{
+		if (right)
+			right->Display(adient + 5);
+
+		for (int i = 0; i < adient; ++i)
+			std::cout << " ";
+		Info();
+		std::cout << "\n";
+		if (left)
+			left->Display(adient + 5);
+	}
+#endif
+
+	virtual ~CExpressionNode()
+	{
+		delete left;
+		delete right;
+	}
+
+protected:
+	CExpressionNode* left, *right;
+};
+
+template<unsigned SIZE> class COperNode : public CExpressionNode<SIZE> 
+{
+protected:
+	CounterOpType counter_op_type;
+public:
+	COperNode(CounterOpType counter_op_type) :counter_op_type(counter_op_type)
+	{
+
+	}
+	void SetCounterOpType(CounterOpType _counter_op_type)
+	{
+		this->counter_op_type = _counter_op_type;
+	}
+};
+
+//************************************************************************************************************
+// CUnionNode - represents node for union operation
+//************************************************************************************************************
+template<unsigned SIZE> class CUnionNode : public COperNode<SIZE>
+{
+public:
+	CUnionNode():COperNode<SIZE>(CounterOpType::SUM)
+	{
+	}
+	CBundle<SIZE>* GetExecutionRoot() override
+	{
+		return new CBundle<SIZE>(new CUnion<SIZE>(this->left->GetExecutionRoot(), this->right->GetExecutionRoot(), this->counter_op_type));
+	}
+#ifdef ENABLE_DEBUG
+	void Info() override
+	{
+		std::cout << "+";
+	}
+#endif
+};
+
+//************************************************************************************************************
+// CKmersSubtractionNode - represents node for subtraction of k-mers (if k-mer exists in both input,
+//	it is absent in result) operation
+//************************************************************************************************************
+template<unsigned SIZE> class CKmersSubtractionNode : public COperNode<SIZE>
+{
+public:
+	CKmersSubtractionNode() :COperNode<SIZE>(CounterOpType::NONE)
+	{
+	}
+	CBundle<SIZE>* GetExecutionRoot() override
+	{
+		return new CBundle<SIZE>(new CKmersSubtract<SIZE>(this->left->GetExecutionRoot(), this->right->GetExecutionRoot()));
+	}
+#ifdef ENABLE_DEBUG
+	void Info() override
+	{
+		std::cout << "-";
+	}
+#endif
+};
+
+
+template<unsigned SIZE> class CCountersSubtractionNode : public COperNode<SIZE>
+{
+public:
+	CCountersSubtractionNode():
+		COperNode<SIZE>(CounterOpType::DIFF)
+	{
+	}
+	CBundle<SIZE>* GetExecutionRoot() override
+	{
+		return new CBundle<SIZE>(new CCountersSubtract<SIZE>(this->left->GetExecutionRoot(), this->right->GetExecutionRoot(), this->counter_op_type));
+	}
+#ifdef ENABLE_DEBUG
+	void Info() override
+	{
+		std::cout << "~";
+	}
+#endif
+};
+//************************************************************************************************************
+// CIntersectionNode - represents node for intersection operation
+//************************************************************************************************************
+template<unsigned SIZE> class CIntersectionNode : public COperNode<SIZE>
+{
+public:	
+	CIntersectionNode():
+		COperNode<SIZE>(CounterOpType::MIN)
+	{
+	}
+	CBundle<SIZE>* GetExecutionRoot() override
+	{		
+		return new CBundle<SIZE>(new CIntersection<SIZE>(this->left->GetExecutionRoot(), this->right->GetExecutionRoot(), this->counter_op_type));
+	}
+#ifdef ENABLE_DEBUG
+	void Info() override
+	{
+		std::cout << "*";
+	}
+#endif
+};
+
+//************************************************************************************************************
+// CInputNode - represents node (leaf) - KMC1 or KMC2 database
+//************************************************************************************************************
+template<unsigned SIZE> class CInputNode : public CExpressionNode<SIZE>
+{	
+	uint32 desc_pos;
+public:
+	CInputNode(uint32 desc_pos) : desc_pos(desc_pos)
+	{
+	}
+	CBundle<SIZE>* GetExecutionRoot() override
+	{		
+		CConfig& config = CConfig::GetInstance();
+		CInput<SIZE>* db = nullptr;
+		if (!config.headers[desc_pos].IsKMC2())
+			db = new CKMC1DbReader<SIZE>(config.headers[desc_pos], config.input_desc[desc_pos], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+		else		
+			db = new CKMC2DbReader<SIZE>(config.headers[desc_pos], config.input_desc[desc_pos], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+		return new CBundle<SIZE>(db);
+	}
+
+#ifdef ENABLE_DEBUG
+	void Info() override
+	{
+		std::cout << "In: " << CConfig::GetInstance().input_desc[desc_pos].file_src;
+	}
+#endif
+};
+
+
+#endif
+
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/fastq_filter.cpp'
--- old/kmc_tools/fastq_filter.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/fastq_filter.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,650 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "fastq_filter.h"
+#include <numeric>
+
+using namespace std;
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+CFastqFilter::CFastqFilter(CFilteringParams& Params, CFilteringQueues& Queues, CKMCFile& kmc_api) :
+kmc_api(kmc_api)
+{
+	input_part_queue = Queues.input_part_queue;
+	filtered_part_queue = Queues.filtered_part_queue;
+	pmm_fastq_reader = Queues.pmm_fastq_reader;
+	pmm_fastq_filter = Queues.pmm_fastq_filter;
+	input_file_type = Params.input_file_type;
+	output_file_type = Params.output_file_type;
+	use_float_value = Params.use_float_value;
+	f_max_kmers = Params.f_max_kmers;
+	f_min_kmers = Params.f_min_kmers;
+	n_max_kmers = Params.n_max_kmers;
+	n_min_kmers = Params.n_min_kmers;
+	kmer_len = Params.kmer_len;
+	output_part_size = Params.mem_part_pmm_fastq_reader;
+	mode = Params.filter_mode;
+}
+
+/*****************************************************************************************************************************/
+CWFastqFilter::CWFastqFilter(CFilteringParams& Params, CFilteringQueues& Queues, CKMCFile& kmc_api)
+{
+	ff = make_unique<CFastqFilter>(Params, Queues, kmc_api);
+}
+
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+void CFastqFilter::Process()
+{
+	if (mode == CFilteringParams::FilterMode::trim)
+	{
+		if (input_file_type == CFilteringParams::file_type::fastq && output_file_type == CFilteringParams::file_type::fastq)
+			ProcessImpl<TrimFastqToFastqHelper>();
+		else if (input_file_type == CFilteringParams::file_type::fastq && output_file_type == CFilteringParams::file_type::fasta)
+			ProcessImpl<TrimFastqToFastaHelper>();
+		else if (input_file_type == CFilteringParams::file_type::fasta && output_file_type == CFilteringParams::file_type::fasta)
+			ProcessImpl<TrimFastaToFastaHelper>();
+		else
+		{
+			cerr << "Error: this file type is not supported by filter operation\n";
+			exit(1);
+		}
+	}
+	else if (mode == CFilteringParams::FilterMode::hard_mask)
+	{
+		if (input_file_type == CFilteringParams::file_type::fastq && output_file_type == CFilteringParams::file_type::fastq)
+			ProcessImpl<HardMaskFastqToFastqHelper>();
+		else if (input_file_type == CFilteringParams::file_type::fastq && output_file_type == CFilteringParams::file_type::fasta)
+			ProcessImpl<HardMaskFastqToFastaHelper>();
+		else if (input_file_type == CFilteringParams::file_type::fasta && output_file_type == CFilteringParams::file_type::fasta)
+			ProcessImpl<HardMaskFastaToFastaHelper>();
+		else
+			cerr << "Error: this file type is not supported by filter operation\n";
+	}
+	else
+	{
+		if (input_file_type == CFilteringParams::file_type::fastq && output_file_type == CFilteringParams::file_type::fastq)
+			ProcessImpl<FastqToFastqHelper>();
+		else if (input_file_type == CFilteringParams::file_type::fastq && output_file_type == CFilteringParams::file_type::fasta)
+			ProcessImpl<FastqToFastaHelper>();
+		else if (input_file_type == CFilteringParams::file_type::fasta && output_file_type == CFilteringParams::file_type::fasta)
+			ProcessImpl<FastaToFastaHelper>();
+		else
+		{
+			cerr << "Error: this file type is not supported by filter operation\n";
+			exit(1);
+		}
+	}
+}
+
+/*****************************************************************************************************************************/
+void CWFastqFilter::operator()()
+{
+	ff->Process();
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+bool CFastqFilter::FilterRead()
+{
+	uint32 read_len = static_cast<uint32>(seq_desc.read_end - seq_desc.read_start);
+	read.assign((char*)input_part + seq_desc.read_start, read_len);
+
+	kmc_api.GetCountersForRead(read, counters);
+	uint32 valid_kmers = 0;
+	for (auto counter : counters)
+	if (counter)
+		++valid_kmers;
+
+	if (use_float_value)
+	{
+		uint32 min = static_cast<uint32>(f_min_kmers * (read_len - kmer_len + 1));
+		uint32 max = static_cast<uint32>(f_max_kmers * (read_len - kmer_len + 1));
+		if (valid_kmers >= min && valid_kmers <= max)
+			return true;
+		return false;
+	}
+	else
+	{
+		if (valid_kmers >= n_min_kmers && valid_kmers <= n_max_kmers)
+			return true;
+		return false;
+	}
+}
+
+/*****************************************************************************************************************************/
+bool CFastqFilter::FilterReadTrim()
+{
+	uint32 read_len = static_cast<uint32>(seq_desc.read_end - seq_desc.read_start);
+	read.assign((char*)input_part + seq_desc.read_start, read_len);
+
+	kmc_api.GetCountersForRead(read, counters);
+	if (counters[0] < n_min_kmers)
+		return false;
+
+	trim_len = kmer_len;
+	for (uint32 i = 1; i < counters.size(); ++i, ++trim_len)
+	{
+		if (counters[i] < n_min_kmers)
+			break;
+	}
+
+
+	return true;
+}
+void CFastqFilter::HardMask()
+{
+	uint32 read_len = static_cast<uint32>(seq_desc.read_end - seq_desc.read_start);
+	read.assign((char*)input_part + seq_desc.read_start, read_len);
+	kmc_api.GetCountersForRead(read, counters);
+	uchar* read_in = input_part + seq_desc.read_start;
+	uint32 read_in_pos = 0;
+	for (uint32 counter_pos = 0; counter_pos < counters.size(); ++counter_pos)
+	{
+		if (counters[counter_pos] < n_min_kmers)
+		{
+			while (read_in_pos < counter_pos + kmer_len)
+			{
+				output_part[output_part_pos++] = 'N';
+				read_in_pos++;
+			}
+		}
+		else if (read_in_pos <= counter_pos)
+			output_part[output_part_pos++] = read_in[read_in_pos++];
+	}
+	while (read_in_pos < read_len)
+		output_part[output_part_pos++] = read_in[read_in_pos++];
+	output_part[output_part_pos++] = '\n';
+}
+
+/*****************************************************************************************************************************/
+bool CFastqFilter::NextSeqFasta()
+{
+	// Title
+	char c;
+	if (input_part_pos >= input_part_size)
+		return false;
+	c = input_part[input_part_pos++];
+	if (c != '>')
+		return false;
+
+	seq_desc.read_header_start = input_part_pos - 1;
+
+	for (; input_part_pos < input_part_size;)
+	{
+		c = input_part[input_part_pos++];
+		if (c < 32)					// newliners
+			break;
+	}
+	seq_desc.read_header_end = input_part_pos - 1;
+
+	if (input_part_pos >= input_part_size)
+		return false;
+
+	c = input_part[input_part_pos++];
+	if (c >= 32)
+		input_part_pos--;
+	else if (input_part_pos >= input_part_size)
+		return false;
+
+	seq_desc.read_start = input_part_pos;
+	// Sequence
+	for (; input_part_pos < input_part_size;)
+	{
+		c = input_part[input_part_pos++];
+		if (c < 32)					// newliners
+			break;
+	}
+	seq_desc.read_end = input_part_pos - 1;
+
+	seq_desc.end = input_part_pos;
+
+	if (input_part_pos >= input_part_size)
+		return true;
+
+	seq_desc.end++;
+	if (input_part[input_part_pos++] >= 32)
+	{
+		input_part_pos--;
+		seq_desc.end--;
+	}
+
+	else if (input_part_pos >= input_part_size)
+		return true;
+
+	return (c == '\n' || c == '\r');
+}
+
+/*****************************************************************************************************************************/
+bool CFastqFilter::NextSeqFastq()
+{
+	char c;
+	// Title
+	if (input_part_pos >= input_part_size)
+		return false;
+
+
+	c = input_part[input_part_pos++];
+	if (c != '@')
+		return false;
+
+	seq_desc.read_header_start = input_part_pos - 1;
+
+	for (; input_part_pos < input_part_size;)
+	{
+		c = input_part[input_part_pos++];
+		if (c < 32)					// newliners
+			break;
+	}
+	seq_desc.read_header_end = input_part_pos - 1;
+
+	if (input_part_pos >= input_part_size)
+		return false;
+
+	c = input_part[input_part_pos++];
+	if (c >= 32)
+		input_part_pos--;
+	else if (input_part_pos >= input_part_size)
+		return false;
+
+	seq_desc.read_start = input_part_pos;
+	// Sequence
+	for (; input_part_pos < input_part_size;)
+	{
+		c = input_part[input_part_pos++];
+		if (c < 32)					// newliners
+			break;
+	}
+	seq_desc.read_end = input_part_pos - 1;
+
+	if (input_part_pos >= input_part_size)
+		return false;
+
+	c = input_part[input_part_pos++];
+	if (c >= 32)
+		input_part_pos--;
+	else if (input_part_pos >= input_part_size)
+		return false;
+
+	// Plus	
+	c = input_part[input_part_pos++];
+	if (input_part_pos >= input_part_size)
+		return false;
+	if (c != '+')
+		return false;
+
+	seq_desc.quality_header_start = input_part_pos - 1;
+
+	for (; input_part_pos < input_part_size;)
+	{
+		c = input_part[input_part_pos++];
+		if (c < 32)					// newliners
+			break;
+	}
+	seq_desc.quality_header_end = input_part_pos - 1;
+
+	if (input_part_pos >= input_part_size)
+		return false;
+
+	c = input_part[input_part_pos++];
+	if (c >= 32)
+		input_part_pos--;
+	else if (input_part_pos >= input_part_size)
+		return false;
+
+	// Quality
+	seq_desc.quality_start = input_part_pos;
+
+	input_part_pos += seq_desc.read_end - seq_desc.read_start;
+	if (input_part_pos >= input_part_size)
+		return false;
+	c = input_part[input_part_pos++];
+
+	seq_desc.quality_end = input_part_pos - 1;
+
+	seq_desc.end = input_part_pos;
+
+	if (input_part_pos >= input_part_size)
+		return true;
+
+	seq_desc.end++;
+	if (input_part[input_part_pos++] >= 32)
+	{
+		input_part_pos--;
+		seq_desc.end--;
+
+	}
+	else if (input_part_pos >= input_part_size)
+		return true;
+
+	return c == '\n' || c == '\r';
+}
+
+/*****************************************************************************************************************************/
+template<class Helper>
+void CFastqFilter::ProcessImpl()
+{
+	Helper helper(*this);
+	pmm_fastq_filter->reserve(output_part);
+	output_part_pos = 0;
+	uint64 required_size;
+	while (input_part_queue->pop(input_part, input_part_size))
+	{
+		input_part_pos = 0;
+		while (helper.NextSeq())
+		{
+			if (helper.FilterRead())
+			{
+				required_size = helper.GetReqSize();
+				if (output_part_pos + required_size > output_part_size)
+				{
+					filtered_part_queue->push(output_part, output_part_pos);
+					pmm_fastq_filter->reserve(output_part);
+					output_part_pos = 0;
+				}
+				helper.SendToOutBuf();
+
+			}
+		}
+		pmm_fastq_reader->free(input_part);
+	}
+	filtered_part_queue->push(output_part, output_part_pos);
+	filtered_part_queue->mark_completed();
+}
+
+
+/*****************************************************************************************************************************/
+/************************************************************ HELPERS CLASSES ************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+class CFastqFilter::FastqToFastqHelper
+{
+	CFastqFilter& owner;
+public:
+	FastqToFastqHelper(CFastqFilter& owner) :owner(owner){}
+	uint64 GetReqSize() const
+	{
+		return owner.seq_desc.quality_header_start - owner.seq_desc.read_header_start + 1 + owner.seq_desc.end - owner.seq_desc.quality_header_end;
+	}
+	void SendToOutBuf() const
+	{
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_header_start, owner.seq_desc.quality_header_start - owner.seq_desc.read_header_start + 1);
+		owner.output_part_pos += owner.seq_desc.quality_header_start - owner.seq_desc.read_header_start + 1;
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.quality_header_end, owner.seq_desc.end - owner.seq_desc.quality_header_end);
+		owner.output_part_pos += owner.seq_desc.end - owner.seq_desc.quality_header_end;
+	}
+
+	bool NextSeq() const
+	{
+		return owner.NextSeqFastq();
+	}
+
+	bool FilterRead() const
+	{
+		return owner.FilterRead();
+	}
+};
+
+/*****************************************************************************************************************************/
+class CFastqFilter::FastqToFastaHelper
+{
+	CFastqFilter& owner;
+public:
+	FastqToFastaHelper(CFastqFilter& owner) :owner(owner){}
+	uint64 GetReqSize() const
+	{
+		return owner.seq_desc.quality_header_start - owner.seq_desc.read_header_start;
+	}
+	void SendToOutBuf() const
+	{
+		owner.input_part[owner.seq_desc.read_header_start] = '>';
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_header_start, owner.seq_desc.quality_header_start - owner.seq_desc.read_header_start);
+		owner.output_part_pos += owner.seq_desc.quality_header_start - owner.seq_desc.read_header_start;
+	}
+	bool NextSeq() const
+	{
+		return owner.NextSeqFastq();
+	}
+
+	bool FilterRead() const
+	{
+		return owner.FilterRead();
+	}
+};
+
+/*****************************************************************************************************************************/
+class CFastqFilter::FastaToFastaHelper
+{
+	CFastqFilter& owner;
+public:
+	FastaToFastaHelper(CFastqFilter& owner) :owner(owner){}
+	uint64 GetReqSize() const
+	{
+		return owner.seq_desc.end - owner.seq_desc.read_header_start;
+	}
+
+	void SendToOutBuf() const
+	{
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_header_start, owner.seq_desc.end - owner.seq_desc.read_header_start);
+		owner.output_part_pos += owner.seq_desc.end - owner.seq_desc.read_header_start;
+	}
+	bool NextSeq() const
+	{
+		return owner.NextSeqFasta();
+	}
+
+	bool FilterRead() const
+	{
+		return owner.FilterRead();
+	}
+};
+
+/*****************************************************************************************************************************/
+class CFastqFilter::TrimFastqToFastqHelper
+{
+	CFastqFilter& owner;
+public:
+	TrimFastqToFastqHelper(CFastqFilter& owner) :owner(owner){}
+	uint64 GetReqSize() const
+	{
+		return owner.seq_desc.read_header_end - owner.seq_desc.read_header_start + 1 + owner.trim_len + 3 + owner.trim_len + 1;
+	}
+	void SendToOutBuf() const
+	{
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_header_start, owner.seq_desc.read_header_end - owner.seq_desc.read_header_start);
+		owner.output_part_pos += owner.seq_desc.read_header_end - owner.seq_desc.read_header_start;
+		owner.output_part[owner.output_part_pos++] = '\n';
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_start, owner.trim_len);
+		owner.output_part_pos += owner.trim_len;
+		owner.output_part[owner.output_part_pos++] = '\n';
+		owner.output_part[owner.output_part_pos++] = '+';
+		owner.output_part[owner.output_part_pos++] = '\n';
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.quality_start, owner.trim_len);
+		owner.output_part_pos += owner.trim_len;
+		owner.output_part[owner.output_part_pos++] = '\n';
+	}
+
+	bool NextSeq() const
+	{
+		return owner.NextSeqFastq();
+	}
+
+	bool FilterRead() const
+	{
+		return owner.FilterReadTrim();
+	}
+};
+
+
+/*****************************************************************************************************************************/
+class CFastqFilter::TrimFastqToFastaHelper
+{
+	CFastqFilter& owner;
+public:
+	TrimFastqToFastaHelper(CFastqFilter& owner) :owner(owner){}
+	uint64 GetReqSize() const
+	{
+		return owner.seq_desc.read_header_end - owner.seq_desc.read_header_start + 1 + owner.trim_len + 1;
+	}
+	void SendToOutBuf() const
+	{
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_header_start, owner.seq_desc.read_header_end - owner.seq_desc.read_header_start);
+		owner.output_part[owner.output_part_pos] = '>';
+		owner.output_part_pos += owner.seq_desc.read_header_end - owner.seq_desc.read_header_start;
+		owner.output_part[owner.output_part_pos++] = '\n';
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_start, owner.trim_len);
+		owner.output_part_pos += owner.trim_len;
+		owner.output_part[owner.output_part_pos++] = '\n';
+	}
+
+	bool NextSeq() const
+	{
+		return owner.NextSeqFastq();
+	}
+
+	bool FilterRead() const
+	{
+		return owner.FilterReadTrim();
+	}
+};
+
+
+
+/*****************************************************************************************************************************/
+class CFastqFilter::TrimFastaToFastaHelper
+{
+	CFastqFilter& owner;
+public:
+	TrimFastaToFastaHelper(CFastqFilter& owner) :owner(owner){}
+	uint64 GetReqSize() const
+	{
+		return owner.seq_desc.read_header_end - owner.seq_desc.read_header_start + 1 + owner.trim_len + 1;
+	}
+	void SendToOutBuf() const
+	{
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_header_start, owner.seq_desc.read_header_end - owner.seq_desc.read_header_start);
+		owner.output_part_pos += owner.seq_desc.read_header_end - owner.seq_desc.read_header_start;
+		owner.output_part[owner.output_part_pos++] = '\n';
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_start, owner.trim_len);
+		owner.output_part_pos += owner.trim_len;
+		owner.output_part[owner.output_part_pos++] = '\n';
+	}
+
+	bool NextSeq() const
+	{
+		return owner.NextSeqFasta();
+	}
+
+	bool FilterRead() const
+	{
+		return owner.FilterReadTrim();
+	}
+};
+
+class CFastqFilter::HardMaskFastqToFastqHelper
+{
+	CFastqFilter& owner;
+public:
+	HardMaskFastqToFastqHelper(CFastqFilter& owner) : owner(owner) {}
+	uint64 GetReqSize() const
+	{
+		return owner.seq_desc.read_header_end - owner.seq_desc.read_header_start + 1 +
+			owner.seq_desc.read_end - owner.seq_desc.read_start + 1 +
+			2 +
+			owner.seq_desc.quality_end - owner.seq_desc.quality_start + 1;
+	}
+	void SendToOutBuf() const
+	{
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_header_start, owner.seq_desc.read_header_end - owner.seq_desc.read_header_start);
+		owner.output_part_pos += owner.seq_desc.read_header_end - owner.seq_desc.read_header_start;
+		owner.output_part[owner.output_part_pos++] = '\n';
+		owner.HardMask();
+		owner.output_part[owner.output_part_pos++] = '+';
+		owner.output_part[owner.output_part_pos++] = '\n';
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.quality_start, owner.seq_desc.quality_end - owner.seq_desc.quality_start);
+		owner.output_part_pos += owner.seq_desc.quality_end - owner.seq_desc.quality_start;
+		owner.output_part[owner.output_part_pos++] = '\n';		
+	}
+	bool NextSeq() const
+	{
+		return owner.NextSeqFastq();
+	}
+	bool FilterRead() const
+	{
+		return true;
+	}
+};
+class CFastqFilter::HardMaskFastqToFastaHelper
+{
+	CFastqFilter& owner;
+public:
+	HardMaskFastqToFastaHelper(CFastqFilter& owner) : owner(owner) {}
+	uint64 GetReqSize() const
+	{
+		return owner.seq_desc.read_header_end - owner.seq_desc.read_header_start + 1 +
+			owner.seq_desc.read_end - owner.seq_desc.read_start + 1;
+	}
+	void SendToOutBuf() const
+	{
+		owner.input_part[owner.seq_desc.read_header_start] = '>';
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_header_start, owner.seq_desc.read_header_end - owner.seq_desc.read_header_start);
+		owner.output_part_pos += owner.seq_desc.read_header_end - owner.seq_desc.read_header_start;
+		owner.output_part[owner.output_part_pos++] = '\n';
+		owner.HardMask();		
+	}
+	bool NextSeq() const
+	{
+		return owner.NextSeqFastq();
+	}
+	bool FilterRead() const
+	{
+		return true;
+	}
+};
+class CFastqFilter::HardMaskFastaToFastaHelper
+{
+	CFastqFilter& owner;
+public:
+	HardMaskFastaToFastaHelper(CFastqFilter& owner) : owner(owner) {}
+	uint64 GetReqSize() const
+	{
+		return owner.seq_desc.read_header_end - owner.seq_desc.read_header_start + 1 +
+			owner.seq_desc.read_end - owner.seq_desc.read_start + 1;
+	}
+	void SendToOutBuf() const
+	{
+		memcpy(owner.output_part + owner.output_part_pos, owner.input_part + owner.seq_desc.read_header_start, owner.seq_desc.read_header_end - owner.seq_desc.read_header_start);
+		owner.output_part_pos += owner.seq_desc.read_header_end - owner.seq_desc.read_header_start;
+		owner.output_part[owner.output_part_pos++] = '\n';
+		owner.HardMask();	
+	}
+	bool NextSeq() const
+	{
+		return owner.NextSeqFasta();
+	}
+	bool FilterRead() const
+	{
+		return true;
+	}
+};
+// ***** EOF

=== added file 'kmc_tools/fastq_filter.h'
--- old/kmc_tools/fastq_filter.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/fastq_filter.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,106 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _FASTQ_FILTER_H
+#define _FASTQ_FILTER_H
+
+#include "config.h"
+#include "queues.h"
+
+#include "../kmc_api/kmc_file.h"
+
+//************************************************************************************************************
+// CFastqFilter - filter of reads
+//************************************************************************************************************
+class CFastqFilter
+{
+private:
+	CFilteringParams::FilterMode mode;
+	CPartQueue *input_part_queue, *filtered_part_queue;
+	CMemoryPool *pmm_fastq_reader;
+	CMemoryPool *pmm_fastq_filter;
+	CFilteringParams::file_type input_file_type, output_file_type;
+	CKMCFile& kmc_api;
+	uint64 output_part_size;
+
+	uchar* input_part;
+	uint64 input_part_size;
+	uint64 input_part_pos;
+	uchar* output_part;
+	uint64 output_part_pos;
+
+	std::vector<uint32> counters;
+	std::string read;
+	struct {
+		uint64 read_header_start;
+		uint64 read_header_end;
+		uint64 read_start;
+		uint64 read_end;
+		uint64 quality_header_start;
+		uint64 quality_header_end;
+		uint64 quality_start;
+		uint64 quality_end;
+		uint64 end;
+	}seq_desc;
+
+	uint32 trim_len; //for trim mode
+
+	bool use_float_value;
+	float f_max_kmers;
+	float f_min_kmers;
+	uint32 n_max_kmers;
+	uint32 n_min_kmers;
+	uint32 kmer_len;
+
+	template<class Helper> void ProcessImpl();
+
+
+
+	bool NextSeqFastq();
+	bool NextSeqFasta();
+	bool FilterRead();
+	bool FilterReadTrim();
+	void HardMask();
+public:
+	CFastqFilter(CFilteringParams& Params, CFilteringQueues& Queues, CKMCFile& kmc_api);
+	void Process();
+
+
+private: //Helpers classes for ProcessImpl
+
+	class FastqToFastqHelper;
+	class FastqToFastaHelper;
+	class FastaToFastaHelper;
+
+	class TrimFastqToFastqHelper;
+	class TrimFastqToFastaHelper;
+	class TrimFastaToFastaHelper;
+
+	class HardMaskFastqToFastqHelper;
+	class HardMaskFastqToFastaHelper;
+	class HardMaskFastaToFastaHelper;
+
+};
+
+//************************************************************************************************************
+// CWFastqFilter - wrapper for CFastqFilter class - for multithreading purposes
+//************************************************************************************************************
+class CWFastqFilter
+{
+	std::unique_ptr<CFastqFilter> ff;
+public:
+	CWFastqFilter(CFilteringParams& Params, CFilteringQueues& Queues, CKMCFile& kmc_api);
+	void operator()();
+};
+
+
+#endif
+
+// ***** EOF

=== added file 'kmc_tools/fastq_reader.cpp'
--- old/kmc_tools/fastq_reader.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/fastq_reader.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,373 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+
+#include <algorithm>
+#include <cstring>
+
+#include "defs.h"
+#include "fastq_reader.h"
+
+//************************************************************************************************************
+// CFastqReader	- reader class
+//************************************************************************************************************
+
+uint64 CFastqReader::OVERHEAD_SIZE = 1 << 16;
+
+//----------------------------------------------------------------------------------
+// Constructor of FASTA/FASTQ reader
+// Parameters:
+//    * _mm - pointer to memory monitor (to check the memory limits)
+CFastqReader::CFastqReader(CMemoryPool *_pmm_fastq, CFilteringParams::file_type _file_type, uint32 _gzip_buffer_size, uint32 _bzip2_buffer_size, int _kmer_len)
+{
+	pmm_fastq = _pmm_fastq;
+
+	file_type  = _file_type;
+	kmer_len = _kmer_len;
+	// Input file mode (default: uncompressed)
+	mode      = m_plain;
+
+	// Pointers to input files in various formats (uncompressed, gzip-compressed, bzip2-compressed)
+	in		  = NULL;
+	in_gzip   = NULL;
+	in_bzip2  = NULL;
+	bzerror   = BZ_OK;
+
+	// Size and pointer for the buffer
+	part_size = 1 << 23;
+	part      = NULL;
+
+	gzip_buffer_size  = _gzip_buffer_size;
+	bzip2_buffer_size = _bzip2_buffer_size;
+
+
+}
+
+//----------------------------------------------------------------------------------
+// Destructor - close the files
+CFastqReader::~CFastqReader()
+{
+	if(mode == m_plain)
+	{
+		if(in)
+			fclose(in);
+	}
+	else if(mode == m_gzip)
+	{
+		if(in_gzip)
+			gzclose(in_gzip);
+	}
+	else if(mode == m_bzip2)
+	{
+		if(in)
+		{
+			BZ2_bzReadClose(&bzerror, in_bzip2);
+			fclose(in);
+		}
+	}
+
+	if(part)
+		pmm_fastq->free(part);
+}
+
+//----------------------------------------------------------------------------------
+// Set the name of the file to process
+bool CFastqReader::SetNames(string _input_file_name)
+{
+	input_file_name = _input_file_name;
+
+	// Set mode according to the extension of the file name
+	if(input_file_name.size() > 3 && string(input_file_name.end()-3, input_file_name.end()) == ".gz")
+		mode = m_gzip;
+	else if(input_file_name.size() > 4 && string(input_file_name.end()-4, input_file_name.end()) == ".bz2")
+		mode = m_bzip2;
+	else
+		mode = m_plain;
+
+	return true;
+}
+
+//----------------------------------------------------------------------------------
+// Set part size of the buffer
+bool CFastqReader::SetPartSize(uint64 _part_size)
+{
+	if(in || in_gzip || in_bzip2)
+		return false;
+
+	if(_part_size < (1 << 20) || _part_size > (1 << 30))
+		return false;
+
+	part_size = _part_size;
+
+	return true;
+}
+
+//----------------------------------------------------------------------------------
+// Open the file
+bool CFastqReader::OpenFiles()
+{
+	if(in || in_gzip || in_bzip2)
+		return false;
+
+	// Uncompressed file
+	if(mode == m_plain)	
+	{
+		if((in = fopen(input_file_name.c_str(), "rb")) == NULL)
+			return false;
+	}
+	// Gzip-compressed file
+	else if(mode == m_gzip)
+	{
+		if((in_gzip = gzopen(input_file_name.c_str(), "rb")) == NULL)
+			return false;
+		gzbuffer(in_gzip, gzip_buffer_size);
+	}
+	// Bzip2-compressed file
+	else if(mode == m_bzip2)
+	{
+		in = fopen(input_file_name.c_str(), "rb");
+		if(!in)
+			return false;
+		setvbuf(in, NULL, _IOFBF, bzip2_buffer_size);
+		if((in_bzip2 = BZ2_bzReadOpen(&bzerror, in, 0, 0, NULL, 0)) == NULL)
+		{
+			fclose(in);
+			return false;
+		}
+	}
+	
+	// Reserve via PMM
+	pmm_fastq->reserve(part);
+
+	part_filled = 0;
+
+	return true;
+}
+
+//----------------------------------------------------------------------------------
+// Read a part of the file
+bool CFastqReader::GetPart(uchar *&_part, uint64 &_size)
+{	
+	if(!in && !in_gzip && !in_bzip2)
+		return false;
+
+	if(IsEof())
+		return false;
+	uint64 readed;
+	
+	// Read data
+	if(mode == m_plain)
+		readed = fread(part+part_filled, 1, part_size - part_filled, in);
+	else if(mode == m_gzip)
+		readed = gzread(in_gzip, part+part_filled, (int) (part_size - part_filled));
+	else if(mode == m_bzip2)
+		readed = BZ2_bzRead(&bzerror, in_bzip2, part+part_filled, (int) (part_size - part_filled));
+	else
+		readed = 0;				// Never should be here
+
+	int64 total_filled = part_filled + readed;
+	int64 i;
+
+	//Commented to finish in the next iteration to check if file is valid FASTA or FASTQ, related to #114
+	//if(IsEof())
+	//{
+	//	_part = part;
+	//	_size = total_filled;
+	//
+	//	part = nullptr;
+	//	return true;
+	//}
+	
+	// Look for the end of the last complete record in a buffer
+	if(file_type == CFilteringParams::file_type::fasta)			// FASTA files
+	{
+		// Looking for a FASTA record at the end of the area
+		i = total_filled - 1;
+		int64 start, end;
+		int64 line_start[4], line_end[4];
+		int readed_lines = 0;
+		bool success = false;
+		int k;
+		while (i >= 0 && readed_lines < 4)
+		{
+			GetFullLineFromEnd(start, end, part, i);
+
+			line_start[4 - readed_lines - 1] = start;
+			line_end[4 - readed_lines - 1] = end;
+			++readed_lines;
+
+			if (readed_lines >= 2)
+			{
+				k = 4 - readed_lines;
+				if (part[line_start[k]] == '>')
+				{
+					success = true;
+					break;
+
+				}
+			}
+		}
+		// Looking for a FASTQ record at the end of the area
+		if (!success)
+		{
+			cerr << "Error: Wrong input file!\n";
+			exit(1);
+		}
+
+		_part = part;
+		_size = line_end[k + 1];
+	}
+	else
+	{
+		i = total_filled - 1;
+		int64 start, end;
+		int64 line_start[8], line_end[8];
+		int readed_lines = 0;
+		bool success = false;
+		int k;
+		while (i >= 0 && readed_lines < 8)
+		{
+			GetFullLineFromEnd(start, end, part, i);
+			line_start[8 - readed_lines - 1] = start;
+			line_end[8 - readed_lines - 1] = end;
+			++readed_lines;
+
+			if (readed_lines >= 4)
+			{
+				k = 8 - readed_lines;
+				if (part[line_start[k]] == '@' && part[line_start[k + 2]] == '+')
+				{
+					if (part[line_start[k + 2] + 1] == '\n' || part[line_start[k + 2] + 1] == '\r')
+					{
+						success = true;
+						break;
+					}
+					if (line_start[k + 1] - line_start[k] == line_start[k + 3] - line_start[k + 2] &&
+						memcmp(part + line_start[k] + 1, part + line_start[k + 2] + 1, line_start[k + 3] - line_start[k + 2] - 1) == 0)
+					{
+						success = true;
+						break;
+					}
+				}
+			}
+		}
+
+		if (!success)
+		{
+			cerr << "Error: Wrong input file!\n";
+			exit(1);
+		}
+		_part = part;
+		_size = line_end[k + 3];
+	}
+	// Allocate new memory for the buffer
+
+	pmm_fastq->reserve(part);
+	copy(_part+_size, _part+total_filled, part);
+	part_filled = total_filled - _size;
+
+	return true;
+}
+
+//----------------------------------------------------------------------------------
+// Skip to next EOL from the current position in a buffer
+bool CFastqReader::SkipNextEOL(uchar *part, int64 &pos, int64 max_pos)
+{
+	int64 i;
+	for(i = pos; i < max_pos-2; ++i)
+		if((part[i] == '\n' || part[i] == '\r') && !(part[i+1] == '\n' || part[i+1] == '\r'))
+			break;
+
+	if(i >= max_pos-2)
+		return false;
+
+	pos = i+1;
+
+	return true;
+}
+
+void CFastqReader::GetFullLineFromEnd(int64& line_sart, int64& line_end, uchar* buff, int64& pos)
+{
+	while (pos >= 0 && buff[pos] != '\n' && buff[pos] != '\r')
+		--pos;
+	line_end = pos + 1;
+	while (pos >= 0 && (buff[pos] == '\n' || buff[pos] == '\r'))
+		--pos;
+	while (pos >= 0 && buff[pos] != '\n' && buff[pos] != '\r')
+		--pos;
+	line_sart = pos + 1;
+};
+//----------------------------------------------------------------------------------
+// Check whether there is an EOF
+bool CFastqReader::IsEof()
+{
+	if(mode == m_plain)
+		return feof(in) != 0;
+	else if(mode == m_gzip)
+		return gzeof(in_gzip) != 0;
+	else if(mode == m_bzip2)
+		return bzerror == BZ_STREAM_END;
+
+	return true;
+}
+
+
+
+//************************************************************************************************************
+// CWFastqReader - wrapper for multithreading purposes
+//************************************************************************************************************
+CWFastqReader::CWFastqReader(CFilteringParams &Params, CFilteringQueues &Queues)
+{	
+	pmm_fastq = Queues.pmm_fastq_reader;
+
+	input_files_queue = Queues.input_files_queue;
+	part_size		  = Params.fastq_buffer_size;
+	part_queue		  = Queues.input_part_queue;
+	file_type         = Params.input_file_type;
+	kmer_len		  = Params.kmer_len;
+
+	gzip_buffer_size  = Params.gzip_buffer_size;
+	bzip2_buffer_size = Params.bzip2_buffer_size;
+
+	fqr = nullptr;
+}
+
+//----------------------------------------------------------------------------------
+CWFastqReader::~CWFastqReader()
+{
+}
+
+//----------------------------------------------------------------------------------
+void CWFastqReader::operator()()
+{
+	uchar *part;
+	uint64 part_filled;
+	
+	while(input_files_queue->pop(file_name))
+	{
+		fqr = new CFastqReader(pmm_fastq, file_type, gzip_buffer_size, bzip2_buffer_size, kmer_len);
+		fqr->SetNames(file_name);
+		fqr->SetPartSize(part_size);
+
+		if(fqr->OpenFiles())
+		{
+			// Reading Fastq parts
+			while(fqr->GetPart(part, part_filled))
+				part_queue->push(part, part_filled);
+		}
+		else
+			cerr << "Error: Cannot open file " << file_name << "\n";
+		delete fqr;
+	}
+	part_queue->mark_completed();
+}
+
+// ***** EOF

=== added file 'kmc_tools/fastq_reader.h'
--- old/kmc_tools/fastq_reader.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/fastq_reader.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,99 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _FASTQ_READER_H
+#define _FASTQ_READER_H
+
+#include "defs.h"
+#include "queues.h"
+#include "config.h"
+#include <stdio.h>
+#include <iostream>
+
+#include "libs/zlib.h"
+#include "libs/bzlib.h"
+
+
+using namespace std;
+
+
+//************************************************************************************************************
+// FASTA/FASTQ reader class
+//************************************************************************************************************
+class CFastqReader {
+	typedef enum {m_plain, m_gzip, m_bzip2} t_mode;
+
+	CMemoryPool *pmm_fastq;
+
+	string input_file_name;
+	CFilteringParams::file_type file_type;
+	int kmer_len;
+	t_mode mode;
+
+	FILE *in;
+	gzFile_s *in_gzip;
+	BZFILE *in_bzip2;
+	int bzerror;
+
+	uint64 part_size;
+	
+	uchar *part;
+	uint64 part_filled;
+	
+	uint32 gzip_buffer_size;
+	uint32 bzip2_buffer_size;
+	
+
+	bool SkipNextEOL(uchar *part, int64 &pos, int64 max_pos);
+
+	void GetFullLineFromEnd(int64& line_sart, int64& line_end, uchar* buff, int64& pos);
+	
+	
+	bool IsEof();
+
+public:
+	CFastqReader(CMemoryPool *_pmm_fastq, CFilteringParams::file_type _file_type, uint32 _gzip_buffer_size, uint32 _bzip2_buffer_size, int _kmer_len);
+	~CFastqReader();
+
+	static uint64 OVERHEAD_SIZE;
+
+	bool SetNames(string _input_file_name);
+	bool SetPartSize(uint64 _part_size);
+	bool OpenFiles();
+	
+	bool GetPart(uchar *&_part, uint64 &_size);
+};
+
+//************************************************************************************************************
+// Wrapper for FASTA/FASTQ reader class - for multithreading purposes
+//************************************************************************************************************
+class CWFastqReader {
+	CMemoryPool *pmm_fastq;
+
+	CFastqReader *fqr;
+	string file_name;
+	uint64 part_size;
+	CInputFilesQueue *input_files_queue;
+	CPartQueue *part_queue;
+	CFilteringParams::file_type file_type;
+	uint32 gzip_buffer_size;
+	uint32 bzip2_buffer_size;
+	int kmer_len;
+
+public:
+	CWFastqReader(CFilteringParams &Params, CFilteringQueues &Queues);
+	~CWFastqReader();
+
+	void operator()();
+};
+
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/fastq_writer.cpp'
--- old/kmc_tools/fastq_writer.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/fastq_writer.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,69 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "fastq_writer.h"
+#include <iostream>
+using namespace std;
+
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+CFastqWriter::CFastqWriter(CFilteringParams& Params, CFilteringQueues& Queues)
+{
+	output_src			= Params.output_src;
+	filtered_part_queue = Queues.filtered_part_queue;
+	pmm_fastq_filter	= Queues.pmm_fastq_filter;
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+void CFastqWriter::Process()
+{
+	uchar* part;
+	uint64 size;
+	FILE* f = fopen(output_src.c_str(), "wb");
+	if (!f)
+	{
+		cerr << "cannot open file :" << output_src;
+		exit(1);
+	}
+	while (filtered_part_queue->pop(part, size))
+	{
+		if (fwrite(part, 1, size, f) != size)
+		{
+			cerr << "Error while writing to " << output_src << "\n";
+			exit(1);
+		}
+		pmm_fastq_filter->free(part);
+	}
+	fclose(f);
+}
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+CWFastqWriter::CWFastqWriter(CFilteringParams& Params, CFilteringQueues& Queues)
+	:writer(Params, Queues)
+{
+
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+void CWFastqWriter::operator()()
+{
+	writer.Process();
+}
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/fastq_writer.h'
--- old/kmc_tools/fastq_writer.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/fastq_writer.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,44 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _FASTQ_WRITER_H
+#define _FASTQ_WRITER_H
+
+#include "defs.h"
+#include "config.h"
+#include "queues.h"
+#include <string>
+
+//************************************************************************************************************
+// CFastqWriter - Writer of fastq/fasta file
+//************************************************************************************************************
+class CFastqWriter
+{
+	std::string output_src;
+	CPartQueue* filtered_part_queue;
+	CMemoryPool *pmm_fastq_filter;
+public:
+	CFastqWriter(CFilteringParams& Params, CFilteringQueues& Queues);
+	void Process();
+};
+
+//************************************************************************************************************
+// CWFastqWriter - wrapper for CFastqWriter class - for multithreading purposes
+//************************************************************************************************************
+class CWFastqWriter
+{
+	CFastqWriter writer;
+public:
+	CWFastqWriter(CFilteringParams& Params, CFilteringQueues& Queues);
+	void operator()();
+};
+
+#endif
+

=== added file 'kmc_tools/histogram_writer.h'
--- old/kmc_tools/histogram_writer.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/histogram_writer.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,109 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _HISTOGRAM_WRITER_H
+#define _HISTOGRAM_WRITER_H
+
+#include "defs.h"
+#include "config.h"
+#include <vector>
+#include <fstream>
+
+
+class CHistogramWriterBase
+{
+private:
+	std::string& file_src;
+	uint32 cutoff_max;
+	uint32 cutoff_min;
+	std::vector<uint64> counters;
+protected:
+	void Init()
+	{
+		counters.resize(cutoff_max + 1);
+	}
+	void ProcessCounter(uint32 counter)
+	{
+		if (counter >= cutoff_min && counter <= cutoff_max)
+			counters[counter]++;
+	}
+	void Finish()
+	{
+		std::ofstream file(file_src);
+		if (!file)
+		{
+			std::cerr << "Error: cannot open file: " << file_src << "\n";
+			exit(1);
+		}
+		for (uint32 i = cutoff_min; i <= cutoff_max; ++i)
+		{
+			file << i << "\t" << counters[i] << "\n";
+		}
+		file.close();
+	}
+	CHistogramWriterBase(std::string& file_src, uint32 cutoff_max, uint32 cutoff_min):
+		file_src(file_src),
+		cutoff_max(cutoff_max),
+		cutoff_min(cutoff_min)
+	{
+	}
+};
+
+template<typename KMCDB> class CHistogramWriter : public CHistogramWriterBase
+{
+	KMCDB& kmcdb;
+		
+public:
+	CHistogramWriter(KMCDB& kmcdb) :
+		CHistogramWriterBase(CConfig::GetInstance().output_desc.file_src, CConfig::GetInstance().output_desc.cutoff_max, CConfig::GetInstance().output_desc.cutoff_min),
+		kmcdb(kmcdb)
+	{
+
+	}
+	bool Process()
+	{
+		Init();
+		uint32 counter;
+		while (kmcdb.NextCounter(counter))
+		{
+			ProcessCounter(counter);
+		}
+		Finish();
+		return true;
+	}
+};
+
+
+class CHistogramWriterForTransform : public CHistogramWriterBase
+{
+public:
+	CHistogramWriterForTransform(CTransformOutputDesc& output_desc) :
+		CHistogramWriterBase(output_desc.file_src, output_desc.cutoff_max, output_desc.cutoff_min)
+	{
+
+	}
+
+	void Init()
+	{
+		CHistogramWriterBase::Init();
+	}
+
+	void PutCounter(uint32 counter)
+	{
+		ProcessCounter(counter);
+	}
+
+	void Finish()
+	{
+		CHistogramWriterBase::Finish();
+	}
+};
+
+#endif
\ No newline at end of file

=== added file 'kmc_tools/kmc1_db_reader.h'
--- old/kmc_tools/kmc1_db_reader.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/kmc1_db_reader.h	2021-04-26 21:01:12 +0000
@@ -0,0 +1,721 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _KMC1_DB_READER_H
+#define _KMC1_DB_READER_H
+#include "kmer.h"
+#include "defs.h"
+#include "config.h"
+#include "bundle.h"
+#include "kmc_header.h"
+#include "queues.h"
+#include <iostream>
+#include <cstring>
+#include <thread>
+#include <tuple>
+
+enum class KMCDBOpenMode { sequential, sorted, counters_only };
+
+class CSuffBufQueue
+{
+	using desc_t = std::tuple<uchar*, uint64>;
+	std::vector<desc_t> data;
+	int start = 0;
+	int end = 0;
+	bool is_completed = false;
+	bool forced_to_finish = false;
+	bool full;
+	mutable std::mutex mtx;
+
+	std::condition_variable cv_push;
+	std::condition_variable cv_pop;
+public:
+	CSuffBufQueue(uint32 n_recs, uint64 buff_size) :
+		is_completed(false), full(false)
+	{
+		data.resize(n_recs);
+		for (auto& e : data)
+		{
+			std::get<0>(e) = new uchar[buff_size];
+			std::get<1>(e) = 0;
+		}
+	}
+	~CSuffBufQueue()
+	{
+		for (auto& e : data)
+			delete std::get<0>(e);
+	}
+	bool pop(uchar* &suffix_buff, uint64& size)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv_pop.wait(lck, [this] { return start != end || full || is_completed || forced_to_finish; });
+
+		if (forced_to_finish)
+			return false;
+
+		if (is_completed && !full && start == end)
+			return false;
+
+		bool was_full = full;
+		std::swap(suffix_buff, std::get<0>(data[start]));
+		size = std::get<1>(data[start]);
+		start = (start + 1) % data.size();
+		full = false;
+		if (was_full)
+			cv_push.notify_all();
+		return true;
+	}
+
+	bool push(uchar* &suffix_buff, uint64 size)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv_push.wait(lck, [this] { return !full || forced_to_finish;  });
+
+		if (forced_to_finish)
+			return false;
+
+		bool was_empty = start == end;
+		std::swap(std::get<0>(data[end]), suffix_buff);
+		std::get<1>(data[end]) = size;
+
+		end = (end + 1) % data.size();
+		if (end == start)
+			full = true;
+
+		if (was_empty)
+			cv_pop.notify_all();
+
+		return true;
+	}
+
+	void mark_completed()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		is_completed = true;
+		cv_pop.notify_all();
+	}
+
+	void force_finish()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		forced_to_finish = true;
+		cv_pop.notify_all();
+		cv_push.notify_all();
+	}
+
+};
+
+
+class CSuffBuffReader
+{
+	uint64 suffix_left_to_read = 0;
+	uint64 suffix_buf_size = 0;
+	CSuffBufQueue& suff_buff_queue;
+	uchar* suff_buff;
+	FILE* suffix_file;
+	const std::string& suffix_file_name;
+public:
+	CSuffBuffReader(uint64 suffix_left_to_read, uint64 suffix_buff_size, CSuffBufQueue& suff_buff_queue, FILE* suffix_file, const std::string& suffix_file_name) :
+		suffix_left_to_read(suffix_left_to_read),
+		suffix_buf_size(suffix_buff_size),
+		suff_buff_queue(suff_buff_queue),
+		suffix_file(suffix_file),
+		suffix_file_name(suffix_file_name)
+	{
+		suff_buff = new uchar[suffix_buf_size];
+	}
+
+	~CSuffBuffReader()
+	{
+		delete[]  suff_buff;
+	}
+
+	void operator()()
+	{
+		while (true)
+		{
+			uint64 to_read = MIN(suffix_left_to_read, suffix_buf_size);
+			if (to_read == 0)
+			{
+				suff_buff_queue.mark_completed();
+				return;
+			}
+			uint64 readed = fread(suff_buff, 1, to_read, suffix_file);
+			if (readed != to_read)
+			{
+				std::cerr << "Error: some error while reading " << suffix_file_name << "\n";
+				exit(1);
+			}
+			suffix_left_to_read -= readed;
+			if (!suff_buff_queue.push(suff_buff, readed))
+				break;
+		}
+		suff_buff_queue.mark_completed();
+	}
+};
+
+//************************************************************************************************************
+// CKMC1DbReader - reader of KMC1 database
+//************************************************************************************************************
+template<unsigned SIZE>
+class CKMC1DbReader : public CInput<SIZE>
+{
+public:
+	CKMC1DbReader(const CKMC_header& header, const CInputDesc& desc, CPercentProgress& percent_progress, KMCDBOpenMode open_mode);
+
+	void NextBundle(CBundle<SIZE>& bundle) override
+	{
+		bool exists = circular_queue.pop(bundle.Data());
+
+		percent_progress.UpdateItem(progress_id, bundle.Size());
+
+		if (exists)
+			return;
+
+		percent_progress.Complete(progress_id);
+
+		this->finished = true;
+		this->building_thr.join();
+		this->suffix_builder_thread.join();
+	}
+
+	void IgnoreRest() override
+	{
+		if (this->finished)
+			return;
+		circular_queue.force_finish();
+		suffix_queue.force_finish();
+		this->suff_buff_queue->force_finish();
+		this->finished = true;
+		this->building_thr.join();
+		this->suffix_builder_thread.join();
+	}
+
+	~CKMC1DbReader()
+	{
+		suff_buff_reader_th.join();
+		delete suff_buff_reader;
+		delete suff_buff_queue;
+		if (prefix_file != nullptr)
+			fclose(prefix_file);
+		if (suffix_file != nullptr)
+			fclose(suffix_file);
+		delete[] prefix_buff;
+		delete[] suffix_buff;
+	}
+
+	bool NextKmerSequential(CKmer<SIZE>& kmer, uint32& counter)
+	{
+		if (next_kmer_sequential_bundle.Empty())
+		{
+			bool exists = circular_queue.pop(next_kmer_sequential_bundle);
+			if (!exists)
+			{
+				building_thr.join();
+				suffix_builder_thread.join();
+				percent_progress.Complete(progress_id);
+				return false;
+			}
+		}
+		kmer = next_kmer_sequential_bundle.TopKmer();
+		counter = next_kmer_sequential_bundle.TopCounter();
+		next_kmer_sequential_bundle.Pop();
+		percent_progress.UpdateItem(progress_id);
+		return true;
+	}
+
+	bool NextCounter(uint32& counter);
+
+private:
+	static const uint32 PREFIX_BUFF_BYTES = KMC1_DB_READER_PREFIX_BUFF_BYTES;
+	static const uint32 SUFFIX_BUFF_BYTES = KMC1_DB_READER_SUFFIX_BUFF_BYTES;
+	const CKMC_header& header;
+	uint32 counter_size;
+	uint64 total_kmers;
+
+	uint64 kmers_left_for_current_prefix;
+	uint64 total_kmers_left;
+
+	const CInputDesc& desc;
+
+	CPercentProgress& percent_progress;
+	KMCDBOpenMode open_mode;
+
+	uint32 progress_id;
+
+	FILE* prefix_file;
+	FILE* suffix_file;
+
+	uint32 record_size; //of suffix, in bytes	
+	CKmer<SIZE> current_prefix;
+
+	uint32 suffix_bytes;
+	uint64* prefix_buff = nullptr;
+	uchar* suffix_buff = nullptr;
+	uchar* record = nullptr;
+
+	uint32 prefix_bytes;
+	uint32 kmer_bytes;
+
+	uint64 prefix_buff_size;	
+	uint64 suffix_buff_size;
+	
+	uint64 prefix_buff_pos;
+	uint64 suffix_buff_pos;
+
+	uint64 prefix_left_to_read;	
+
+	std::string prefix_file_name;
+	std::string suffix_file_name;
+
+	uint64 suffix_number;
+
+	CCircularQueue<SIZE> circular_queue;
+	CCircularQueue<SIZE> suffix_queue;
+	std::thread building_thr;
+	std::thread suffix_builder_thread;
+
+	CBundleData<SIZE> suffix_bundle_get; CBundleData<SIZE> bundle_data;
+	CBundleData<SIZE> suffix_bundle_insert;
+	CBundleData<SIZE> next_kmer_sequential_bundle;
+	CBundleData<SIZE> next_counter_bundle;
+
+	void reload_pref_buff();
+
+	bool reload_suf_buff();
+
+	bool fill_bundle();
+	bool fill_suffix();
+
+	void open_files();
+
+	void allocate_buffers()
+	{
+		suffix_buff = new uchar[suffix_buff_size];
+		prefix_buff = new uint64[prefix_buff_size];
+	}
+
+	CSuffBuffReader* suff_buff_reader = nullptr;
+	CSuffBufQueue* suff_buff_queue = nullptr;
+	std::thread suff_buff_reader_th;
+};
+
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+
+template<unsigned SIZE> CKMC1DbReader<SIZE>::CKMC1DbReader(const CKMC_header& header, const CInputDesc& desc, CPercentProgress& percent_progress, KMCDBOpenMode open_mode) :
+	header(header),
+	counter_size(header.counter_size),
+	total_kmers(header.total_kmers),
+	desc(desc),
+	percent_progress(percent_progress),
+	open_mode(open_mode),
+	circular_queue(DEFAULT_CIRCULAL_QUEUE_CAPACITY),
+	suffix_queue(DEFAULT_CIRCULAL_QUEUE_CAPACITY)
+{
+	progress_id = percent_progress.RegisterItem(header.total_kmers);
+
+	prefix_file = suffix_file = nullptr;
+	suffix_bytes = (header.kmer_len - header.lut_prefix_len) / 4;
+	record_size = suffix_bytes + header.counter_size;
+	suffix_buff_size = SUFFIX_BUFF_BYTES / record_size * record_size;
+	prefix_buff_size = PREFIX_BUFF_BYTES / sizeof(uint64);
+
+	uint64 suffix_left_to_read = header.total_kmers * record_size;
+
+	if (suffix_left_to_read < suffix_buff_size)
+		suffix_buff_size = suffix_left_to_read;
+
+	
+
+	prefix_left_to_read = (1 << header.lut_prefix_len * 2) - 1;
+
+	if (prefix_left_to_read < prefix_buff_size)
+		prefix_buff_size = prefix_left_to_read;
+
+	prefix_bytes = (header.lut_prefix_len + 3) / 4;
+
+	kmer_bytes = prefix_bytes + suffix_bytes;
+
+	open_files();
+	allocate_buffers();
+
+	suff_buff_queue = new CSuffBufQueue(4, suffix_buff_size);
+	suff_buff_reader = new CSuffBuffReader(suffix_left_to_read, suffix_buff_size, *suff_buff_queue, suffix_file, suffix_file_name);
+
+	suff_buff_reader_th = std::thread(std::ref(*suff_buff_reader));
+
+	reload_pref_buff();
+	reload_suf_buff();
+
+	total_kmers_left = header.total_kmers;
+
+	current_prefix.clear();
+	suffix_number = 0;
+
+
+	//run threads
+	suffix_builder_thread = std::thread([this]{
+		while (fill_suffix())
+		{
+			if (!suffix_queue.push(suffix_bundle_insert))
+				break;
+		}
+		suffix_queue.mark_completed();
+	});
+
+	building_thr = std::thread([this]{
+
+		while (fill_bundle())
+		{
+			if (!circular_queue.push(bundle_data))
+				break;
+		}
+		circular_queue.mark_completed();
+	});
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKMC1DbReader<SIZE>::NextCounter(uint32& counter)
+{
+	if (next_counter_bundle.Empty())
+	{
+		bool exists = circular_queue.pop(next_counter_bundle);
+		if (!exists)
+		{
+			building_thr.join();
+			suffix_builder_thread.join();
+			percent_progress.Complete(progress_id);
+			return false;
+		}
+	}
+	counter = next_counter_bundle.TopCounter();
+	next_counter_bundle.Pop();
+	percent_progress.UpdateItem(progress_id);
+
+	return true;
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC1DbReader<SIZE>::reload_pref_buff()
+{
+	uint64 to_read = MIN(prefix_left_to_read, prefix_buff_size);
+	prefix_buff_pos = 0;
+	if (to_read == 0)
+	{
+		prefix_buff[0] = header.total_kmers;//guard		
+		return;
+	}
+
+	if (fread(prefix_buff, sizeof(uint64), to_read, prefix_file) != to_read)
+	{
+		std::cerr << "Error: some error while reading " << prefix_file_name << "\n";
+		exit(1);
+	}
+	prefix_left_to_read -= to_read;
+	if (to_read < prefix_buff_size)
+	{
+		prefix_buff[to_read] = header.total_kmers;//guard
+	}
+	kmers_left_for_current_prefix = prefix_buff[0];
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKMC1DbReader<SIZE>::reload_suf_buff()
+{
+	suffix_buff_pos = 0;
+	uint64 size;
+	if (!suff_buff_queue->pop(suffix_buff, size))
+		return false;
+
+	record = suffix_buff;
+	suffix_buff_pos = size / record_size;	
+	return true;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC1DbReader<SIZE>::open_files()
+{
+
+	suffix_file_name = desc.file_src + ".kmc_suf";
+
+	suffix_file = fopen(suffix_file_name.c_str(), "rb");
+	
+	if (!suffix_file)
+	{
+		std::cerr << "Error: cannot open file: " << suffix_file_name << "\n";
+		exit(1);
+	
+	}
+	
+	setvbuf(suffix_file, NULL, _IONBF, 0);
+
+	char marker[4];
+	if (fread(marker, 1, 4, suffix_file) != 4)
+	{
+		std::cerr << "Error: while reading start marker in file: " << suffix_file_name << "\n";
+		exit(1);
+	}
+
+	if (strncmp(marker, "KMCS", 4) != 0)
+	{
+		std::cerr << "Error: wrong start marker in file: " << suffix_file_name << "\n";
+		exit(1);
+	}
+
+
+	my_fseek(suffix_file, -4, SEEK_END);
+	if (fread(marker, 1, 4, suffix_file) != 4)
+	{
+		std::cerr << "Error: while reading end marker in file: " << suffix_file_name << "\n";
+		exit(1);
+	}
+
+	if (strncmp(marker, "KMCS", 4) != 0)
+	{
+		std::cerr << "Error: wrong end marker in file: " << suffix_file_name << "\n";
+		exit(1);
+	}
+	my_fseek(suffix_file, 4, SEEK_SET); //skip KMCS
+
+
+	prefix_file_name = desc.file_src + ".kmc_pre";
+
+	prefix_file = fopen(prefix_file_name.c_str(), "rb");
+	
+	if (!prefix_file)
+	{
+		std::cerr << "Error: cannot open file: " << prefix_file_name << "\n";
+		exit(1);
+	}
+	setvbuf(prefix_file, NULL, _IONBF, 0);
+
+	my_fseek(prefix_file, 4 + sizeof(uint64), SEEK_SET);//skip KMCP and first value as it must be 0
+
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKMC1DbReader<SIZE>::fill_suffix()
+{
+	uint64 local_suffix_buff_pos = suffix_buff_pos;
+	uchar* local_record = record;
+
+	uint32 bundle_size = suffix_bundle_insert.size;
+	uint32 bundle_pos = suffix_bundle_insert.insert_pos;
+
+	uint32 counter_bytes = this->counter_size;
+	uint32 suffix_bytes = this->suffix_bytes;
+
+	uint32 counter_mask = (uint32)((1ull << (counter_bytes << 3)) - 1);
+
+	bool little_endian = CConfig::GetInstance().IsLittleEndian();
+
+	if (local_suffix_buff_pos)
+	{
+		while (true)
+		{
+#ifdef DISABLE_FAST_LOAD
+			suffix_bundle_insert.kmers_with_counters[bundle_pos].kmer.load(local_record, suffix_bytes);
+#else
+			suffix_bundle_insert.kmers_with_counters[bundle_pos].kmer.load_fast(local_record, suffix_bytes, little_endian);
+#endif
+			CCounterBuilder::build_counter(suffix_bundle_insert.kmers_with_counters[bundle_pos].counter, local_record, counter_bytes, counter_mask, little_endian);
+
+			++bundle_pos;
+
+
+
+			if (!--local_suffix_buff_pos)
+			{
+				if (!reload_suf_buff())
+					break;
+				local_suffix_buff_pos = suffix_buff_pos;
+				local_record = record;
+			}
+
+			if (bundle_pos == bundle_size)
+				break;
+
+		}
+	}
+	suffix_buff_pos = local_suffix_buff_pos;
+	record = local_record;
+	suffix_bundle_insert.insert_pos = bundle_pos;
+	if (!suffix_bundle_insert.Empty())
+		return true;
+	return false;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKMC1DbReader<SIZE>::fill_bundle()
+{
+	CKmer<SIZE> kmer;
+	uint32 counter;
+
+	uint32 cutoff_min = desc.cutoff_min;
+	uint32 cutoff_max = desc.cutoff_max;
+	uint32 cutoff_range = cutoff_max - cutoff_min;
+
+	uint64 local_kmers_left_for_current_prefix = kmers_left_for_current_prefix;
+	uint64 local_total_kmers_left = total_kmers_left;
+
+	uint32 bundle_pos = 0;
+	uint32 size = bundle_data.size;
+
+
+
+	if (suffix_bundle_get.Empty())
+	{
+		suffix_queue.pop(suffix_bundle_get);
+	}
+
+
+	uint32 suffix_bundle_pos = suffix_bundle_get.get_pos;
+	uint32 suffix_bundle_size = suffix_bundle_get.insert_pos;
+
+	uint32 left_in_suffix_bundle = suffix_bundle_size - suffix_bundle_pos;
+
+	uint32 suffix_bytes = this->suffix_bytes;
+
+	const uint32 unroll = 8;
+	uint32 iter = MIN(suffix_bundle_size - suffix_bundle_pos, size - bundle_pos) / unroll;
+
+
+	//macro to force inlining
+#define FILL_BUNDLE_LOOP_UNROLL_CODE 														\
+	while (!local_kmers_left_for_current_prefix)			   								\
+	{														   								\
+	current_prefix.increment_at(suffix_bytes);			   									\
+	uint64 tmp = prefix_buff[prefix_buff_pos];			   									\
+	++prefix_buff_pos;									   									\
+	if (prefix_buff_pos >= prefix_buff_size)												\
+	reload_pref_buff();																		\
+																							\
+	local_kmers_left_for_current_prefix = prefix_buff[prefix_buff_pos] - tmp;				\
+	}																						\
+																							\
+	counter = suffix_bundle_get.kmers_with_counters[suffix_bundle_pos++].counter;			\
+	--local_kmers_left_for_current_prefix;													\
+																							\
+	if (counter - cutoff_min <= cutoff_range)												\
+	{																						\
+	kmer = suffix_bundle_get.kmers_with_counters[suffix_bundle_pos - 1].kmer;				\
+	kmer.set_prefix(current_prefix, suffix_bytes);											\
+	bundle_data.kmers_with_counters[bundle_pos].kmer = kmer;								\
+	bundle_data.kmers_with_counters[bundle_pos++].counter = counter;						\
+	}
+#ifdef WIN32 //VS is better if unroll by hand
+	for (uint32 ii = 0; ii < iter; ++ii)
+	{
+		FILL_BUNDLE_LOOP_UNROLL_CODE
+			FILL_BUNDLE_LOOP_UNROLL_CODE
+			FILL_BUNDLE_LOOP_UNROLL_CODE
+			FILL_BUNDLE_LOOP_UNROLL_CODE
+			FILL_BUNDLE_LOOP_UNROLL_CODE
+			FILL_BUNDLE_LOOP_UNROLL_CODE
+			FILL_BUNDLE_LOOP_UNROLL_CODE
+			FILL_BUNDLE_LOOP_UNROLL_CODE
+	}
+#else //g++ is better when it is not unrolled...
+	for(uint32 ii = 0 ; ii < iter * unroll ; ++ii)
+	{
+		FILL_BUNDLE_LOOP_UNROLL_CODE
+	}
+
+#endif
+
+	local_total_kmers_left -= unroll * iter;
+	left_in_suffix_bundle -= unroll * iter;
+
+	left_in_suffix_bundle += 1; //for simpler condition
+	if (bundle_pos == size)
+	{
+		kmers_left_for_current_prefix = local_kmers_left_for_current_prefix;
+		total_kmers_left = local_total_kmers_left;
+
+		suffix_bundle_get.get_pos = suffix_bundle_pos;
+		suffix_bundle_get.insert_pos = suffix_bundle_size;
+		bundle_data.insert_pos = bundle_pos;
+		return true;
+	}
+
+	while (local_total_kmers_left)
+	{
+		while (!local_kmers_left_for_current_prefix)
+		{
+			current_prefix.increment_at(suffix_bytes);
+			uint64 tmp = prefix_buff[prefix_buff_pos];
+			++prefix_buff_pos;
+			if (prefix_buff_pos >= prefix_buff_size)
+				reload_pref_buff();
+
+			local_kmers_left_for_current_prefix = prefix_buff[prefix_buff_pos] - tmp;
+		}
+
+
+		if (!--left_in_suffix_bundle)
+		{
+			suffix_queue.pop(suffix_bundle_get);
+			suffix_bundle_pos = suffix_bundle_get.get_pos;
+			suffix_bundle_size = suffix_bundle_get.insert_pos;
+			left_in_suffix_bundle = suffix_bundle_size - suffix_bundle_pos;
+		}
+
+		counter = suffix_bundle_get.kmers_with_counters[suffix_bundle_pos++].counter;
+
+		--local_kmers_left_for_current_prefix;
+		--local_total_kmers_left;
+
+		if (counter - cutoff_min <= cutoff_range)
+		{
+			kmer = suffix_bundle_get.kmers_with_counters[suffix_bundle_pos - 1].kmer;
+			kmer.set_prefix(current_prefix, suffix_bytes);
+
+			bundle_data.kmers_with_counters[bundle_pos].kmer = kmer;
+			bundle_data.kmers_with_counters[bundle_pos].counter = counter;
+
+			if (++bundle_pos == size)
+			{
+				kmers_left_for_current_prefix = local_kmers_left_for_current_prefix;
+				total_kmers_left = local_total_kmers_left;
+
+				suffix_bundle_get.get_pos = suffix_bundle_pos;
+				suffix_bundle_get.insert_pos = suffix_bundle_size;
+				bundle_data.insert_pos = bundle_pos;
+				return true;
+			}
+		}
+	}
+	kmers_left_for_current_prefix = local_kmers_left_for_current_prefix;
+	total_kmers_left = local_total_kmers_left;
+
+	suffix_bundle_get.get_pos = suffix_bundle_pos;
+	suffix_bundle_get.insert_pos = suffix_bundle_size;
+
+	bundle_data.insert_pos = bundle_pos;
+	if (!bundle_data.Empty())
+		return true;
+	return false;
+}
+
+
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/kmc1_db_writer.h'
--- old/kmc_tools/kmc1_db_writer.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/kmc1_db_writer.h	2021-04-26 21:01:12 +0000
@@ -0,0 +1,447 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _KMC1_DB_WRITER_H
+#define _KMC1_DB_WRITER_H
+
+#include "defs.h"
+#include "config.h"
+#include "queues.h"
+
+#include <string>
+#include <vector>
+
+//************************************************************************************************************
+// CKMC1SuffixFileWriter - thread for writing suffixes' parts
+//************************************************************************************************************
+class CKMC1SuffixFileWriter
+{
+public:
+	CKMC1SuffixFileWriter(CSufWriteQueue& input_queue, FILE* kmc_suf) :
+		input_queue(input_queue),
+		kmc_suf(kmc_suf)
+	{
+	}
+	void operator()()
+	{
+		uchar* buf;
+		uint32 size;
+		while (input_queue.pop(buf, size))
+		{
+			if (fwrite(buf, 1, size, kmc_suf) != size)
+			{
+				std::cerr << "Error while writing to kmc_suf file\n";
+				exit(1);
+			}
+			delete[] buf;
+		}
+	}
+private:
+	CSufWriteQueue& input_queue;
+	FILE* kmc_suf;
+};
+
+//************************************************************************************************************
+// CKMC1DbWriter - writer of KMC1 database
+//************************************************************************************************************
+template<unsigned SIZE> class CKMC1DbWriter
+{
+public:
+	CKMC1DbWriter(CBundle<SIZE>* bundle, COutputDesc& output_desc);
+	~CKMC1DbWriter();
+	bool Process();
+
+private:
+	static const uint32 PRE_BUFF_SIZE_BYTES = KMC1_DB_WRITER_PREFIX_BUFF_BYTES;
+	static const uint32 SUF_BUFF_SIZE_BYTES = KMC1_DB_WRITER_SUFFIX_BUFF_BYTES;
+
+	CConfig& config;
+	COutputDesc& output_desc;
+	CBundle<SIZE>* bundle = nullptr;
+	FILE* kmc_pre, *kmc_suf;
+	uint32 lut_prefix_len;
+	uint32 current_prefix;
+	uint32 counter_size;
+	uint32 pre_buff_size;
+	uint32 suf_buff_size;
+	uint64* pre_buff;
+	uchar* suf_buff;
+	uint64 added_kmers;
+	uint32 suffix_rec_bytes;
+	uint32 suf_pos, pre_pos;
+
+
+	void store_pre_buf();
+	void send_suf_buf_to_queue();
+	void start_writing();
+	inline void add_kmer(CKmer<SIZE>& kmer, uint32 counter);
+	void finish_writing();
+
+	template<typename T> void write_header_part(T data);
+	void calc_lut_prefix_len();
+
+
+	CCircularQueue<SIZE> bundles_queue;
+	CSufWriteQueue suf_buf_queue;
+
+
+	//for simple and transform operations
+	std::thread preparing_thread;
+	CKMC1SuffixFileWriter* suffix_writer = nullptr;
+	std::thread suf_buf_writing_thread;
+public:
+	void MultiOptputInit();
+	void MultiOptputAddResultPart(COutputBundle<SIZE>& bundle);
+	void MultiOptputAddResultPart(CBundle<SIZE>& bundle);
+	void MultiOptputFinish();
+
+};
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template <unsigned SIZE> CKMC1DbWriter<SIZE>::CKMC1DbWriter(CBundle<SIZE>* bundle, COutputDesc& output_desc) :
+config(CConfig::GetInstance()),
+output_desc(output_desc),
+bundle(bundle),
+bundles_queue(DEFAULT_CIRCULAL_QUEUE_CAPACITY)
+{
+	kmc_pre = NULL;
+	kmc_suf = NULL;
+	pre_buff = NULL;
+	suf_buff = NULL;
+	std::string kmc_pre_file_name = output_desc.file_src + ".kmc_pre";
+	std::string kmc_suf_file_name = output_desc.file_src + ".kmc_suf";
+
+	kmc_pre = fopen(kmc_pre_file_name.c_str(), "wb");
+	
+	if (!kmc_pre)
+	{
+		std::cerr << "Error: cannot open file : " << kmc_pre_file_name << "\n";
+		exit(1);
+	}
+
+	setvbuf(kmc_pre, NULL, _IONBF, 0);
+
+	kmc_suf = fopen(kmc_suf_file_name.c_str(), "wb");
+
+	if (!kmc_suf)
+	{
+		fclose(kmc_pre);
+		std::cerr << "Error: cannot open file : " << kmc_suf_file_name << "\n";
+		exit(1);
+	}
+	setvbuf(kmc_suf, NULL, _IONBF, 0);
+
+	setvbuf(kmc_pre, NULL, _IONBF, 0);
+	setvbuf(kmc_suf, NULL, _IONBF, 0);
+	// Calculate LUT size
+
+
+
+	calc_lut_prefix_len();
+
+	counter_size = MIN(BYTE_LOG(output_desc.counter_max), BYTE_LOG(output_desc.cutoff_max));
+	if (output_desc.counter_value)
+		counter_size = BYTE_LOG(output_desc.counter_value);
+	suffix_rec_bytes = (config.kmer_len - lut_prefix_len) / 4 + counter_size;
+	current_prefix = 0;
+	added_kmers = 0;
+	pre_buff_size = PRE_BUFF_SIZE_BYTES / sizeof(uint64);
+	suf_buff_size = SUF_BUFF_SIZE_BYTES / suffix_rec_bytes;
+	suf_pos = pre_pos = 0;
+
+	pre_buff = new uint64[pre_buff_size];
+	pre_buff[pre_pos++] = 0;
+	suf_buff = new uchar[suf_buff_size * suffix_rec_bytes];
+
+
+	suf_buf_queue.init(suf_buff_size * suffix_rec_bytes, SUFFIX_WRITE_QUEUE_CAPACITY);
+
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKMC1DbWriter<SIZE>::Process()
+{
+
+	start_writing();
+
+	//Converts bundles to output buffers, suffix buffer is placed to another queue and write in separate thread (suffix_writer)
+	preparing_thread = std::thread([this]{
+		CBundleData<SIZE> bundle_data;
+		while (bundles_queue.pop(bundle_data))
+		{
+			while (!bundle_data.Empty())
+			{
+				add_kmer(bundle_data.TopKmer(), bundle_data.TopCounter());
+				bundle_data.Pop();
+			}
+		}
+		suf_buf_queue.push(suf_buff, suffix_rec_bytes * suf_pos);
+		suf_buf_queue.mark_completed();
+	});
+
+
+
+	suffix_writer = new CKMC1SuffixFileWriter(suf_buf_queue, kmc_suf);
+	suf_buf_writing_thread = std::thread(std::ref(*suffix_writer));
+
+#ifdef ENABLE_LOGGER
+	CTimer timer;
+
+#endif
+	while (!bundle->Finished())
+	{
+#ifdef ENABLE_LOGGER
+		timer.start();
+#endif
+		bundles_queue.push(bundle->Data());
+#ifdef ENABLE_LOGGER
+		CLoger::GetLogger().log_operation("dodawanie do kolejki wyjsciowej bundla", this, timer.get_time());
+#endif
+	}
+
+	bundles_queue.mark_completed();
+
+	preparing_thread.join();
+	suf_buf_writing_thread.join();
+
+	finish_writing();
+
+	delete suffix_writer;
+	return true;
+}
+
+
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::MultiOptputInit()
+{
+	start_writing();
+	//Converts bundles to output buffers, suffix buffer is placed to another queue and write in separate thread (suffix_writer)
+	preparing_thread = std::thread([this]{
+		CBundleData<SIZE> bundle_data;
+		while (bundles_queue.pop(bundle_data))
+		{
+			while (!bundle_data.Empty())
+			{
+				add_kmer(bundle_data.TopKmer(), bundle_data.TopCounter());
+				bundle_data.Pop();
+			}
+		}
+		suf_buf_queue.push(suf_buff, suffix_rec_bytes * suf_pos);
+		suf_buf_queue.mark_completed();
+	});
+
+	suffix_writer = new CKMC1SuffixFileWriter(suf_buf_queue, kmc_suf);
+	suf_buf_writing_thread = std::thread(std::ref(*suffix_writer));
+}
+
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::MultiOptputAddResultPart(COutputBundle<SIZE>& bundle)
+{
+	bundles_queue.push(bundle.Data());
+}
+
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::MultiOptputAddResultPart(CBundle<SIZE>& bundle)
+{
+	bundles_queue.push(bundle.Data());
+}
+
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::MultiOptputFinish()
+{
+	bundles_queue.mark_completed();
+	preparing_thread.join();
+	suf_buf_writing_thread.join();
+	delete suffix_writer;
+	finish_writing();
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CKMC1DbWriter<SIZE>::~CKMC1DbWriter()
+{
+	delete[] suf_buff;
+	delete[] pre_buff;
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template <unsigned SIZE> template <typename T> void CKMC1DbWriter<SIZE>::write_header_part(T data)
+{
+	for (uint32 i = 0; i < sizeof(T); ++i)
+	{
+		char c = (data >> (i << 3)) & 0xff;
+		if (putc(c, kmc_pre) == EOF)
+		{
+			std::cerr << "Error while writing header of kmc1\n";
+			exit(1);
+		}
+	}
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::start_writing()
+{
+	if (fwrite("KMCP", 1, 4, kmc_pre) != 4)
+	{
+		std::cerr << "Error while writing starting KMCP marker";
+		exit(1);
+	}
+	if (fwrite("KMCS", 1, 4, kmc_suf) != 4)
+	{
+		std::cerr << "Error while writing starting KMCS marker";
+		exit(1);
+	}
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::finish_writing()
+{
+	uint32 max_prefix = (1 << 2 * lut_prefix_len);
+	while (current_prefix < max_prefix - 1)
+	{
+		pre_buff[pre_pos++] = added_kmers;
+		++current_prefix;
+		if (pre_pos == pre_buff_size)
+			store_pre_buf();
+	}
+	store_pre_buf();
+	send_suf_buf_to_queue();
+
+	//store header
+	write_header_part(config.kmer_len);
+	write_header_part(config.headers.front().mode);
+	write_header_part(counter_size);
+	write_header_part(lut_prefix_len);
+	write_header_part(output_desc.cutoff_min);
+	write_header_part(output_desc.cutoff_max);
+	write_header_part(added_kmers);
+
+	bool both_stands = false;
+	for (auto& input : config.headers)
+		both_stands = both_stands || input.both_strands; //if any input database is in both strands, output is also in both strands
+
+	write_header_part(!both_stands);
+
+
+	for (uint32 i = 0; i < 31; ++i)
+		write_header_part(uchar(0));
+
+	write_header_part((uint32)64);
+
+
+	if (fwrite("KMCP", 1, 4, kmc_pre) != 4)
+	{
+		std::cerr << "Error while writing end KMCP marker";
+		exit(1);
+	}
+	if (fwrite("KMCS", 1, 4, kmc_suf) != 4)
+	{
+		std::cerr << "Error while writing end KMCS marker";
+		exit(1);
+	}
+	fclose(kmc_pre);
+	fclose(kmc_suf);
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::add_kmer(CKmer<SIZE>& kmer, uint32 counter)
+{
+	//if specific counter value is set use it as counter value (set_counts operation), do not check if counter is valid in term of cutoffs and counter max
+	if (output_desc.counter_value)
+		counter = static_cast<uint32>(output_desc.counter_value);
+	else
+	{
+		if (counter < output_desc.cutoff_min || counter > output_desc.cutoff_max)
+			return;
+		if (counter > output_desc.counter_max)
+			counter = output_desc.counter_max;
+	}
+	uint64 kmer_prefix = kmer.remove_suffix((config.kmer_len - lut_prefix_len) * 2);
+	while (current_prefix < kmer_prefix)
+	{
+		pre_buff[pre_pos++] = added_kmers;
+		++current_prefix;
+		if (pre_pos == pre_buff_size)
+			store_pre_buf();
+	}
+	uchar* rec = suf_buff + suf_pos * suffix_rec_bytes;
+
+	kmer.store(rec, suffix_rec_bytes - counter_size);
+	for (uint32 i = 0; i < counter_size; ++i)
+		*rec++ = counter >> (i << 3);
+	++suf_pos;
+	if (suf_pos == suf_buff_size)
+		send_suf_buf_to_queue();
+	++added_kmers;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::store_pre_buf()
+{
+	if (fwrite(pre_buff, sizeof(uint64), pre_pos, kmc_pre) != pre_pos)
+	{
+		std::cerr << "Error while writing to kmc_pre file\n";
+		exit(1);
+	}
+	pre_pos = 0;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::send_suf_buf_to_queue()
+{
+	suf_buf_queue.push(suf_buff, suffix_rec_bytes * suf_pos);
+	suf_pos = 0;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC1DbWriter<SIZE>::calc_lut_prefix_len()
+{
+
+
+	std::vector<uint32> best_lut_prefix_len_inputs(config.headers.size());
+
+
+	for (uint32 i = 0; i < config.headers.size(); ++i)
+	{
+		uint32 best_lut_prefix_len = 0;
+		uint64 best_mem_amount = 1ull << 62;
+		for (lut_prefix_len = 1; lut_prefix_len < 16; ++lut_prefix_len)
+		{
+			uint32 suffix_len = config.headers[i].kmer_len - lut_prefix_len;
+			if (suffix_len % 4)
+				continue;
+
+			uint64 suf_mem = config.headers[i].total_kmers * suffix_len / 4;
+			uint64 lut_mem = (1ull << (2 * lut_prefix_len)) * sizeof(uint64);
+
+			if (suf_mem + lut_mem < best_mem_amount)
+			{
+				best_lut_prefix_len = lut_prefix_len;
+				best_mem_amount = suf_mem + lut_mem;
+			}
+		}
+		best_lut_prefix_len_inputs[i] = best_lut_prefix_len;
+	}
+
+	//TODO: poki co jako lut size biore najwieszy z najlepszych dla baz wejsciowych
+	lut_prefix_len = *std::max_element(best_lut_prefix_len_inputs.begin(), best_lut_prefix_len_inputs.end());
+}
+
+#endif
+
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/kmc2_db_reader.h'
--- old/kmc_tools/kmc2_db_reader.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/kmc2_db_reader.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,2085 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _KMC2_DB_READER_H
+#define _KMC2_DB_READER_H
+
+#include "config.h"
+#include "bundle.h"
+#include "queues.h"
+#include <vector>
+#include <mutex>
+#include <memory>
+#include <tuple>
+//#include <stack>
+#include <queue>
+
+#include <algorithm>
+#include <condition_variable>
+
+//Forward declaration
+template<unsigned SIZE> class CKMC2DbReaderSorted;
+
+template<unsigned SIZE> class CBin;
+
+
+
+struct CBinBuff //must be moveble
+{
+	uchar* buf;
+	uint32 size;
+
+	CBinBuff() :
+		buf(nullptr), size(0)
+	{
+	}
+
+	CBinBuff(uchar* buf, uint32 size) :buf(buf), size(size)
+	{
+
+	}
+
+#ifdef WIN32
+	CBinBuff& operator=(CBinBuff&& rhs) throw()
+#else
+	CBinBuff& operator=(CBinBuff&& rhs) noexcept
+#endif
+	{
+		if (this != &rhs)
+		{
+			buf = rhs.buf;
+			size = rhs.size;
+			rhs.buf = nullptr;
+			rhs.size = 0;
+		}
+		return *this;
+	}
+
+#ifdef WIN32
+	CBinBuff(CBinBuff&& rhs) throw()
+#else
+	CBinBuff(CBinBuff&& rhs) noexcept
+#endif
+	{
+		buf = rhs.buf;
+		size = rhs.size;
+		rhs.buf = nullptr;
+		rhs.size = 0;
+	}
+
+	CBinBuff(const CBinBuff&) = delete;
+	CBinBuff& operator=(const CBinBuff&) = delete;
+};
+
+template<unsigned SIZE> class CBinBufProvider
+{
+	std::vector<CBinBuff> internal_bufs;
+	uchar *buf_bins, *buf_internal;
+	uint32 bins_left_to_read = 0;
+	uint32 max_bin_bytes;
+	uint32 rec_size;
+
+	using desc_t = std::tuple<uint64, uint64, bool>;//current_kmer, last_kmer, is_empty
+	using to_read_t = std::tuple<uint32, uint64, uchar*, uint32>;//bin_id, file_pos, buffer to read, size to read
+
+	std::vector<desc_t> desc;
+	//std::stack<to_read_t, std::vector<to_read_t>> to_read;
+	std::queue<to_read_t, std::list<to_read_t>> to_read;
+
+	mutable std::mutex mtx;
+	std::condition_variable cv_pop;
+	std::condition_variable cv_get_next_to_read;
+
+	bool forced_to_finish = false;
+
+public:
+	void init(std::vector<CBin<SIZE>>& bins);
+
+	void pop(uint32 bin_id, CBinBuff& bin_buf)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv_pop.wait(lck, [this, bin_id]{return !std::get<2>(desc[bin_id]); });
+
+		std::swap(bin_buf, internal_bufs[bin_id]);
+		std::get<2>(desc[bin_id]) = true;
+
+		uint64 kmers_left = std::get<1>(desc[bin_id]) - std::get<0>(desc[bin_id]);
+		if (kmers_left)
+		{
+			uint32 kmers_to_read = (uint32)MIN(kmers_left, max_bin_bytes / rec_size);
+			internal_bufs[bin_id].size = kmers_to_read * rec_size;
+			bool was_empty = to_read.empty();
+			to_read.push(std::make_tuple(bin_id, 4 + std::get<0>(desc[bin_id]) * rec_size, internal_bufs[bin_id].buf, internal_bufs[bin_id].size));
+			std::get<0>(desc[bin_id]) += kmers_to_read;
+			if (was_empty)
+				cv_get_next_to_read.notify_all();
+		}
+		else
+		{
+			--bins_left_to_read;
+			if (!bins_left_to_read)
+				cv_get_next_to_read.notify_all();
+		}
+	}
+
+	void notify_bin_filled(uint32 bin_id)
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		std::get<2>(desc[bin_id]) = false;
+		cv_pop.notify_all();
+	}
+
+	bool get_next_to_read(uint32& bin_id, uint64& file_pos, uchar* &buf, uint32& size)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv_get_next_to_read.wait(lck, [this]{return !to_read.empty() || !bins_left_to_read || forced_to_finish; });
+		if (forced_to_finish || (to_read.empty() && !bins_left_to_read))
+			return false;
+
+		std::tie(bin_id, file_pos, buf, size) = to_read.front();
+		to_read.pop();
+		return true;
+	}
+
+	void force_to_finish()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		forced_to_finish = true;
+		cv_get_next_to_read.notify_all();
+	}
+
+	~CBinBufProvider()
+	{
+		delete[] buf_bins;
+		delete[] buf_internal;
+	}
+};
+
+template<unsigned SIZE>
+class CSufBinReader
+{
+	CBinBufProvider<SIZE>& bin_provider;
+	FILE* suf_file;
+public:
+	CSufBinReader(CBinBufProvider<SIZE>& bin_provider, FILE* suf_file) :
+		bin_provider(bin_provider),
+		suf_file(suf_file)
+	{
+
+	}
+	void operator()()
+	{
+		uint32 bin_id;
+		uint64 file_pos;
+		uchar* buf;
+		uint32 size;
+
+		while (bin_provider.get_next_to_read(bin_id, file_pos, buf, size))
+		{
+			my_fseek(suf_file, file_pos, SEEK_SET);
+			if (fread(buf, 1, size, suf_file) != size)
+			{
+				std::cerr << "Error while reading suffix file\n";
+				exit(1);
+			}
+			bin_provider.notify_bin_filled(bin_id);
+		}
+	}
+};
+
+template<unsigned SIZE> class CKmerPQ;
+template<unsigned SIZE> class CBin
+{
+public:
+	CBin(uint32 bin_id, uint64* LUT, CKMC2DbReaderSorted<SIZE>& kmc2_db);
+	bool NextKmer(CKmer<SIZE>& kmer, uint32& counter);
+
+	uint64 get_kmer_number_start()
+	{
+		return kmer_number_start;
+	}
+
+	uint64 get_kmers_left()
+	{
+		return kmers_left;
+	}
+
+	uint64 get_kmer_number_end()
+	{
+		return kmer_number_end;
+	}
+
+	uint32 get_record_size()
+	{
+		return record_size;
+	}
+
+	void set_bin_buff(CBinBuff&& _bin_buff)
+	{
+		bin_buff = std::move(_bin_buff);
+		pos = bin_buff.size; //force reload
+	}
+
+
+#ifdef WIN32
+	//Because VS2013 does generate default move ctor here
+	CBin(CBin&& o) throw() :
+		bin_id(o.bin_id),
+		bin_buff(std::move(o.bin_buff)),
+		LUT(o.LUT),
+		pos(o.pos),
+		bin_provider(o.bin_provider),
+		kmc2_db(o.kmc2_db),
+		kmer_number_start(o.kmer_number_start), kmer_number_end(o.kmer_number_end),
+		kmers_left(o.kmers_left),
+		kmers_left_for_current_prefix(o.kmers_left_for_current_prefix),
+		kmer_bytes(o.kmer_bytes), prefix_bytes(o.prefix_bytes), suffix_bytes(o.suffix_bytes), counter_size(o.counter_size), record_size(o.record_size),
+		cutoff_range(o.cutoff_range), cutoff_min(o.cutoff_min),
+		prefix_pos(o.prefix_pos),
+		prefix(o.prefix),
+		max_prefix(o.max_prefix),
+		is_little_endian(o.is_little_endian),
+		counter_mask(o.counter_mask)
+	{
+
+	}
+#else
+	//g++ generate here move ctor automatically
+#endif
+
+
+
+
+private:
+	uint32 bin_id;
+	CBinBuff bin_buff;
+	uint64* LUT;
+	uint32 pos = 0;
+	CBinBufProvider<SIZE>& bin_provider;
+	CKMC2DbReaderSorted<SIZE>& kmc2_db;
+	void reload_suf_buf();
+	uint64 kmer_number_start, kmer_number_end;
+	uint64 kmers_left;
+	uint64 kmers_left_for_current_prefix;
+	uint32 kmer_bytes, prefix_bytes, suffix_bytes, counter_size, record_size;
+	uint32 cutoff_range, cutoff_min;
+	uint64 prefix_pos = 0;
+	CKmer<SIZE> prefix;
+	uint64 max_prefix;
+	bool is_little_endian;
+	uint32 counter_mask;
+
+
+	friend class CKmerPQ<SIZE>;
+};
+
+
+template<unsigned SIZE> void CBinBufProvider<SIZE>::init(std::vector<CBin<SIZE>>& bins)
+{
+	uint64 start, end;
+	uint64 needed_mem = 0;
+	rec_size = bins.front().get_record_size();
+	max_bin_bytes = SINGLE_BIN_BUFF_SIZE_FOR_DB2_READER / rec_size * rec_size;
+	uint32 mem;
+
+	internal_bufs.resize(bins.size());
+	for (uint32 i = 0; i < bins.size(); ++i)
+	{
+		auto& b = bins[i];
+		start = b.get_kmer_number_start();
+		end = b.get_kmer_number_end();
+		mem = (uint32)MIN((end - start) * rec_size, max_bin_bytes);
+
+		internal_bufs[i] = CBinBuff(nullptr, mem);
+		desc.push_back(std::make_tuple(start, end, true));
+		needed_mem += mem;
+	}
+
+	bins_left_to_read = (uint32)bins.size();
+	buf_bins = new uchar[needed_mem];
+	buf_internal = new uchar[needed_mem];
+
+	internal_bufs[0].buf = buf_internal;
+
+	uchar* ptr = buf_bins;
+	bins[0].set_bin_buff(CBinBuff(ptr, internal_bufs[0].size));
+
+	for (uint32 i = 1; i < internal_bufs.size(); ++i)
+	{
+		internal_bufs[i].buf = internal_bufs[i - 1].buf + internal_bufs[i - 1].size;
+		ptr += internal_bufs[i - 1].size;
+		bins[i].set_bin_buff(CBinBuff(ptr, internal_bufs[i].size));
+	}
+
+	for (uint32 bin_id = 0; bin_id < desc.size(); ++bin_id)
+	{
+		uint64 kmers_left = std::get<1>(desc[bin_id]) - std::get<0>(desc[bin_id]);
+		if (kmers_left)
+		{
+			uint32 kmers_to_read = (uint32)MIN(kmers_left, max_bin_bytes / rec_size);
+			internal_bufs[bin_id].size = kmers_to_read * rec_size;
+			to_read.push(std::make_tuple(bin_id, 4 + std::get<0>(desc[bin_id]) * rec_size, internal_bufs[bin_id].buf, internal_bufs[bin_id].size));
+			std::get<0>(desc[bin_id]) += kmers_to_read;
+		}
+		else
+		{
+			--bins_left_to_read;
+		}
+	}
+}
+
+//************************************************************************************************************
+// CKmerPQ - Priority Queue of k-mers - binary heap. K-mers from bins are processed by this priority queue
+//************************************************************************************************************
+template<unsigned SIZE> class CKmerPQ
+{
+public:
+	CKmerPQ(uint32 _no_of_bins);
+	inline void init_add(CBin<SIZE>* bin);
+
+	void Process(CCircularQueue<SIZE>& output_queue);
+
+	inline void reset();
+
+private:
+	using elem_t = std::pair<CKmer<SIZE>, uint32>;//kmer, desc_id
+	using desc_t = std::pair<CBin<SIZE>*, uint32>;//bin, counter
+	std::vector<elem_t> elems;
+	std::vector<desc_t> descs;
+	uint32 pos, desc_pos;
+};
+
+//************************************************************************************************************
+// CMergerParent - Merger of k-mers produced by CMergerChilds
+//************************************************************************************************************
+template<unsigned SIZE> class CMergerParent
+{
+public:
+	CMergerParent(std::vector<CCircularQueue<SIZE>*>& input_queues, CCircularQueue<SIZE>& output_queue, uint32 n_subthreads);
+	void operator()();
+
+private:
+	void Process2Inputs();
+	void ProcessMoreInputs();
+
+	uint32 LastLowerPlus1(CBundleData<SIZE>& b, uint32 start, uint32 end, CKmer<SIZE> kmer);
+	void ProcessWithSubthreads();
+
+	std::vector<CBundleData<SIZE>> input_bundles;
+	std::vector<CCircularQueue<SIZE>*>& input_queues;
+
+	CBundleData<SIZE> output_bundle;
+	CCircularQueue<SIZE>& output_queue;
+	uint32 n_subthreads;
+};
+
+//************************************************************************************************************
+// CMergerChild - Merger of k-mers from bins
+//************************************************************************************************************
+template<unsigned SIZE> class CMergerChild
+{
+	using bin_iter = typename std::vector<CBin<SIZE>>::iterator;
+public:
+	CMergerChild(bin_iter begin, bin_iter end, CCircularQueue<SIZE>& output_queue);
+	void operator()();
+
+private:
+	std::vector<std::reference_wrapper<CBin<SIZE>>> bins;
+	CCircularQueue<SIZE>& output_queue;
+};
+
+//************************************************************************************************************
+// CKMC2DbReaderSorted - Produce k-mers in sorted order from KMC2 database
+//************************************************************************************************************
+template<unsigned SIZE> class CKMC2DbReaderSorted
+{
+public:
+	CKMC2DbReaderSorted(const CKMC_header& header, const CInputDesc& desc);
+
+	void NextBundle(CBundle<SIZE>& bundle, bool& finished);
+
+	void IgnoreRest();
+
+	~CKMC2DbReaderSorted();
+
+private:
+	//void get_suf_buf_part(uchar* &buf, uint64 start, uint32 size);
+
+	const CKMC_header& header;
+	const CInputDesc& desc;
+	uint64* LUTS = nullptr;
+	uint32 lut_size = 0;
+	uint32 suffix_bytes;
+	uint32 record_size;
+	FILE* kmc_suf;
+
+	friend class CBin<SIZE>;
+	std::vector<CBin<SIZE>> bins;
+	CBinBufProvider<SIZE> bin_provider;
+
+	CSufBinReader<SIZE>* suf_bin_reader;
+	std::thread suf_bin_reader_th;
+
+
+	uint32 n_child_threads;
+	uint32 n_parent_threads;
+
+	CMergerParent<SIZE>* parent = nullptr;
+	std::thread parent_thread;
+
+	CCircularQueue<SIZE>* output_queue;
+	std::vector<CCircularQueue<SIZE>*> childs_parent_queues;
+
+	std::vector<CMergerChild<SIZE>*> childs;
+	std::vector<std::thread> childs_threads;
+
+	//mutable std::mutex mtx;
+};
+
+//************************************************************************************************************
+// CKCM2DbReaderSeqCounter_Base - Base class for classes to access k-mers one by one (not sorted) or 
+// for counters only from KMC2 database
+//************************************************************************************************************
+template <unsigned SIZE>
+class CKCM2DbReaderSeqCounter_Base
+{
+protected:
+	CKCM2DbReaderSeqCounter_Base(const CKMC_header& header, const CInputDesc& desc);
+	~CKCM2DbReaderSeqCounter_Base();
+
+	void open_files();
+	bool reload_suf_buff();
+
+	static const uint32 PREFIX_BUFF_BYTES = KMC2_DB_READER_PREFIX_BUFF_BYTES;
+	static const uint32 SUFFIX_BUFF_BYTES = KMC2_DB_READER_SUFFIX_BUFF_BYTES;
+
+	const CKMC_header& header;
+	const CInputDesc& desc;
+
+	uint32 suffix_bytes;
+	uint32 record_size; //of suffix, in bytes
+	uint64 suffix_buff_size, suffix_buff_pos, suffix_left_to_read;
+	uint64 prefix_buff_size, prefix_buff_pos, prefix_left_to_read;
+	uint64 suffix_number;
+
+	uint32 kmer_bytes, prefix_bytes;
+
+	uchar* suffix_buff = nullptr;
+
+	FILE* suffix_file;
+	std::string suffix_file_name;
+};
+
+
+//************************************************************************************************************
+// CKMC2DbReaderSequential - Produce k-mers sequentialy from KMC2 database (they are not sorted!)
+//************************************************************************************************************
+template<unsigned SIZE>
+class CKMC2DbReaderSequential : public CKCM2DbReaderSeqCounter_Base<SIZE>
+{
+public:
+	CKMC2DbReaderSequential(const CKMC_header& header, const CInputDesc& desc);
+	bool NextKmerSequential(CKmer<SIZE>& kmer, uint32& counter);
+	~CKMC2DbReaderSequential();
+
+private:
+	void allocate_buffers();
+	void reload_pref_buff();
+
+	uint32 signle_bin_size, map_size, map_size_bytes, no_of_bins;
+	std::string prefix_file_name;
+	FILE* prefix_file;
+	uint64 current_prefix_index;
+	uint64 prefix_mask;
+
+	uint64* prefix_buff = nullptr;
+	int a;
+};
+
+//************************************************************************************************************
+// CKMC2DbReaderCountersOnly - Produce counters of k-mers from KMC2 database
+//************************************************************************************************************
+template<unsigned SIZE>
+class CKMC2DbReaderCountersOnly : CKCM2DbReaderSeqCounter_Base<SIZE>
+{
+public:
+	CKMC2DbReaderCountersOnly(const CKMC_header& header, const CInputDesc& desc);
+	bool NextCounter(uint32& counter);
+
+private:
+	void allocate_buffers();
+};
+
+//************************************************************************************************************
+// CKMC2DbReader - reader of KMC2 
+//************************************************************************************************************
+template<unsigned SIZE>
+class CKMC2DbReader : public CInput<SIZE>
+{
+public:
+	CKMC2DbReader(const CKMC_header& header, const CInputDesc& desc, CPercentProgress& percent_progress, KMCDBOpenMode open_mode);
+
+	void NextBundle(CBundle<SIZE>& bundle) override;
+
+	void IgnoreRest() override;
+
+	bool NextKmerSequential(CKmer<SIZE>& kmer, uint32& counter);
+	bool NextCounter(uint32& counter);
+
+private:
+	CPercentProgress& percent_progress;
+	uint32 progress_id;
+
+	std::unique_ptr<CKMC2DbReaderSorted<SIZE>> db_reader_sorted;
+	std::unique_ptr<CKMC2DbReaderSequential<SIZE>> db_reader_sequential;
+	std::unique_ptr<CKMC2DbReaderCountersOnly<SIZE>> db_reader_counters_only;
+};
+
+
+
+/*****************************************************************************************************************************/
+/**************************************************** CBin IMPLEMENTATION ****************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+
+template<unsigned SIZE> CBin<SIZE>::CBin(uint32 bin_id, uint64* LUT, CKMC2DbReaderSorted<SIZE>& kmc2_db) :
+bin_id(bin_id),
+LUT(LUT),
+bin_provider(kmc2_db.bin_provider),
+kmc2_db(kmc2_db),
+suffix_bytes(kmc2_db.suffix_bytes),
+counter_size(kmc2_db.header.counter_size),
+max_prefix(kmc2_db.lut_size - 1)
+
+{
+	kmer_number_start = LUT[0];
+	kmer_number_end = LUT[kmc2_db.lut_size];
+	kmers_left = kmer_number_end - kmer_number_start;
+	kmers_left_for_current_prefix = LUT[1] - LUT[0];
+	prefix_bytes = (kmc2_db.header.lut_prefix_len + 3) / 4;
+	kmer_bytes = prefix_bytes + suffix_bytes;
+
+	cutoff_min = kmc2_db.desc.cutoff_min;
+	cutoff_range = kmc2_db.desc.cutoff_max - kmc2_db.desc.cutoff_min;
+
+	record_size = suffix_bytes + counter_size;
+
+	prefix.clear();
+	is_little_endian = CConfig::GetInstance().IsLittleEndian();
+	counter_mask = (uint32)((1ull << (counter_size << 3)) - 1);
+}
+
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CBin<SIZE>::NextKmer(CKmer<SIZE>& kmer, uint32& counter)
+{
+	while (kmers_left)
+	{
+		if (pos >= bin_buff.size)
+			reload_suf_buf();
+
+		//skip empty
+		while (!kmers_left_for_current_prefix)
+		{
+			++prefix_pos;
+			prefix.increment_at(suffix_bytes);
+			kmers_left_for_current_prefix = LUT[prefix_pos + 1] - LUT[prefix_pos];
+		}
+
+		uchar* record = bin_buff.buf + pos;
+
+		kmer.load_fast(record, suffix_bytes, is_little_endian);
+		kmer.set_prefix(prefix, suffix_bytes);
+
+		CCounterBuilder::build_counter(counter, record, counter_size, counter_mask, is_little_endian);
+		pos += record_size;
+
+		--kmers_left;
+		--kmers_left_for_current_prefix;
+		if (counter - cutoff_min <= cutoff_range)
+			return true;
+	}
+	return false;
+}
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CBin<SIZE>::reload_suf_buf()
+{
+	bin_provider.pop(bin_id, bin_buff);
+	pos = 0;
+}
+
+
+
+/*****************************************************************************************************************************/
+/************************************************** CKmerPQ IMPLEMENTATION ***************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+
+template<unsigned SIZE> CKmerPQ<SIZE>::CKmerPQ(uint32 _no_of_bins)
+{
+	elems.resize(_no_of_bins + 1);
+	descs.resize(_no_of_bins + 1);
+	pos = 1;
+	desc_pos = 0;
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKmerPQ<SIZE>::reset()
+{
+	pos = 1;
+	desc_pos = 0;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKmerPQ<SIZE>::Process(CCircularQueue<SIZE>& output_queue)
+{
+	if (pos <= 1)
+	{
+		output_queue.mark_completed();
+		return;
+	}
+	CBundleData<SIZE> bundle_data;
+	uint32 desc_id = 0;
+	CBin<SIZE>* bin = descs[elems[1].second].first;
+	CKmer<SIZE> kmer;
+	uint32 counter;
+	uchar* record = nullptr;
+
+	uint32 suffix_bytes = bin->suffix_bytes;
+	uint32 counter_size = bin->counter_size;
+	uint32 counter_mask = bin->counter_mask;
+	uint32 record_size = bin->record_size;
+	uint32 cutoff_min = bin->cutoff_min;
+	uint32 cutoff_range = bin->cutoff_range;
+
+	bool endian = CConfig::GetInstance().IsLittleEndian();
+
+	while (true)
+	{
+		if (pos <= 1)
+			break;
+		bundle_data.Insert(elems[1].first, descs[elems[1].second].second);
+
+
+		//UPDATE HEAP!
+		desc_id = elems[1].second;
+		bin = descs[desc_id].first;
+
+		bool exists = false;
+
+
+		while (bin->kmers_left)
+		{
+
+			if (bin->pos >= bin->bin_buff.size)
+				bin->reload_suf_buf();
+
+			//skip empty
+			while (!bin->kmers_left_for_current_prefix)
+			{
+				++bin->prefix_pos;
+				bin->prefix.increment_at(suffix_bytes);
+				bin->kmers_left_for_current_prefix = bin->LUT[bin->prefix_pos + 1] - bin->LUT[bin->prefix_pos];
+			}
+
+			record = bin->bin_buff.buf + bin->pos;
+
+			kmer.load_fast(record, suffix_bytes, endian);
+			kmer.set_prefix(bin->prefix, suffix_bytes);
+
+			CCounterBuilder::build_counter(counter, record, counter_size, counter_mask, endian);
+			bin->pos += record_size;
+
+			--bin->kmers_left;
+			--bin->kmers_left_for_current_prefix;
+			if (counter - cutoff_min <= cutoff_range)
+			{
+				exists = true;
+				break;
+			}
+		}
+
+		if (!exists)
+		{
+			kmer.set(elems[--pos].first);
+			desc_id = elems[pos].second;
+		}
+		else
+			descs[desc_id].second = counter;
+
+		uint32 parent, less;
+		parent = less = 1;
+		while (true)
+		{
+			if (parent * 2 >= pos)
+				break;
+			if (parent * 2 + 1 >= pos)
+				less = parent * 2;
+			else if (elems[parent * 2].first < elems[parent * 2 + 1].first)
+				less = parent * 2;
+			else
+				less = parent * 2 + 1;
+			if (elems[less].first < kmer)
+			{
+				elems[parent] = elems[less];
+				parent = less;
+			}
+			else
+				break;
+		}
+		elems[parent] = std::make_pair(kmer, desc_id);
+
+		if (bundle_data.Full())
+		{
+			if (!output_queue.push(bundle_data))
+				break;
+		}
+	}
+	if (!bundle_data.Empty())
+		output_queue.push(bundle_data);
+	output_queue.mark_completed();
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> inline void CKmerPQ<SIZE>::init_add(CBin<SIZE>* bin)
+{
+	CKmer<SIZE> kmer;
+	uint32 counter;
+	if (bin->NextKmer(kmer, counter))
+	{
+		descs[desc_pos] = std::make_pair(bin, counter);
+		elems[pos] = std::make_pair(kmer, desc_pos);
+		uint32 child_pos = pos++;
+
+		while (child_pos > 1 && elems[child_pos].first < elems[child_pos / 2].first)
+		{
+			swap(elems[child_pos], elems[child_pos / 2]);
+			child_pos /= 2;
+		}
+
+		++desc_pos;
+	}
+}
+
+/*****************************************************************************************************************************/
+/*********************************************** CMergerParent IMPLEMENTATION ************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+template<unsigned SIZE>	CMergerParent<SIZE>::CMergerParent(std::vector<CCircularQueue<SIZE>*>& input_queues, CCircularQueue<SIZE>& output_queue, uint32 n_subthreads) :
+	input_queues(input_queues),
+	output_queue(output_queue),
+	n_subthreads(n_subthreads)
+{
+	input_bundles.resize(input_queues.size());
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CMergerParent<SIZE>::operator()()
+{
+	if (n_subthreads > 1)
+	{
+		ProcessWithSubthreads();
+	}
+
+	else if (input_queues.size() == 2)
+	{
+		Process2Inputs();
+	}
+	else
+	{
+		ProcessMoreInputs();
+	}
+}
+
+//************************************************************************************************************
+// CParentSubthreadPartDesc - Contains current state of buffers
+//************************************************************************************************************
+struct CParentSubthreadPartDesc
+{
+	uint32 start, end, part_end;
+	uint32 left()
+	{
+		return end - part_end;
+	};
+};
+
+//************************************************************************************************************
+// CParentSubthreadSynchronizer - Synchronize subthreads created by CMergerParent
+//************************************************************************************************************
+class CParentSubthreadSynchronizer
+{
+	uint32 n_tasks = 0;
+	std::mutex mtx;
+	std::condition_variable cv;
+public:
+	void decrement()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		--n_tasks;
+	}
+	void increment()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		++n_tasks;
+	}
+
+	void wait()
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv.wait(lck, [this]{return !n_tasks; });
+	}
+
+	void notify_task_finished()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		--n_tasks;
+		if (!n_tasks)
+			cv.notify_all();
+	}
+};
+
+//************************************************************************************************************
+// CParentSubthreadDesc - Input data of subthreads of CMergerParent
+//************************************************************************************************************
+template<unsigned SIZE>
+struct CParentSubthreadDesc
+{
+	std::vector<CBundleData<SIZE>>* inputs;
+	CBundleData<SIZE>* out;
+	std::vector<CParentSubthreadPartDesc> desc;
+	uint32 o_start;
+};
+
+//************************************************************************************************************
+// CParentSubthreadDescQueue - Passes data to subthreads from CMergerParent
+//************************************************************************************************************
+template<unsigned SIZE>
+class CParentSubthreadDescQueue
+{
+	mutable std::mutex mtx;
+	std::condition_variable cv;
+	bool empty = true;
+	bool completed = false;
+public:
+	CParentSubthreadDesc<SIZE> desc;
+	void start()
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv.wait(lck, [this]{return empty; });
+		empty = false;
+		cv.notify_all();
+	}
+
+	bool pop(CParentSubthreadDesc<SIZE> &_desc)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv.wait(lck, [this]{return !empty || completed; });
+		if (completed)
+			return false;
+		_desc = desc;
+		empty = true;
+		cv.notify_all();
+		return true;
+	}
+	void mark_completed()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		completed = true;
+		cv.notify_all();
+	}
+};
+
+//************************************************************************************************************
+// CMergerParentSubthread - Merge data described in CParentSubthreadDescQueue
+//************************************************************************************************************
+template<unsigned SIZE>
+class CMergerParentSubthread
+{
+	CParentSubthreadDescQueue<SIZE>& task_queue;
+	CParentSubthreadSynchronizer& synchronizer;
+public:
+	CMergerParentSubthread(CParentSubthreadDescQueue<SIZE>& task_queue, CParentSubthreadSynchronizer& synchronizer)
+		:
+		task_queue(task_queue),
+		synchronizer(synchronizer)
+	{
+	}
+
+	void operator()()
+	{
+		CParentSubthreadDesc<SIZE> t;
+		while (task_queue.pop(t))
+		{
+			std::vector<CBundleData<SIZE>>& inputs = *t.inputs;
+			CBundleData<SIZE>& out = *t.out;
+			using heap_elem_t = std::pair<CKmer<SIZE>, uint32>;
+			std::vector<heap_elem_t> heap(inputs.size() + 1);
+
+			uint32 pos = 1;
+
+			for (uint32 i = 0; i < inputs.size(); ++i)
+			{
+				if (t.desc[i].start >= t.desc[i].part_end)
+					continue;
+				heap[pos] = std::make_pair(inputs[i].kmers_with_counters[t.desc[i].start].kmer, i);
+				t.desc[i].start++;
+				uint32 child_pos = pos++;
+				while (child_pos > 1 && heap[child_pos].first < heap[child_pos / 2].first)
+				{
+					std::swap(heap[child_pos], heap[child_pos / 2]);
+					child_pos /= 2;
+				}
+			}
+
+			uint32 out_pos = t.o_start;
+			while (true)
+			{
+				if (pos <= 1)
+					break;
+				uint32 desc_pos = heap[1].second;
+				out.kmers_with_counters[out_pos++] = inputs[desc_pos].kmers_with_counters[t.desc[desc_pos].start - 1];
+
+				CKmer<SIZE> kmer;
+				uint32 desc_id = heap[1].second;
+
+				if (t.desc[desc_pos].start < t.desc[desc_pos].part_end)
+				{
+					kmer.set(inputs[desc_pos].kmers_with_counters[t.desc[desc_pos].start].kmer);
+					++t.desc[desc_pos].start;
+				}
+				else
+				{
+					kmer.set(heap[--pos].first);
+					desc_id = heap[pos].second;
+				}
+
+				uint32 parent, less;
+				parent = less = 1;
+				while (true)
+				{
+					if (parent * 2 >= pos)
+						break;
+					if (parent * 2 + 1 >= pos)
+						less = parent * 2;
+					else if (heap[parent * 2].first < heap[parent * 2 + 1].first)
+						less = parent * 2;
+					else
+						less = parent * 2 + 1;
+					if (heap[less].first < kmer)
+					{
+						heap[parent] = heap[less];
+						parent = less;
+					}
+					else
+						break;
+				}
+				heap[parent] = std::make_pair(kmer, desc_id);
+			}
+			//out.insert_pos = out_pos;
+			synchronizer.notify_task_finished();
+		}
+	}
+};
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CMergerParent<SIZE>::Process2Inputs()
+{
+	CBundleData<SIZE> b1, b2;
+	CCircularQueue<SIZE>* q1, *q2;
+	q1 = input_queues[0];
+	q2 = input_queues[1];
+	bool q1_empty = !q1->pop(b1);
+	bool q2_empty = !q2->pop(b2);
+
+	if (q1_empty && q2_empty)
+	{
+		output_queue.mark_completed();
+		return;
+	}
+	if (q1_empty || q2_empty)
+	{
+		CCircularQueue<SIZE>* q = q1_empty ? q2 : q1;
+		CBundleData<SIZE>& b = q1_empty ? b2 : b1;
+		while (true)
+		{
+			if (!output_queue.push(b))
+				break;
+			if (!q->pop(b))
+				break;
+		}
+		output_queue.mark_completed();
+		return;
+	}
+
+	uint32 get1 = 0;
+	uint32 get2 = 0;
+
+	CKmer<SIZE> kmer2 = b2.kmers_with_counters[get2].kmer;
+	uint32 counter2 = b2.kmers_with_counters[get2].counter;
+	CKmer<SIZE> kmer1 = b1.kmers_with_counters[get1].kmer;
+	uint32 counter1 = b1.kmers_with_counters[get1].counter;
+
+	uint32 left1 = b1.NRecLeft();
+	uint32 left2 = b2.NRecLeft();
+
+	uint32 out_insert_pos = 0;
+	uint32 out_size = output_bundle.size;
+
+	while (true)
+	{
+		if (kmer1 < kmer2)
+		{
+			output_bundle.kmers_with_counters[out_insert_pos].kmer = kmer1;
+			output_bundle.kmers_with_counters[out_insert_pos++].counter = counter1;
+			if (out_insert_pos == out_size)
+			{
+				output_bundle.insert_pos = out_insert_pos;
+
+				if (!output_queue.push(output_bundle))
+					break;
+				out_insert_pos = 0;
+				out_size = output_bundle.size;
+			}
+
+			++get1;
+			if (--left1)
+			{
+				kmer1 = b1.kmers_with_counters[get1].kmer;
+				counter1 = b1.kmers_with_counters[get1].counter;
+			}
+			else
+			{
+				b1.get_pos = get1;
+				if (q1->pop(b1))
+				{
+					get1 = 0;
+					kmer1 = b1.kmers_with_counters[get1].kmer;
+					counter1 = b1.kmers_with_counters[get1].counter;
+					left1 = b1.NRecLeft();
+				}
+				else
+					break;
+
+			}
+		}
+		else
+		{
+			output_bundle.kmers_with_counters[out_insert_pos].kmer = kmer2;
+			output_bundle.kmers_with_counters[out_insert_pos++].counter = counter2;
+			if (out_insert_pos == out_size)
+			{
+				output_bundle.insert_pos = out_insert_pos;
+				if (!output_queue.push(output_bundle))
+					break;
+				out_insert_pos = 0;
+				out_size = output_bundle.size;
+			}
+
+			++get2;
+			if (--left2)
+			{
+				kmer2 = b2.kmers_with_counters[get2].kmer;
+				counter2 = b2.kmers_with_counters[get2].counter;
+			}
+			else
+			{
+				b2.get_pos = get2;
+				if (q2->pop(b2))
+				{
+					get2 = 0;
+					kmer2 = b2.kmers_with_counters[get2].kmer;
+					counter2 = b2.kmers_with_counters[get2].counter;
+					left2 = b2.NRecLeft();
+				}
+				else
+					break;
+			}
+		}
+	}
+
+	b1.get_pos = get1;
+	b2.get_pos = get2;
+	output_bundle.insert_pos = out_insert_pos;
+
+	if (b1.Empty())
+		q1->pop(b1);
+	if (!b1.Empty())
+	{
+		while (true)
+		{
+			if (b1.Empty())
+			{
+				if (!q1->pop(b1))
+					break;
+			}
+			output_bundle.Insert(b1.TopKmer(), b1.TopCounter());
+			b1.Pop();
+			if (output_bundle.Full())
+			{
+				if (!output_queue.push(output_bundle))
+					break;
+			}
+		}
+	}
+
+	if (b2.Empty())
+		q2->pop(b2);
+	if (!b2.Empty())
+	{
+		while (true)
+		{
+			if (b2.Empty())
+			{
+				if (!q2->pop(b2))
+					break;
+			}
+			output_bundle.Insert(b2.TopKmer(), b2.TopCounter());
+			b2.Pop();
+			if (output_bundle.Full())
+			{
+				if (!output_queue.push(output_bundle))
+					break;
+			}
+		}
+	}
+
+	if (!output_bundle.Empty())
+		output_queue.push(output_bundle);
+	output_queue.mark_completed();
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CMergerParent<SIZE>::ProcessMoreInputs()
+{
+	//init
+	auto q_iter = input_queues.begin();
+	auto b_iter = input_bundles.begin();
+	for (; q_iter != input_queues.end();)
+	{
+		if (!(*q_iter)->pop(*b_iter))
+		{
+			q_iter = input_queues.erase(q_iter);
+			b_iter = input_bundles.erase(b_iter);
+		}
+		else
+			++q_iter, ++b_iter;
+	}
+	uint32 index_of_min = 0;
+	CKmer<SIZE> min_kmer;
+
+	uint32 output_bundle_insert_pos = output_bundle.insert_pos;
+	uint32 output_bundle_size = output_bundle.size;
+	decltype(output_bundle.kmers_with_counters) kmers_counters = output_bundle.kmers_with_counters;
+
+	while (input_bundles.size())
+	{
+		index_of_min = 0;
+		min_kmer = input_bundles[index_of_min].TopKmer();
+
+		if (input_bundles.size() == 4)
+		{
+			uint32 tmp_min = 2;
+			CKmer<SIZE> tmp_kmer = input_bundles[tmp_min].TopKmer();
+
+			if (input_bundles[1].TopKmer() < min_kmer)
+			{
+				min_kmer = input_bundles[1].TopKmer();
+				index_of_min = 1;
+			}
+			if (input_bundles[3].TopKmer() < tmp_kmer)
+			{
+				tmp_kmer = input_bundles[3].TopKmer();
+				tmp_min = 3;
+			}
+			if (tmp_kmer < min_kmer)
+			{
+				index_of_min = tmp_min;
+			}
+		}
+		else if (input_bundles.size() == 3)
+		{
+			if (input_bundles[1].TopKmer() < min_kmer)
+			{
+				min_kmer = input_bundles[1].TopKmer();
+				index_of_min = 1;
+			}
+			if (input_bundles[2].TopKmer() < min_kmer)
+			{
+				min_kmer = input_bundles[2].TopKmer();
+				index_of_min = 2;
+			}
+		}
+		else if (input_bundles.size() == 2)
+		{
+			if (input_bundles[1].TopKmer() < min_kmer)
+			{
+				min_kmer = input_bundles[1].TopKmer();
+				index_of_min = 1;
+			}
+		}
+		else if (input_bundles.size() == 1)
+		{
+		}
+		else
+		{
+			for (uint32 i = 1; i < input_bundles.size(); ++i)
+			{
+				if (input_bundles[i].TopKmer() < min_kmer)
+				{
+					index_of_min = i;
+					min_kmer = input_bundles[index_of_min].TopKmer();
+				}
+			}
+		}
+
+		kmers_counters[output_bundle_insert_pos].kmer = input_bundles[index_of_min].TopKmer();
+		kmers_counters[output_bundle_insert_pos++].counter = input_bundles[index_of_min].TopCounter();
+		//output_bundle.Insert(input_bundles[index_of_min].TopKmer(), input_bundles[index_of_min].TopCounter());
+
+		input_bundles[index_of_min].Pop();
+		if (input_bundles[index_of_min].Empty())
+		{
+
+			if (!input_queues[index_of_min]->pop(input_bundles[index_of_min]))
+			{
+				input_queues.erase(input_queues.begin() + index_of_min);
+				input_bundles.erase(input_bundles.begin() + index_of_min);
+			}
+		}
+
+
+		if (output_bundle_insert_pos == output_bundle_size)
+			//if (output_bundle.Full())
+		{
+			output_bundle.insert_pos = output_bundle_insert_pos;
+			if (!output_queue.push(output_bundle))
+			{
+				output_bundle_insert_pos = output_bundle.insert_pos; //0
+				kmers_counters = output_bundle.kmers_with_counters;
+				break;
+			}
+			output_bundle_insert_pos = output_bundle.insert_pos; //0
+			kmers_counters = output_bundle.kmers_with_counters;
+		}
+	}
+	output_bundle.insert_pos = output_bundle_insert_pos;
+	if (!output_bundle.Empty())
+		output_queue.push(output_bundle);
+	output_queue.mark_completed();
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CMergerParent<SIZE>::ProcessWithSubthreads()
+{
+	std::vector<CBundleData<SIZE>> input_bundles(input_queues.size());
+	
+	//std::vector<CBundleData<SIZE>> output_bundles;
+	//output_bundles.reserve(n_subthreads);
+	//for (uint32 i = 0; i < n_subthreads; ++i)
+		//output_bundles.emplace_back(KMC2_DB_READER_BUNDLE_CAPACITY);
+	CBundleData<SIZE> output_bundle(KMC2_DB_READER_BUNDLE_CAPACITY);
+	uint32 curr_end = output_bundle.insert_pos;
+
+	auto q_iter = input_queues.begin();
+	auto b_iter = input_bundles.begin();
+	for (; q_iter != input_queues.end();)
+	{
+		if (!(*q_iter)->pop(*b_iter))
+		{
+			q_iter = input_queues.erase(q_iter);
+			b_iter = input_bundles.erase(b_iter);
+		}
+		else
+			++q_iter, ++b_iter;
+	}
+
+	std::vector<CParentSubthreadDescQueue<SIZE>> task_descs(n_subthreads);
+	std::vector<CParentSubthreadPartDesc> descs(input_queues.size());
+	for (uint32 i = 0; i < input_queues.size(); ++i)
+	{
+		descs[i].start = descs[i].part_end = input_bundles[i].get_pos;
+		descs[i].end = input_bundles[i].insert_pos;
+	}
+
+	std::vector<CMergerParentSubthread<SIZE>> subtasks;
+	std::vector<std::thread> subthreads;
+
+	subtasks.reserve(n_subthreads);
+	subthreads.reserve(n_subthreads);
+
+	CParentSubthreadSynchronizer syncer;
+	for (uint32 i = 0; i < n_subthreads; ++i)
+	{
+		subtasks.emplace_back(task_descs[i], syncer);
+		subthreads.push_back(std::thread(std::ref(subtasks.back())));
+	}
+
+	while (input_bundles.size())
+	{
+		//prepare threads					
+		for (uint32 th = 0; th < n_subthreads; ++th)
+		{
+			bool any_empty = false;
+			for (uint32 i = 0; i < descs.size(); ++i)
+			{
+				descs[i].part_end = descs[i].start + MIN((output_bundle.size - curr_end) / input_bundles.size() / (n_subthreads - th), descs[i].left());
+
+				//if any is empty it must be refilled or removed
+				if (descs[i].part_end == descs[i].start)
+				{
+					any_empty = true;
+					break;
+				}
+			}
+			if (any_empty)
+				break;
+
+			//find min kmer
+			uint32 min_kmer_i = 0;
+			CKmer<SIZE> min_kmer = input_bundles[0].kmers_with_counters[descs[0].part_end - 1].kmer;
+
+			for (uint32 i = min_kmer_i + 1; i < input_bundles.size(); ++i)
+			{
+				if (input_bundles[i].kmers_with_counters[descs[i].part_end - 1].kmer < min_kmer)
+				{
+					min_kmer = input_bundles[i].kmers_with_counters[descs[i].part_end - 1].kmer;
+					min_kmer_i = i;
+				}
+			}
+
+			uint32 prev_end = curr_end;
+
+			//correct part_end according to min kmer
+			for (uint32 i = 0; i < descs.size(); ++i)
+			{
+				if (i != min_kmer_i)
+				{
+					descs[i].part_end = LastLowerPlus1(input_bundles[i], descs[i].start, descs[i].part_end, min_kmer);
+				}
+				curr_end += descs[i].part_end - descs[i].start;
+			}
+
+			task_descs[th].desc.desc = descs;
+			task_descs[th].desc.inputs = &input_bundles;
+			task_descs[th].desc.out = &output_bundle;
+			task_descs[th].desc.o_start = prev_end;
+			syncer.increment();
+			task_descs[th].start();
+
+			for (uint32 i = 0; i < descs.size(); ++i)
+			{
+				descs[i].start = descs[i].part_end;
+			}
+		}
+
+		syncer.wait(); //BARIER
+
+		//send output
+		output_bundle.insert_pos = curr_end;
+		if (!output_bundle.Empty())
+		{
+			if (!output_queue.push(output_bundle))
+			{
+				break;
+			}
+		}
+
+		curr_end = output_bundle.insert_pos;
+
+		auto q_iter = input_queues.begin();
+		auto b_iter = input_bundles.begin();
+		auto d_iter = descs.begin();
+
+		for (; b_iter != input_bundles.end();)
+		{
+			b_iter->get_pos = d_iter->start;
+			if ((*b_iter).Empty())
+			{
+				if ((*q_iter)->pop(*b_iter))
+				{
+					d_iter->start = d_iter->part_end = b_iter->get_pos;
+					d_iter->end = b_iter->insert_pos;
+				}
+				else
+				{
+					d_iter = descs.erase(d_iter);
+					b_iter = input_bundles.erase(b_iter);
+					q_iter = input_queues.erase(q_iter);
+					continue;
+				}
+			}
+			++q_iter, ++b_iter, ++d_iter;
+		}
+
+	}
+
+	for (auto& t : task_descs)
+		t.mark_completed();
+
+	for (auto& t : subthreads)
+		t.join();
+
+	output_queue.mark_completed();
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> uint32 CMergerParent<SIZE>::LastLowerPlus1(CBundleData<SIZE>& b, uint32 start, uint32 end, CKmer<SIZE> kmer)
+{
+	auto ub = std::upper_bound(b.kmers_with_counters + start, b.kmers_with_counters + end, kmer,
+		[](const CKmer<SIZE>& kmer, typename CBundleData<SIZE>::CKmerWithCounter& kc)
+	{
+		return kmer < kc.kmer;
+	});
+	return ub - b.kmers_with_counters;
+}
+
+/*****************************************************************************************************************************/
+/************************************************ CMergerChild IMPLEMENTATION ************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CMergerChild<SIZE>::CMergerChild(bin_iter begin, bin_iter end, CCircularQueue<SIZE>& output_queue) :
+bins(begin, end),
+output_queue(output_queue)
+{
+
+}
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CMergerChild<SIZE>::operator()()
+{
+	CKmerPQ<SIZE> kmers_pq(static_cast<uint32>(bins.size()));
+	for (uint32 i = 0; i < bins.size(); ++i)
+		kmers_pq.init_add(&bins[i].get());
+
+	kmers_pq.Process(output_queue);
+}
+
+
+
+/*****************************************************************************************************************************/
+/********************************************* CKMC2DbReaderSorted IMPLEMENTATION ********************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CKMC2DbReaderSorted<SIZE>::CKMC2DbReaderSorted(const CKMC_header& header, const CInputDesc& desc) :
+header(header),
+desc(desc)
+{
+	LUTS = nullptr;
+	lut_size = 1 << 2 * header.lut_prefix_len;
+	uint32 lut_recs = (1 << 2 * header.lut_prefix_len) * header.no_of_bins + 1;
+	LUTS = new uint64[lut_recs];
+	suffix_bytes = (header.kmer_len - header.lut_prefix_len) / 4;
+	record_size = suffix_bytes + header.counter_size;
+	if (!LUTS)
+	{
+		std::cerr << "Error: cannot allocate memory for LUTS of KMC2 database\n";
+		exit(1);
+	}
+
+	std::string kmc_pre_file_name = desc.file_src + ".kmc_pre";
+	FILE* kmc_pre = fopen(kmc_pre_file_name.c_str(), "rb");
+	if (!kmc_pre)
+	{
+		std::cerr << "Error: cannot open kmc2 prefix file to read LUTS";
+		exit(1);
+	}
+
+	my_fseek(kmc_pre, 4, SEEK_SET);
+	if (fread(LUTS, sizeof(uint64), lut_recs, kmc_pre) != lut_recs)
+	{
+		std::cerr << "Some error occured while reading LUTS from kmc2 prefix file \n";
+		exit(1);
+	}
+	fclose(kmc_pre);
+
+	std::string kmc_suf_file_name = desc.file_src + ".kmc_suf";
+	kmc_suf = fopen(kmc_suf_file_name.c_str(), "rb");
+
+	if (!kmc_suf)
+	{
+		std::cerr << "Error: cannot open kmc2 suffix file\n";
+		exit(1);
+	}
+	setvbuf(kmc_suf, NULL, _IONBF, 0);
+
+	bins.reserve(header.no_of_bins);
+	for (uint32 i = 0; i < header.no_of_bins; ++i)
+		bins.emplace_back(i, LUTS + i * lut_size, *this);
+
+	//starting threads
+
+	bin_provider.init(bins);
+
+	suf_bin_reader = new CSufBinReader<SIZE>(bin_provider, kmc_suf);
+	suf_bin_reader_th = std::thread(std::ref(*suf_bin_reader));
+
+	uint32 n_threads = desc.threads;
+
+	if (n_threads < 3)
+	{
+		n_threads = n_child_threads = 1;		
+		output_queue = new CCircularQueue<SIZE>(DEFAULT_CIRCULAL_QUEUE_CAPACITY);
+		childs.push_back(new CMergerChild<SIZE>(bins.begin(), bins.end(), *output_queue));
+		childs_threads.push_back(std::thread(std::ref(*childs.front())));
+		return;
+	}
+
+	else if (n_threads == 3)
+	{
+		n_child_threads = 2;
+		n_parent_threads = 1; 
+	}
+	//based on experiment on 24 core machine
+	else if (n_threads < 6)
+	{
+		n_child_threads = 3;
+		n_parent_threads = n_threads - n_child_threads;
+	}
+	else if (n_threads < 9)
+	{
+		n_child_threads = 4;
+		n_parent_threads = n_threads - n_child_threads;
+	}
+	else if (n_threads < 11)
+	{
+		n_child_threads = 5;
+		n_parent_threads = n_threads - n_child_threads;
+	}
+	else if (n_threads < 14)
+	{
+		n_child_threads = 6;
+		n_parent_threads = n_threads - n_child_threads;
+	}
+	else if (n_threads < 17)
+	{
+		n_child_threads = 7;
+		n_parent_threads = n_threads - n_child_threads;
+	}
+	else
+	{
+		n_child_threads = (n_threads - 17) / 5 + 8;
+		n_parent_threads = n_threads - n_child_threads;
+	}
+	childs_parent_queues.reserve(n_child_threads);
+	childs.reserve(n_child_threads);
+
+	uint32 bundle_size = KMC2_DB_READER_BUNDLE_CAPACITY;
+	if (n_parent_threads < 2)
+	{
+		bundle_size = BUNDLE_CAPACITY;
+	}
+
+	for (uint32 i = 0; i < n_child_threads; ++i)
+		childs_parent_queues.push_back(new CCircularQueue<SIZE>(DEFAULT_CIRCULAL_QUEUE_CAPACITY, bundle_size));
+
+	uint32 bins_per_thread = header.no_of_bins / n_child_threads;
+
+	for (uint32 i = 0; i < n_child_threads - 1; ++i)
+	{
+		childs.push_back(new CMergerChild<SIZE>(bins.begin() + i * bins_per_thread, bins.begin() + (i + 1) * bins_per_thread, *childs_parent_queues[i]));
+		childs_threads.push_back(std::thread(std::ref(*childs.back())));
+	}
+
+	//last one
+	childs.push_back(new CMergerChild<SIZE>(bins.begin() + (n_child_threads - 1) * bins_per_thread, bins.end(), *childs_parent_queues.back()));
+	childs_threads.push_back(std::thread(std::ref(*childs.back())));
+
+	output_queue = new CCircularQueue<SIZE>(DEFAULT_CIRCULAL_QUEUE_CAPACITY, bundle_size);
+
+	parent = new CMergerParent<SIZE>(childs_parent_queues, *output_queue, n_parent_threads);
+	parent_thread = std::thread(std::ref(*parent));
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC2DbReaderSorted<SIZE>::NextBundle(CBundle<SIZE>& bundle, bool& finished)
+{
+	if (output_queue->pop(bundle.Data()))
+	{
+		return;
+	}
+
+	for (auto& child_thread : childs_threads)
+		child_thread.join();
+	for (auto& child : childs)
+		delete child;
+
+	if(parent_thread.joinable()) //for small number of threads there is only one child thread and parent threads is not needed
+		parent_thread.join();
+	if (parent) //as above
+		delete parent;
+
+	delete output_queue;
+
+	for (auto& q : childs_parent_queues)
+		delete q;
+
+	suf_bin_reader_th.join();
+	delete suf_bin_reader;
+	finished = true;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC2DbReaderSorted<SIZE>::IgnoreRest()
+{
+	output_queue->force_finish();
+
+	for (auto& q : childs_parent_queues)
+		q->force_finish();
+
+	for (auto& child_thread : childs_threads)
+		child_thread.join();
+	for (auto& child : childs)
+		delete child;
+
+	if (parent_thread.joinable()) //for small number of threads there is only one child thread and parent threads is not needed
+		parent_thread.join();
+
+	if(parent) //as above
+		delete parent;
+
+	delete output_queue;
+
+	for (auto& q : childs_parent_queues)
+		delete q;
+
+	bin_provider.force_to_finish();
+
+	suf_bin_reader_th.join();
+	delete suf_bin_reader;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CKMC2DbReaderSorted<SIZE>::~CKMC2DbReaderSorted()
+{
+	delete[] LUTS;
+	fclose(kmc_suf);
+}
+
+/*****************************************************************************************************************************/
+/******************************************* CKCM2DbReaderSeqCounter_Base IMPLEMENTATION *************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CKCM2DbReaderSeqCounter_Base<SIZE>::CKCM2DbReaderSeqCounter_Base(const CKMC_header& header, const CInputDesc& desc) :
+header(header),
+desc(desc)
+{
+	suffix_bytes = (header.kmer_len - header.lut_prefix_len) / 4;
+	record_size = suffix_bytes + header.counter_size;
+	suffix_buff_size = SUFFIX_BUFF_BYTES / record_size * record_size;
+
+	suffix_left_to_read = header.total_kmers * record_size;
+
+	if (suffix_left_to_read < suffix_buff_size)
+		suffix_buff_size = suffix_left_to_read;
+
+	prefix_bytes = (header.lut_prefix_len + 3) / 4;
+
+	kmer_bytes = prefix_bytes + suffix_bytes;
+
+}
+
+/*****************************************************************************************************************************/
+/********************************************************* PROTECTED**********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CKCM2DbReaderSeqCounter_Base<SIZE>::~CKCM2DbReaderSeqCounter_Base()
+{
+	if (suffix_file)
+		fclose(suffix_file);
+	delete[] suffix_buff;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKCM2DbReaderSeqCounter_Base<SIZE>::open_files()
+{
+	suffix_file_name = desc.file_src + ".kmc_suf";
+
+	suffix_file = fopen(suffix_file_name.c_str(), "rb");
+
+
+	if (!suffix_file)
+	{
+		std::cerr << "Error: cannot open file: " << suffix_file_name << "\n";
+		exit(1);
+	}
+	setvbuf(suffix_file, NULL, _IONBF, 0);
+
+	char marker[4];
+	if (fread(marker, 1, 4, suffix_file) != 4)
+	{
+		std::cerr << "Error: while reading start marker in file: " << suffix_file_name << "\n";
+		exit(1);
+	}
+
+	if (strncmp(marker, "KMCS", 4) != 0)
+	{
+		std::cerr << "Error: wrong start marker in file: " << suffix_file_name << "\n";
+		exit(1);
+	}
+
+
+	my_fseek(suffix_file, -4, SEEK_END);
+	if (fread(marker, 1, 4, suffix_file) != 4)
+	{
+		std::cerr << "Error: while reading end marker in file: " << suffix_file_name << "\n";
+		exit(1);
+	}
+
+	if (strncmp(marker, "KMCS", 4) != 0)
+	{
+		std::cerr << "Error: wrong end marker in file: " << suffix_file_name << "\n";
+		exit(1);
+	}
+
+	my_fseek(suffix_file, 4, SEEK_SET); //skip KMCS	
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKCM2DbReaderSeqCounter_Base<SIZE>::reload_suf_buff()
+{
+	uint64 to_read = MIN(suffix_left_to_read, suffix_buff_size);
+	if (to_read == 0)
+		return false;
+	uint64 readed = fread(suffix_buff, 1, to_read, suffix_file);
+	if (readed != to_read)
+	{
+		std::cerr << "Error: some error while reading " << suffix_file_name << "\n";
+		exit(1);
+	}
+	suffix_buff_pos = 0;
+	suffix_left_to_read -= to_read;
+	return true;
+}
+
+
+/*****************************************************************************************************************************/
+/********************************************* CKMC2DbReaderSequential IMPLEMENTATION ****************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CKMC2DbReaderSequential<SIZE>::CKMC2DbReaderSequential(const CKMC_header& header, const CInputDesc& desc) :
+CKCM2DbReaderSeqCounter_Base<SIZE>(header, desc)
+{
+	this->open_files();
+
+	prefix_file_name = desc.file_src + ".kmc_pre";
+	prefix_file = fopen(prefix_file_name.c_str(), "rb");
+
+	if (!prefix_file)
+	{
+		std::cerr << "Error: cannot open file: " << prefix_file_name << "\n";
+		exit(1);
+	}
+	setvbuf(prefix_file, NULL, _IONBF, 0);
+	my_fseek(prefix_file, 4 + sizeof(uint64), SEEK_SET);//skip KMCP and first value as it must be 0
+
+	signle_bin_size = 1 << 2 * header.lut_prefix_len;
+	map_size = (1 << 2 * header.signature_len) + 1;
+
+	map_size_bytes = map_size * sizeof(uint32);
+
+	no_of_bins = header.no_of_bins;
+
+	this->prefix_buff_size = this->PREFIX_BUFF_BYTES / sizeof(uint64);
+
+	this->suffix_left_to_read = this->header.total_kmers * this->record_size;
+
+	if (this->suffix_left_to_read < this->suffix_buff_size)
+		this->suffix_buff_size = this->suffix_left_to_read;
+
+	this->prefix_left_to_read = (1 << this->header.lut_prefix_len * 2) * this->no_of_bins;
+
+	if (this->prefix_left_to_read < this->prefix_buff_size)
+		this->prefix_buff_size = this->prefix_left_to_read;
+
+	prefix_mask = (1 << 2 * this->header.lut_prefix_len) - 1;
+
+	allocate_buffers();
+
+	my_fseek(prefix_file, 4 + sizeof(uint64), SEEK_SET);
+	reload_pref_buff();
+
+	this->reload_suf_buff();
+	current_prefix_index = 0;
+	this->suffix_number = 0;
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKMC2DbReaderSequential<SIZE>::NextKmerSequential(CKmer<SIZE>& kmer, uint32& counter)
+{
+	while (true)
+	{
+		if (this->suffix_number >= this->header.total_kmers)
+			return false;
+
+		while (this->prefix_buff[this->prefix_buff_pos] <= this->suffix_number)
+		{
+			++current_prefix_index;
+			++this->prefix_buff_pos;
+			if (this->prefix_buff_pos >= this->prefix_buff_size)
+				this->reload_pref_buff();
+		}
+
+		uchar* record = this->suffix_buff + this->suffix_buff_pos;
+		uint32 pos = this->kmer_bytes - 1;
+
+		uint32 current_prefix = static_cast<uint32>(current_prefix_index & prefix_mask);
+
+		kmer.load(record, this->suffix_bytes);
+		for (int32 i = this->prefix_bytes - 1; i >= 0; --i)
+			kmer.set_byte(pos--, current_prefix >> (i << 3));
+
+		counter = 0;
+		for (int32 i = this->header.counter_size - 1; i >= 0; --i)
+		{
+			counter <<= 8;
+			counter += record[i];
+		}
+
+		++this->suffix_number;
+		this->suffix_buff_pos += this->record_size;
+
+		if (this->suffix_buff_pos >= this->suffix_buff_size)
+			this->reload_suf_buff();
+
+		if (counter >= this->desc.cutoff_min && counter <= this->desc.cutoff_max)
+			return true;
+	}
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CKMC2DbReaderSequential<SIZE>::~CKMC2DbReaderSequential()
+{
+	if (prefix_file)
+		fclose(prefix_file);
+	delete[] prefix_buff;
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC2DbReaderSequential<SIZE>::allocate_buffers()
+{
+	this->suffix_buff = new uchar[this->suffix_buff_size + sizeof(uint64)]; //+ sizeof(uint64) because CKmer::load_fast may look out of buffer
+	this->prefix_buff = new uint64[this->prefix_buff_size];
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC2DbReaderSequential<SIZE>::reload_pref_buff()
+{
+	uint64 to_read = MIN(this->prefix_left_to_read, this->prefix_buff_size);
+	this->prefix_buff_pos = 0;
+	if (fread(prefix_buff, sizeof(uint64), to_read, prefix_file) != to_read)
+	{
+		std::cerr << "Error: some error while reading " << prefix_file_name << "\n";
+		exit(1);
+	}
+	this->prefix_left_to_read -= to_read;
+	if (to_read < this->prefix_buff_size)
+	{
+		this->prefix_buff[to_read] = this->header.total_kmers;//guard
+	}
+}
+
+
+/*****************************************************************************************************************************/
+/******************************************** CKMC2DbReaderCountersOnly IMPLEMENTATION ***************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CKMC2DbReaderCountersOnly<SIZE>::CKMC2DbReaderCountersOnly(const CKMC_header& header, const CInputDesc& desc) :
+CKCM2DbReaderSeqCounter_Base<SIZE>(header, desc)
+{
+	this->open_files();
+	allocate_buffers();
+	this->reload_suf_buff();
+	this->suffix_number = 0;
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKMC2DbReaderCountersOnly<SIZE>::NextCounter(uint32& counter)
+{
+	while (true)
+	{
+		if (this->suffix_number >= this->header.total_kmers)
+			return false;
+
+		uchar* record = this->suffix_buff + this->suffix_buff_pos + this->suffix_bytes;
+
+		counter = 0;
+		for (int32 i = this->header.counter_size - 1; i >= 0; --i)
+		{
+			counter <<= 8;
+			counter += record[i];
+		}
+
+		++this->suffix_number;
+		this->suffix_buff_pos += this->record_size;
+
+		if (this->suffix_buff_pos >= this->suffix_buff_size)
+			this->reload_suf_buff();
+
+		if (counter >= this->desc.cutoff_min && counter <= this->desc.cutoff_max)
+			return true;
+	}
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC2DbReaderCountersOnly<SIZE>::allocate_buffers()
+{
+	this->suffix_buff = new uchar[this->suffix_buff_size];
+}
+
+
+
+/*****************************************************************************************************************************/
+/************************************************* CKMC2DbReader IMPLEMENTATION **********************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CKMC2DbReader<SIZE>::CKMC2DbReader(const CKMC_header& header, const CInputDesc& desc, CPercentProgress& percent_progress, KMCDBOpenMode open_mode) :
+	percent_progress(percent_progress)
+{
+	progress_id = percent_progress.RegisterItem(header.total_kmers);
+	switch (open_mode)
+	{
+	case KMCDBOpenMode::sorted:
+		db_reader_sorted = std::make_unique <CKMC2DbReaderSorted<SIZE>>(header, desc);
+		break;
+	case KMCDBOpenMode::sequential:
+		db_reader_sequential = std::make_unique<CKMC2DbReaderSequential<SIZE>>(header, desc);
+		break;
+	case KMCDBOpenMode::counters_only:
+		db_reader_counters_only = std::make_unique<CKMC2DbReaderCountersOnly<SIZE>>(header, desc);
+		break;
+	default: //should never be here
+		std::cerr << "Error: unknow open mode \n";
+		exit(1);
+	}
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC2DbReader<SIZE>::NextBundle(CBundle<SIZE>& bundle)
+{
+	db_reader_sorted->NextBundle(bundle, this->finished);
+	percent_progress.UpdateItem(progress_id, bundle.Size());
+	if (this->finished)
+	{
+		percent_progress.Complete(progress_id);
+	}
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void CKMC2DbReader<SIZE>::IgnoreRest()
+{
+	if (this->finished)
+		return;
+	db_reader_sorted->IgnoreRest();
+	this->finished = true;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKMC2DbReader<SIZE>::NextKmerSequential(CKmer<SIZE>& kmer, uint32& counter)
+{
+	if (db_reader_sequential->NextKmerSequential(kmer, counter))
+	{
+		percent_progress.UpdateItem(progress_id);
+		return true;
+	}
+	percent_progress.Complete(progress_id);
+	return false;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> bool CKMC2DbReader<SIZE>::NextCounter(uint32& counter)
+{
+	if (db_reader_counters_only->NextCounter(counter))
+	{
+		percent_progress.UpdateItem(progress_id);
+		return true;
+	}
+	percent_progress.Complete(progress_id);
+	return false;
+}
+
+
+
+#endif
+

=== added file 'kmc_tools/kmc_header.cpp'
--- old/kmc_tools/kmc_header.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/kmc_header.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,88 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "kmc_header.h"
+#include <cstring>
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+
+CKMC_header::CKMC_header(std::string file_name)
+{
+	file_name += ".kmc_pre";
+	FILE* file = my_fopen(file_name.c_str(), "rb");
+	if (!file)
+	{
+		std::cerr << "Error: Cannot open file " << file_name << "\n";
+		exit(1);
+	}
+	char marker[4];
+	if (fread(marker, 1, 4, file) != 4)
+	{
+		std::cerr << "Error while reading start marker in " << file_name << "\n";
+		exit(1);
+	}
+
+	if (strncmp(marker, "KMCP", 4) != 0)
+	{
+		std::cerr << "Error: wrong start marker in " << file_name << "\n";
+		exit(1);
+	}
+
+	my_fseek(file, -4, SEEK_END);
+	if (fread(marker, 1, 4, file) != 4)
+	{
+		std::cerr << "Error while reading end marker in " << file_name << "\n";
+		exit(1);
+	}
+
+	if (strncmp(marker, "KMCP", 4) != 0)
+	{
+		std::cerr << "Error: wrong end marker in " << file_name << "\n";
+		exit(1);
+	}
+
+	my_fseek(file, 0, SEEK_END);
+	file_size = my_ftell(file);
+
+	my_fseek(file, -8, SEEK_END);
+	load_uint(file, header_offset);
+
+	my_fseek(file, -12, SEEK_END);
+	load_uint(file, db_version);
+
+	my_fseek(file, 0LL - (header_offset + 8), SEEK_END);
+	load_uint(file, kmer_len);
+	load_uint(file, mode);
+	load_uint(file, counter_size);
+	load_uint(file, lut_prefix_len);
+	if (IsKMC2())
+		load_uint(file, signature_len);
+	load_uint(file, min_count);
+	load_uint(file, max_count);
+	load_uint(file, total_kmers);
+	uchar both_s_tmp;
+	load_uint(file, both_s_tmp);
+	both_strands = both_s_tmp == 1;
+	both_strands = !both_strands;
+
+	fclose(file);
+
+	if (IsKMC2())
+	{
+		uint32 single_lut_size = (1ull << (2 * lut_prefix_len)) * sizeof(uint64);
+		uint32 map_size = ((1 << 2 * signature_len) + 1) * sizeof(uint32);
+		no_of_bins = (uint32)((file_size - sizeof(uint64) - 12 - header_offset - map_size) / single_lut_size);
+	}
+}
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/kmc_header.h'
--- old/kmc_tools/kmc_header.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/kmc_header.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,55 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _KMC_HEADER_H
+#define _KMC_HEADER_H
+#include "defs.h"
+#include <string>
+#include <iostream>
+
+//************************************************************************************************************
+// CKMC_header - represents header of KMC database.
+//************************************************************************************************************
+struct CKMC_header
+{
+public:
+	uint32 kmer_len = 0;
+	uint32 mode = 0;
+	uint32 counter_size = 0;
+	uint32 lut_prefix_len = 0;
+	uint32 signature_len = 0; //only for kmc2
+	uint32 min_count = 0;
+	uint32 max_count = 0;
+	uint64 total_kmers = 0;
+	bool both_strands = true;
+	uint32 db_version = 0;
+	uint32 header_offset = 0;
+	uint64 file_size = 0;
+
+	uint32 no_of_bins = 0; //only for kmc2
+	bool IsKMC2()const
+	{
+		return db_version == 0x200;
+	}
+	CKMC_header(std::string file_name);
+
+private:
+	template<typename T> void load_uint(FILE* file, T& res)
+	{
+		res = 0;
+		for (uint32 i = 0; i < sizeof(T); ++i)
+			res += (T)getc(file) << (i << 3);
+	}
+};
+
+#endif
+
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/kmc_tools.cpp'
--- old/kmc_tools/kmc_tools.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/kmc_tools.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,577 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include <iostream>
+#include <vector>
+#include <utility>
+
+#include "config.h"
+#include "check_kmer.h"
+#include "parser.h"
+#include "timer.h"
+#include "kmc1_db_reader.h"
+#include "kmc2_db_reader.h"
+#include "kmc1_db_writer.h"
+#include "parameters_parser.h"
+#include "histogram_writer.h"
+#include "dump_writer.h"
+#include "fastq_reader.h"
+#include "fastq_filter.h"
+#include "fastq_writer.h"
+#ifdef ENABLE_LOGGER
+#include "develop.h"
+#endif
+using namespace std;
+
+
+template<unsigned SIZE> class CTools
+{
+	CParametersParser& parameters_parser;
+	CConfig& config;
+
+	template<typename KMCDB>
+	void ProcessTransformOper(KMCDBOpenMode open_mode)
+	{
+		KMCDB* db = new KMCDB(config.headers.front(), config.input_desc.front(), config.percent_progress, open_mode);
+
+		vector<CKMC1DbWriter<SIZE>*> kmc_db_writers;
+		vector<CBundle<SIZE>*> bundles;
+		vector<CDumpWriterForTransform<SIZE>> dump_writers;
+		vector<CHistogramWriterForTransform> histogram_writers;
+
+		for (auto& desc : config.transform_output_desc)
+		{
+			switch (desc.op_type)
+			{
+			case CTransformOutputDesc::OpType::COMPACT:
+			case CTransformOutputDesc::OpType::REDUCE:
+			case CTransformOutputDesc::OpType::SORT:
+			case CTransformOutputDesc::OpType::SET_COUNTS:
+				kmc_db_writers.push_back(new CKMC1DbWriter<SIZE>(nullptr, desc));
+				kmc_db_writers.back()->MultiOptputInit();
+				bundles.push_back(new CBundle<SIZE>(nullptr));
+				break;
+			case CTransformOutputDesc::OpType::DUMP:
+				dump_writers.emplace_back(desc);
+				dump_writers.back().Init();
+				break;
+			case CTransformOutputDesc::OpType::HISTOGRAM:
+				histogram_writers.emplace_back(desc);
+				histogram_writers.back().Init();
+				break;
+			}
+		}
+		CKmer<SIZE> kmer;
+		uint32 counter;
+		
+		if (open_mode == KMCDBOpenMode::counters_only) // only histogram oper
+		{
+			while (db->NextCounter(counter))
+			{
+				for (auto& out : histogram_writers)
+					out.PutCounter(counter);
+			}
+		}
+		else if (open_mode == KMCDBOpenMode::sequential) //historam or dump only
+		{
+			while (db->NextKmerSequential(kmer, counter))
+			{
+				for (auto& out : histogram_writers)
+					out.PutCounter(counter);
+				for (auto& out : dump_writers)
+					out.PutKmer(kmer, counter);
+			}
+		}
+		else 
+		{
+			CBundle<SIZE> tmp_bundle(db);
+			db = nullptr;
+			while (!tmp_bundle.Finished())
+			{
+				for (auto& out : histogram_writers)
+					out.PutCounter(tmp_bundle.TopCounter());
+				for (auto& out : dump_writers)
+					out.PutKmer(tmp_bundle.TopKmer(), tmp_bundle.TopCounter());
+
+				for (uint32 i = 0; i < bundles.size(); ++i)
+				{
+					bundles[i]->Insert(tmp_bundle.TopKmer(), tmp_bundle.TopCounter());
+					if (bundles[i]->Full())
+					{
+						kmc_db_writers[i]->MultiOptputAddResultPart(*bundles[i]);
+					}
+				}
+
+				tmp_bundle.Pop();				
+			}			
+		}
+
+		for (uint32 i = 0; i < bundles.size(); ++i)
+		{
+			if (!bundles[i]->Empty())
+				kmc_db_writers[i]->MultiOptputAddResultPart(*bundles[i]);
+		}
+
+		for (auto& out : histogram_writers)
+			out.Finish();
+		for (auto& out : dump_writers)		
+			out.Finish();			
+		
+		for (auto& out : kmc_db_writers)
+		{
+			out->MultiOptputFinish();
+			delete out;
+		}
+		for (auto& b : bundles)
+			delete b;
+			
+		delete db;
+	}
+
+	bool info()
+	{
+		auto header = CConfig::GetInstance().headers.front();
+
+		std::cout << "k                 :  " << header.kmer_len <<"\n"
+				  << "total k-mers      :  " << header.total_kmers<< "\n"
+				  << "cutoff max        :  " << header.max_count << "\n"
+				  << "cutoff min        :  " << header.min_count << "\n"
+				  << "counter size      :  " << header.counter_size << " bytes\n"
+				  << "mode              :  " << (header.mode ? "quality-aware counters" : "occurrence counters") << "\n"
+				  << "both strands      :  " << (header.both_strands ? "yes" : "no") << "\n"
+				  << "database format   :  " << (header.IsKMC2() ? "KMC2.x" : "KMC1.x") << "\n"
+				  << "signature length  :  " << header.signature_len << "\n"
+				  << "number of bins    :  " << header.no_of_bins << "\n"
+				  << "lut_prefix_len    :  " << header.lut_prefix_len << "\n";
+		return true;
+	}
+
+	bool filter()
+	{
+		CFilteringParams& filtering_params = config.filtering_params;
+		CFilteringQueues filtering_queues;				
+
+		//set parameters and quques
+		int32 avaiable_threads = config.avaiable_threads;
+		filtering_params.n_readers = max(1, avaiable_threads / 2);
+
+		bool gz_bz2 = false;
+		vector<uint64> file_sizes;
+
+		for (auto& p : filtering_params.input_srcs)
+		{
+			string ext(p.end() - 3, p.end());
+			if (ext == ".gz" || ext == ".bz2")
+			{
+				gz_bz2 = true;				
+			}
+			FILE* tmp = my_fopen(p.c_str(), "rb");
+			if (!tmp)
+			{
+				cerr << "Error: cannot open file: " << p.c_str();
+				exit(1);
+			}
+			my_fseek(tmp, 0, SEEK_END);
+			file_sizes.push_back(my_ftell(tmp));
+			fclose(tmp);
+		}
+		if (gz_bz2)
+		{
+			sort(file_sizes.begin(), file_sizes.end(), greater<uint64>());
+			uint64 file_size_threshold = (uint64)(file_sizes.front() * 0.05);
+			int32 n_allowed_files = 0;
+			for (auto& p : file_sizes)
+			if (p > file_size_threshold)
+				++n_allowed_files;
+			filtering_params.n_readers = MIN(n_allowed_files, MAX(1, avaiable_threads / 2));
+		}
+		else
+			filtering_params.n_readers = 1;
+
+
+
+		avaiable_threads -= filtering_params.n_readers;
+		filtering_params.n_filters = max(1, avaiable_threads);
+		
+		filtering_params.fastq_buffer_size = 1 << 25; 
+
+		filtering_params.mem_part_pmm_fastq_reader = filtering_params.fastq_buffer_size + CFastqReader::OVERHEAD_SIZE;
+		filtering_params.mem_tot_pmm_fastq_reader = filtering_params.mem_part_pmm_fastq_reader * (filtering_params.n_readers + 48);
+
+		filtering_params.mem_part_pmm_fastq_filter = filtering_params.mem_part_pmm_fastq_reader;
+		filtering_params.mem_tot_pmm_fastq_filter = filtering_params.mem_part_pmm_fastq_filter * (filtering_params.n_filters + 48);
+
+		filtering_queues.input_files_queue = new CInputFilesQueue(filtering_params.input_srcs); 
+		filtering_queues.input_part_queue = new CPartQueue(filtering_params.n_readers);
+		filtering_queues.filtered_part_queue = new CPartQueue(filtering_params.n_filters);
+
+		filtering_queues.pmm_fastq_reader = new CMemoryPool(filtering_params.mem_tot_pmm_fastq_reader, filtering_params.mem_part_pmm_fastq_reader); 
+		filtering_queues.pmm_fastq_filter = new CMemoryPool(filtering_params.mem_tot_pmm_fastq_filter, filtering_params.mem_part_pmm_fastq_filter);
+
+
+		filtering_params.kmer_len = config.headers.front().kmer_len;
+
+		vector<thread> readers_ths;
+		vector<thread> filters_ths;
+		vector<unique_ptr<CWFastqFilter>> filters;
+		vector<unique_ptr<CWFastqReader>> readers;
+
+		CKMCFile kmc_api;
+		if (!kmc_api.OpenForRA(config.input_desc.front().file_src))
+		{
+			cerr << "Error: cannot open: " << config.input_desc.front().file_src << " by KMC API\n";
+			exit(1);
+		}
+		kmc_api.SetMinCount(config.input_desc.front().cutoff_min);
+		kmc_api.SetMaxCount(config.input_desc.front().cutoff_max);
+
+		CWFastqWriter writer(filtering_params, filtering_queues);
+		thread writer_th(writer);
+
+		for (uint32 i = 0; i < filtering_params.n_filters; ++i)
+		{
+			filters.push_back(make_unique<CWFastqFilter>(filtering_params, filtering_queues, kmc_api));
+			filters_ths.emplace_back(ref(*filters.back()));
+		}
+
+		for (uint32 i = 0; i < filtering_params.n_readers; ++i)
+		{
+			readers.push_back(make_unique<CWFastqReader>(filtering_params, filtering_queues));
+			readers_ths.emplace_back(ref(*readers.back()));
+		}
+
+		writer_th.join();
+		for (auto& thread : filters_ths)
+			thread.join();
+
+		filters.clear();
+
+
+		for (auto& thread : readers_ths)
+			thread.join();
+
+		readers.clear();
+				
+		delete filtering_queues.input_part_queue;
+		delete filtering_queues.pmm_fastq_reader;
+		delete filtering_queues.pmm_fastq_filter;
+		delete filtering_queues.input_files_queue;
+		delete filtering_queues.filtered_part_queue;
+
+		return true;
+	}
+
+	bool simple_set()
+	{
+		CInput<SIZE> *db1, *db2;
+		if (!config.headers[0].IsKMC2())
+			db1 = new CKMC1DbReader<SIZE>(config.headers[0], config.input_desc[0], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+		else
+			db1 = new CKMC2DbReader<SIZE>(config.headers[0], config.input_desc[0], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+
+		if (!config.headers[1].IsKMC2())
+			db2 = new CKMC1DbReader<SIZE>(config.headers[1], config.input_desc[1], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+		else
+			db2 = new CKMC2DbReader<SIZE>(config.headers[1], config.input_desc[1], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+
+
+		CBundle<SIZE> input1(db1), input2(db2);
+
+
+		vector<COutputBundle<SIZE>*> output_bundles;
+		vector<CKMC1DbWriter<SIZE>*> writers;
+
+		for (uint32 i = 0; i < config.simple_output_desc.size(); ++i)
+		{
+			writers.push_back(new CKMC1DbWriter<SIZE>(nullptr, config.simple_output_desc[i]));
+			writers.back()->MultiOptputInit();
+			output_bundles.push_back(new COutputBundle<SIZE>(config.simple_output_desc[i].op_type, config.simple_output_desc[i].counter_op, *writers.back()));
+		}
+
+
+		CSimpleOperation<SIZE> op(&input1, &input2, output_bundles);
+		op.Process();
+
+		for (auto& writer : writers)
+		{
+			writer->MultiOptputFinish();
+			delete writer;
+		}
+
+		for (auto o : output_bundles)
+			delete o;
+
+		return true;
+	}
+
+	bool check()
+	{
+		CKmerCheck<SIZE> checker(config.headers.front(), config.input_desc.front());
+		return checker.CheckKmer();
+
+	}
+
+	bool transform()
+	{
+		bool kmers_needed = false;
+		bool sort_needed = false;
+
+		//remove not valid
+		if (!config.headers.front().IsKMC2())
+		{
+			auto it = config.transform_output_desc.begin();
+			while (it != config.transform_output_desc.end())
+			{
+				if (it->op_type == CTransformOutputDesc::OpType::SORT)
+				{
+					it = config.transform_output_desc.erase(it);
+					cerr << "Warning: input database is already sorted. Each sort operation will be omitted\n";
+				}
+				else ++it;
+			}
+		}
+		if (!config.transform_output_desc.size())
+		{
+			return false;
+		}
+
+
+		for (auto& desc : config.transform_output_desc)
+		{
+			if (desc.op_type == CTransformOutputDesc::OpType::REDUCE || desc.op_type == CTransformOutputDesc::OpType::COMPACT || desc.op_type == CTransformOutputDesc::OpType::SORT || desc.op_type == CTransformOutputDesc::OpType::SET_COUNTS)
+			{
+				kmers_needed = true;
+				sort_needed = true;
+				break;
+			}
+			if (desc.op_type == CTransformOutputDesc::OpType::DUMP)
+			{
+				kmers_needed = true;
+				if (desc.sorted_output)
+				{
+					sort_needed = true;
+					break;
+				}
+			}
+		}
+
+		KMCDBOpenMode open_mode;
+		if (!kmers_needed)
+			open_mode = KMCDBOpenMode::counters_only;
+		else if (sort_needed)
+			open_mode = KMCDBOpenMode::sorted;
+		else
+			open_mode = KMCDBOpenMode::sequential;
+		if (config.headers.front().IsKMC2())
+			ProcessTransformOper<CKMC2DbReader<SIZE>>(open_mode);
+		else
+			ProcessTransformOper<CKMC1DbReader<SIZE>>(open_mode);
+
+		return true;
+	}
+
+public:
+	CTools(CParametersParser& parameters_parser) :
+		parameters_parser(parameters_parser),
+		config(CConfig::GetInstance())
+	{
+	}
+	bool Process()
+	{
+		if (config.mode == CConfig::Mode::FILTER)
+		{
+			return filter();
+		}
+		else if (config.mode == CConfig::Mode::INFO)
+		{
+			return info();
+		}
+		else if (config.mode == CConfig::Mode::COMPARE)
+		{
+			CInput<SIZE> *db1, *db2;
+			if (!config.headers[0].IsKMC2())
+				db1 = new CKMC1DbReader<SIZE>(config.headers[0], config.input_desc[0], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+			else
+				db1 = new CKMC2DbReader<SIZE>(config.headers[0], config.input_desc[0], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+
+			if (!config.headers[1].IsKMC2())
+				db2 = new CKMC1DbReader<SIZE>(config.headers[1], config.input_desc[1], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+			else
+				db2 = new CKMC2DbReader<SIZE>(config.headers[1], config.input_desc[1], CConfig::GetInstance().percent_progress, KMCDBOpenMode::sorted);
+
+			CBundle<SIZE> input1(db1), input2(db2);
+			CComparer<SIZE> comparer(&input1, &input2);
+
+			bool res = comparer.Equals();
+
+			delete db1;
+			delete db2; 
+			//std::cout << "\n";
+			if (res)
+			{
+				cout << "DB Equals\n";
+				exit(0);
+			}
+			else
+			{
+				cout << "DB Differs\n";
+				exit(1);
+			}
+		}
+		else if (config.mode == CConfig::Mode::TRANSFORM)
+		{
+			return transform();
+		}
+		else if (config.mode == CConfig::Mode::SIMPLE_SET)
+		{
+			return simple_set();
+		}
+		else if (config.mode == CConfig::Mode::CHECK)
+		{
+			return check();
+		}
+		else if(config.mode == CConfig::Mode::COMPLEX)
+		{
+			CExpressionNode<SIZE>* expression_root = parameters_parser.GetExpressionRoot<SIZE>();
+			auto t = expression_root->GetExecutionRoot();
+			delete expression_root;
+			CKMC1DbWriter<SIZE> writer(t, config.output_desc);
+			writer.Process();
+			delete t;
+			return true;
+		}
+		else
+		{
+			//being here means bug
+			std::cerr << "Error: unknown mode\n";
+			exit(1);
+		}
+		return false;
+	}
+};
+
+
+template<unsigned SIZE> class CApplication
+{
+	CApplication<SIZE - 1>* app_1;
+	CTools<SIZE>* tools;
+	bool is_selected;
+	CConfig& config;
+	CParametersParser& parameter_parser;
+public:
+	CApplication(CParametersParser& parameter_parser) :
+		config(CConfig::GetInstance()), parameter_parser(parameter_parser)
+	{
+		is_selected = config.kmer_len <= (int32)SIZE * 32 && config.kmer_len > ((int32)SIZE - 1) * 32;
+
+		app_1 = new CApplication<SIZE - 1>(parameter_parser);
+		if (is_selected)
+		{
+			tools = new CTools<SIZE>(parameter_parser);
+		}
+		else
+		{
+			tools = nullptr;
+		}
+	}
+
+	~CApplication()
+	{
+		delete app_1;
+		if (is_selected)
+			delete tools;
+	}
+
+	bool Process()
+	{
+		if (is_selected)
+			return tools->Process();
+		else
+			return app_1->Process();
+	}
+};
+
+template<> class CApplication<1>
+{
+	CTools<1>* tools;
+	CConfig& config;
+	CParametersParser& parameter_parser;
+	bool is_selected;
+public:
+	CApplication(CParametersParser& parameter_parser) :
+		config(CConfig::GetInstance()), parameter_parser(parameter_parser)
+	{
+		is_selected = config.kmer_len <= 32;
+
+		if (is_selected)
+			tools = new CTools<1>(parameter_parser);
+		else
+			tools = nullptr;
+	}
+	~CApplication<1>()
+	{
+		if (tools)
+			delete tools;
+	}
+	bool Process() {
+		if (is_selected)
+		{
+			return tools->Process();
+		}
+		return false;
+	}
+};
+
+//----------------------------------------------------------------------------------
+// Check if --help or --version was used
+bool help_or_version(int argc, char** argv)
+{
+	const string version = "--version";
+	const string help = "--help";
+	for (int i = 1; i < argc; ++i)
+	{
+		if (argv[i] == version || argv[i] == help)
+			return true;
+	}
+	return false;
+}
+
+int main(int argc, char**argv)
+{
+#ifdef ENABLE_LOGGER 
+	CTimer timer;
+	timer.start();
+#endif 
+
+	if (argc == 1 || help_or_version(argc, argv))
+	{
+		CGeneralUsageDisplayer{}.Display();
+		return 0;
+	}
+
+	CParametersParser params_parser(argc, argv);
+	params_parser.Parse();
+	if (params_parser.validate_input_dbs())
+	{
+		params_parser.SetThreads();
+		CApplication<KMER_WORDS> app(params_parser);
+		app.Process();
+		//cout << "\n";
+	}
+
+#ifdef ENABLE_LOGGER 
+	cout << "RUN TIME: " << timer.get_time() <<"ms\n\n";
+	CLoger::GetLogger().print_stats(); 
+#endif
+}
+
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/kmc_tools.vcxproj'
--- old/kmc_tools/kmc_tools.vcxproj	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/kmc_tools.vcxproj	2020-12-10 18:04:42 +0000
@@ -0,0 +1,216 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{F3B0CC94-9DD0-4642-891C-EA08BDA50260}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>kmc_tools</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v140</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies />
+      <IgnoreSpecificDefaultLibraries>libcmt.lib</IgnoreSpecificDefaultLibraries>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <Profile>true</Profile>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>Use</PrecompiledHeader>
+      <Optimization>Full</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+      <FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <Profile>true</Profile>
+      <AdditionalDependencies />
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClInclude Include="..\kmc_api\kmc_file.h" />
+    <ClInclude Include="..\kmc_api\kmer_api.h" />
+    <ClInclude Include="..\kmc_api\mmer.h" />
+    <ClInclude Include="bundle.h" />
+    <ClInclude Include="check_kmer.h" />
+    <ClInclude Include="config.h" />
+    <ClInclude Include="defs.h" />
+    <ClInclude Include="dump_writer.h" />
+    <ClInclude Include="expression_node.h" />
+    <ClInclude Include="fastq_filter.h" />
+    <ClInclude Include="fastq_reader.h" />
+    <ClInclude Include="fastq_writer.h" />
+    <ClInclude Include="histogram_writer.h" />
+    <ClInclude Include="kmc1_db_reader.h" />
+    <ClInclude Include="kmc1_db_writer.h" />
+    <ClInclude Include="kmc2_db_reader.h" />
+    <ClInclude Include="kmc_header.h" />
+    <ClInclude Include="kmer.h" />
+    <ClInclude Include="libs\bzlib.h" />
+    <ClInclude Include="libs\bzlib_private.h" />
+    <ClInclude Include="libs\zconf.h" />
+    <ClInclude Include="libs\zlib.h" />
+    <ClInclude Include="meta_oper.h" />
+    <ClInclude Include="nc_utils.h" />
+    <ClInclude Include="operations.h" />
+    <ClInclude Include="output_parser.h" />
+    <ClInclude Include="parameters_parser.h" />
+    <ClInclude Include="parser.h" />
+    <ClInclude Include="percent_progress.h" />
+    <ClInclude Include="queues.h" />
+    <ClInclude Include="stdafx.h" />
+    <ClInclude Include="targetver.h" />
+    <ClInclude Include="thread_watch.h" />
+    <ClInclude Include="timer.h" />
+    <ClInclude Include="tokenizer.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\kmc_api\kmc_file.cpp" />
+    <ClCompile Include="..\kmc_api\kmer_api.cpp" />
+    <ClCompile Include="..\kmc_api\mmer.cpp" />
+    <ClCompile Include="fastq_filter.cpp" />
+    <ClCompile Include="fastq_reader.cpp" />
+    <ClCompile Include="fastq_writer.cpp" />
+    <ClCompile Include="kmc_header.cpp" />
+    <ClCompile Include="kmc_tools.cpp" />
+    <ClCompile Include="nc_utils.cpp" />
+    <ClCompile Include="parameters_parser.cpp" />
+    <ClCompile Include="parser.cpp" />
+    <ClCompile Include="percent_progress.cpp" />
+    <ClCompile Include="stdafx.cpp">
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+    </ClCompile>
+    <ClCompile Include="thread_watch.cpp" />
+    <ClCompile Include="tokenizer.cpp" />
+  </ItemGroup>
+  <ItemGroup>
+    <Library Include="libs\libbzip2.lib" />
+    <Library Include="libs\zlibstat.lib" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file

=== added file 'kmc_tools/kmc_tools.vcxproj.filters'
--- old/kmc_tools/kmc_tools.vcxproj.filters	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/kmc_tools.vcxproj.filters	2020-12-10 18:04:42 +0000
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="stdafx.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="targetver.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="bundle.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="config.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="defs.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="dump_writer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="expression_node.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="histogram_writer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="kmc_header.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="kmc1_db_reader.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="kmc1_db_writer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="kmer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="meta_oper.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="nc_utils.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="operations.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="output_parser.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="parameters_parser.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="parser.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="tokenizer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="queues.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="kmc2_db_reader.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="percent_progress.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="libs\bzlib.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="libs\bzlib_private.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="libs\zconf.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="libs\zlib.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="fastq_reader.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="fastq_filter.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\kmc_api\kmc_file.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\kmc_api\mmer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\kmc_api\kmer_api.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="fastq_writer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="timer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="thread_watch.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="check_kmer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="stdafx.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="kmc_tools.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="kmc_header.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="nc_utils.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="parameters_parser.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="parser.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="tokenizer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="fastq_reader.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="fastq_filter.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\kmc_api\kmc_file.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\kmc_api\mmer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\kmc_api\kmer_api.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="fastq_writer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="percent_progress.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="thread_watch.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <Library Include="libs\zlibstat.lib" />
+    <Library Include="libs\libbzip2.lib" />
+  </ItemGroup>
+</Project>
\ No newline at end of file

=== added file 'kmc_tools/kmer.h'
--- old/kmc_tools/kmer.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/kmer.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,652 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _KMER_H
+#define _KMER_H
+
+// Important remark: there is no inheritance here to guarantee that all classes defined here are POD according to C++11
+
+#include "defs.h"
+#include "meta_oper.h"
+#include <string>
+#include <cstring>
+
+// *************************************************************************
+// Ckmer class for k > 32 with classic kmer counting
+template<unsigned SIZE> struct CKmer {
+	unsigned long long data[SIZE];
+
+	typedef unsigned long long data_t;
+	inline void set(const CKmer<SIZE> &x);
+
+	inline void mask(const CKmer<SIZE> &x);
+	inline uint32 end_mask(const uint32 mask);
+	inline void set_2bits(const uint64 x, const uint32 p);
+	inline uchar get_2bits(const uint32 p);
+	inline uchar get_byte(const uint32 p);
+	inline void set_byte(const uint32 p, uchar x);
+	inline void set_bytes(const uint32 p, const uint32 n, uint32 x);
+	inline void set_bits(const uint32 p, const uint32 n, uint64 x);
+
+	inline void increment_at(uint32 suffix_bytes);
+
+	inline void SHL_insert_2bits(const uint64 x);
+	inline void SHR_insert_2bits(const uint64 x, const uint32 p);
+
+	inline void SHR(const uint32 p);
+	inline void SHL(const uint32 p);
+
+	inline uint64 remove_suffix(const uint32 n) const;
+	inline void set_n_1(const uint32 n);
+	inline void set_n_01(const uint32 n);
+
+	inline void store(uchar *&buffer, int32 n);
+	inline void store(uchar *buffer, int32 p, int32 n);
+
+	inline void load(uchar *&buffer, int32 n);
+
+	inline void load_fast(uchar *&buffer, int32 n, bool little_endian);
+
+
+	inline bool operator==(const CKmer<SIZE> &x);
+	inline bool operator<(const CKmer<SIZE> &x)const;
+
+	inline void clear(void);
+
+	inline char get_symbol(int p);
+
+
+	inline void set_prefix(CKmer<SIZE>& rhs, uint32 suffix_bytes);
+};
+
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::set(const CKmer<SIZE> &x)
+{
+#ifdef USE_META_PROG
+	IterFwd([&](const int &i){
+		data[i] = x.data[i];
+	}, uint_<SIZE - 1>());
+#else
+	for (uint32 i = 0; i < SIZE; ++i)
+		data[i] = x.data[i];
+#endif
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::mask(const CKmer<SIZE> &x)
+{
+#ifdef USE_META_PROG
+	IterFwd([&](const int &i){
+		data[i] &= x.data[i];
+	}, uint_<SIZE - 1>());
+#else
+	for (uint32 i = 0; i < SIZE; ++i)
+		data[i] &= x.data[i];
+#endif
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline uint32 CKmer<SIZE>::end_mask(const uint32 mask)
+{
+	return data[0] & mask;
+}
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::set_2bits(const uint64 x, const uint32 p)
+{
+	//	data[p >> 6] |= x << (p & 63);
+	data[p >> 6] += x << (p & 63);
+}
+
+template<unsigned SIZE> inline uchar CKmer<SIZE>::get_2bits(const uint32 p)
+{
+	return (data[p >> 6] >> (p & 63)) & 3;
+}
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::SHR_insert_2bits(const uint64 x, const uint32 p)
+{
+#ifdef USE_META_PROG
+	IterFwd([&](const int &i){
+		data[i] >>= 2;
+		//		data[i] |= data[i+1] << (64-2);
+		data[i] += data[i + 1] << (64 - 2);
+	}, uint_<SIZE - 2>());
+#else
+	for (uint32 i = 0; i < SIZE - 1; ++i)
+	{
+		data[i] >>= 2;
+		//		data[i] |= data[i+1] << (64-2);
+		data[i] += data[i + 1] << (64 - 2);
+	}
+#endif
+	data[SIZE - 1] >>= 2;
+
+	//	data[p >> 6] |= x << (p & 63);
+	data[p >> 6] += x << (p & 63);
+}
+
+
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::SHR(const uint32 p)
+{
+#ifdef USE_META_PROG
+	IterFwd([&](const int &i){
+		data[i] >>= 2 * p;
+		//		data[i] |= data[i+1] << (64-2*p);
+		data[i] += data[i + 1] << (64 - 2 * p);
+	}, uint_<SIZE - 2>());
+#else
+	for (uint32 i = 0; i < SIZE - 1; ++i)
+	{
+		data[i] >>= 2 * p;
+		//		data[i] |= data[i+1] << (64-2*p);
+		data[i] += data[i + 1] << (64 - 2 * p);
+	}
+#endif
+	data[SIZE - 1] >>= 2 * p;
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::SHL(const uint32 p)
+{
+#ifdef USE_META_PROG
+	IterRev([&](const int &i){
+		data[i + 1] <<= p * 2;
+		//		data[i+1] |= data[i] >> (64-p*2);
+		data[i + 1] += data[i] >> (64 - p * 2);
+	}, uint_<SIZE - 2>());
+#else
+	for (uint32 i = SIZE - 1; i > 0; --i)
+	{
+		data[i] <<= p * 2;
+		//		data[i] |= data[i-1] >> (64-p*2);
+		data[i] += data[i - 1] >> (64 - p * 2);
+	}
+#endif
+	data[0] <<= p * 2;
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::SHL_insert_2bits(const uint64 x)
+{
+#ifdef USE_META_PROG
+	IterRev([&](const int &i){
+		data[i + 1] <<= 2;
+		//		data[i+1] |= data[i] >> (64-2);
+		data[i + 1] += data[i] >> (64 - 2);
+	}, uint_<SIZE - 2>());
+#else
+	for (uint32 i = SIZE - 1; i > 0; --i)
+	{
+		data[i] <<= 2;
+		//		data[i] |= data[i-1] >> (64-2);
+		data[i] += data[i - 1] >> (64 - 2);
+	}
+#endif
+	data[0] <<= 2;
+	//	data[0] |= x;
+	data[0] += x;
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline uchar CKmer<SIZE>::get_byte(const uint32 p)
+{
+	return (data[p >> 3] >> ((p << 3) & 63)) & 0xFF;
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::set_byte(const uint32 p, uchar x)
+{
+	//	data[p >> 3] |= ((uint64) x) << ((p & 7) << 3);
+	data[p >> 3] += ((uint64)x) << ((p & 7) << 3);
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::set_bytes(const uint32 p, const uint32 n, uint32 x)
+{
+	data[p >> 3] += ((uint64)x) << ((p & 7) << 3);
+	//if (8 - (p & 7) < n)
+	if((p>>3) != ((p + n - 1) >> 3))
+		data[(p >> 3) + 1] += ((uint64)x) >> ((8 - (p & 7)) << 3);
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::set_bits(const uint32 p, const uint32 n, uint64 x)
+{
+	//	data[p >> 6] |= x << (p & 63);
+	data[p >> 6] += x << (p & 63);
+	if ((p >> 6) != ((p + n - 1) >> 6))
+		//		data[(p >> 6) + 1] |= x >> (64 - (p & 63));
+		data[(p >> 6) + 1] += x >> (64 - (p & 63));
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::increment_at(uint32 suffix_bytes)
+{
+	data[(suffix_bytes) >> 3] += 1ull << ((suffix_bytes & 7) << 3);
+	if (((suffix_bytes) >> 3) < (SIZE - 1) && (data[(suffix_bytes) >> 3]) == 0) //overflow
+		++data[SIZE - 1];
+
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline bool CKmer<SIZE>::operator==(const CKmer<SIZE> &x) {
+	for (uint32 i = 0; i < SIZE; ++i)
+	if (data[i] != x.data[i])
+		return false;
+
+	return true;
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline bool CKmer<SIZE>::operator<(const CKmer<SIZE> &x)const {
+	for (int32 i = SIZE - 1; i >= 0; --i)
+	if (data[i] < x.data[i])
+		return true;
+	else if (data[i] > x.data[i])
+		return false;
+	return false;
+}
+
+
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::clear(void)
+{
+#ifdef USE_META_PROG
+	IterFwd([&](const int &i){
+		data[i] = 0;
+	}, uint_<SIZE - 1>());
+#else	
+	for (uint32 i = 0; i < SIZE; ++i)
+		data[i] = 0;
+#endif
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline uint64 CKmer<SIZE>::remove_suffix(const uint32 n) const
+{
+	uint32 p = n >> 6; // / 64;
+	uint32 r = n & 63;	// % 64;
+
+	if (p == SIZE - 1)
+		return data[p] >> r;
+	else
+		//		return (data[p+1] << (64-r)) | (data[p] >> r);
+		return (data[p + 1] << (64 - r)) + (data[p] >> r);
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::set_n_1(const uint32 n)
+{
+	clear();
+
+	for (uint32 i = 0; i < (n >> 6); ++i)
+		data[i] = ~((uint64)0);
+
+	uint32 r = n & 63;
+
+	if (r)
+		data[n >> 6] = (1ull << r) - 1;
+}
+
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::set_n_01(const uint32 n)
+{
+	clear();
+
+	for (uint32 i = 0; i < n; ++i)
+	if (!(i & 1))
+		//			data[i >> 6] |= (1ull << (i & 63));
+		data[i >> 6] += (1ull << (i & 63));
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::store(uchar *&buffer, int32 n)
+{
+	for (int32 i = n - 1; i >= 0; --i)
+		*buffer++ = get_byte(i);
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::store(uchar *buffer, int32 p, int32 n)
+{
+	for (int32 i = n - 1; i >= 0; --i)
+		buffer[p++] = get_byte(i);
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::load(uchar *&buffer, int32 n)
+{
+	clear();
+	for (int32 i = n - 1; i >= 0; --i)
+		set_byte(i, *buffer++);
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::load_fast(uchar *&buffer, int32 n, bool little_endian)
+{	
+	uint32 p = ((n + 7) >> 3) - 1;
+	for (uint32 i = SIZE - 1; i > p; --i)
+		data[i] = 0;
+	
+	if (!(n & 7))
+		++p;
+	else
+	{
+		memcpy(&data[p], buffer, sizeof(uint64));
+		if (little_endian)
+			data[p] = _bswap_uint64(data[p]);
+		data[p] >>= (sizeof(uint64)-(n & 7)) << 3;
+		buffer += n & 7;
+	}
+	for (int i = p - 1; i >= 0; --i)
+	{
+		memcpy(&data[i], buffer, sizeof(uint64));
+		if (little_endian)
+			data[i] = _bswap_uint64(data[i]);
+		buffer += 8;
+	}
+}
+
+
+// *********************************************************************
+template<unsigned SIZE> inline char CKmer<SIZE>::get_symbol(int p)
+{
+	uint32 x = (data[p >> 5] >> (2 * (p & 31))) & 0x03;
+
+	switch (x)
+	{
+	case 0: return 'A';
+	case 1: return 'C';
+	case 2: return 'G';
+	default: return 'T';
+	}
+}
+
+// *********************************************************************
+template<unsigned SIZE> inline void CKmer<SIZE>::set_prefix(CKmer<SIZE>& rhs, uint32 suffix_bytes)
+{
+	data[(suffix_bytes) >> 3] += rhs.data[(suffix_bytes) >> 3];
+	if (((suffix_bytes) >> 3) < SIZE - 1)
+		data[SIZE - 1] += rhs.data[SIZE - 1];
+}
+// *********************************************************************
+// *********************************************************************
+// *********************************************************************
+// *********************************************************************
+// Ckmer class for k <= 32 with classic kmer counting
+template<> struct CKmer<1> {
+	unsigned long long data;
+
+	typedef unsigned long long data_t;
+	static uint32 QUALITY_SIZE;
+	static uint32 KMER_SIZE;
+
+	void set(const CKmer<1> &x);	
+
+	void mask(const CKmer<1> &x);
+	uint32 end_mask(const uint32 mask);
+	void set_2bits(const uint64 x, const uint32 p);
+	uchar get_2bits(const uint32 p);
+	uchar get_byte(const uint32 p);
+	void set_byte(const uint32 p, uchar x);
+	void set_bytes(const uint32 p, const uint32 n, uint32 x);
+	void set_bits(const uint32 p, const uint32 n, uint64 x);
+
+	void increment_at(uint32 suffix_bytes);
+
+	void SHL_insert_2bits(const uint64 x);
+	void SHR_insert_2bits(const uint64 x, const uint32 p);
+
+	void SHR(const uint32 p);
+	void SHL(const uint32 p);
+
+	uint64 remove_suffix(const uint32 n) const;
+	void set_n_1(const uint32 n);
+	void set_n_01(const uint32 n);
+
+	void store(uchar *&buffer, int32 n);
+	void store(uchar *buffer, int32 p, int32 n);
+
+
+	void load(uchar *&buffer, int32 n);
+
+	void load_fast(uchar *&buffer, int32 n, bool little_endian);
+
+	bool operator==(const CKmer<1> &x);
+	bool operator<(const CKmer<1> &x)const;
+
+	void clear(void);
+
+	inline char get_symbol(int p);
+
+	inline void set_prefix(CKmer<1>& rhs, uint32 suffix_bytes);
+};
+
+
+// *********************************************************************
+inline void CKmer<1>::mask(const CKmer<1> &x)
+{
+	data &= x.data;
+}
+
+
+// *********************************************************************
+inline uint32 CKmer<1>::end_mask(const uint32 mask)
+{
+	return data & mask;
+}
+// *********************************************************************
+inline void CKmer<1>::set(const CKmer<1> &x)
+{
+	data = x.data;
+}
+
+// *********************************************************************
+inline void CKmer<1>::set_2bits(const uint64 x, const uint32 p)
+{
+	//	data |= x << p;
+	data += x << p;
+}
+
+inline uchar CKmer<1>::get_2bits(const uint32 p)
+{
+	return (data >> p) & 3;
+}
+// *********************************************************************
+inline void CKmer<1>::SHR_insert_2bits(const uint64 x, const uint32 p)
+{
+	data >>= 2;
+	//	data |= x << p;
+	data += x << p;
+}
+
+// *********************************************************************
+inline void CKmer<1>::SHR(const uint32 p)
+{
+	data >>= 2 * p;
+}
+
+// *********************************************************************
+inline void CKmer<1>::SHL(const uint32 p)
+{
+	data <<= p * 2;
+}
+// *********************************************************************
+inline void CKmer<1>::SHL_insert_2bits(const uint64 x)
+{
+	//	data = (data << 2) | x;
+	data = (data << 2) + x;
+}
+
+// *********************************************************************
+inline uchar CKmer<1>::get_byte(const uint32 p)
+{
+	return (data >> (p << 3)) & 0xFF;
+}
+
+// *********************************************************************
+inline void CKmer<1>::set_byte(const uint32 p, uchar x)
+{
+	//	data |= ((uint64) x) << (p << 3);
+	data += ((uint64)x) << (p << 3);
+}
+
+// *********************************************************************
+inline void CKmer<1>::set_bytes(const uint32 p, const uint32 /*n*/, uint32 x)
+{
+	data += ((uint64) x) << (p << 3);
+}
+
+// *********************************************************************
+inline void CKmer<1>::set_bits(const uint32 p, const uint32 /*n*/, uint64 x)
+{
+	//	data |= x << p;
+	data += x << p;
+}
+
+// *********************************************************************
+inline void CKmer<1>::increment_at(uint32 suffix_bytes)
+{
+	data += (1ull << (suffix_bytes << 3));
+}
+// *********************************************************************
+inline bool CKmer<1>::operator==(const CKmer<1> &x) {
+	return data == x.data;
+}
+
+// *********************************************************************
+inline bool CKmer<1>::operator<(const CKmer<1> &x)const {
+	return data < x.data;
+}
+
+// *********************************************************************
+inline void CKmer<1>::clear(void)
+{
+	data = 0ull;
+}
+
+// *********************************************************************
+inline uint64 CKmer<1>::remove_suffix(const uint32 n) const
+{
+	return data >> n;
+}
+
+// *********************************************************************
+inline void CKmer<1>::set_n_1(const uint32 n)
+{
+	if (n == 64)
+		data = ~(0ull);
+	else
+		data = (1ull << n) - 1;
+}
+
+// *********************************************************************
+inline void CKmer<1>::set_n_01(const uint32 n)
+{
+	data = 0ull;
+
+	for (uint32 i = 0; i < n; ++i)
+	if (!(i & 1))
+		data += (1ull << i);
+}
+
+// *********************************************************************
+inline void CKmer<1>::store(uchar *&buffer, int32 n)
+{
+	for (int32 i = n - 1; i >= 0; --i)
+		*buffer++ = get_byte(i);
+}
+
+// *********************************************************************
+inline void CKmer<1>::store(uchar *buffer, int32 p, int32 n)
+{
+	for (int32 i = n - 1; i >= 0; --i)
+		buffer[p++] = get_byte(i);
+}
+
+// *********************************************************************
+inline void CKmer<1>::load_fast(uchar *&buffer, int32 n, bool little_endian)
+{
+	//for short k-mers n may be 0
+	if (!n)
+	{
+		data = 0;
+		return;
+	}
+	//It compiles to the same as data = *(uint64*)buffer; ->  mov	rax, QWORD PTR [rcx]
+	//i am not sure about other platforms than x86
+	memcpy(&data, buffer, sizeof(data)); 
+	if (little_endian)
+		data = _bswap_uint64(data);
+	data >>= (sizeof(data)-n) << 3;
+	buffer += n;
+}
+
+// *********************************************************************
+inline void CKmer<1>::load(uchar *&buffer, int32 n)
+{
+	clear();
+
+	if (!n)
+		return;
+	unsigned long long tmp;
+	tmp = 0;
+
+	switch (n-1)
+	{
+	case 7:
+		data = ((uint64) *buffer++) << 56;
+	case 6:
+		tmp = ((uint64) *buffer++) << 48;
+	case 5:
+		data += ((uint64) *buffer++) << 40;
+	case 4:
+		tmp += ((uint64) *buffer++) << 32;
+	case 3:
+		data += ((uint64) *buffer++) << 24;
+	case 2:
+		tmp += ((uint64) *buffer++) << 16;
+	case 1:
+		data += ((uint64) *buffer++) << 8;
+	}
+
+	tmp += *buffer++;
+
+	data += tmp;
+}
+
+// *********************************************************************
+char CKmer<1>::get_symbol(int p)
+{
+	uint32 x = (data >> (2 * p)) & 0x03;
+
+	switch (x)
+	{
+	case 0: return 'A';
+	case 1: return 'C';
+	case 2: return 'G';
+	default: return 'T';
+	}
+}
+
+// *********************************************************************
+void CKmer<1>::set_prefix(CKmer<1>& rhs, uint32 /*suffix_bytes*/)
+{
+	data += rhs.data;
+}
+#endif
+
+// ***** EOF
+
+

=== added file 'kmc_tools/meta_oper.h'
--- old/kmc_tools/meta_oper.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/meta_oper.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,45 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _META_OPER_H
+#define _META_OPER_H
+
+//#include <functional>
+
+
+template <size_t N> struct uint_{ };
+
+// For loop (forward)
+template <size_t N, typename Lambda>
+inline void IterFwd(const Lambda &oper, uint_<N>) {
+	IterFwd(oper, uint_<N - 1>());
+	oper(N);
+}
+
+template <typename Lambda>
+inline void IterFwd(const Lambda &oper, uint_<0>) {
+	oper(0);
+}
+
+// For loop (backward)
+template <size_t N, typename Lambda>
+inline void IterRev(const Lambda &oper, uint_<N>) {
+	oper(N);
+	IterRev(oper, uint_<N - 1>());
+}
+
+template <typename Lambda>
+inline void IterRev(const Lambda &oper, uint_<0>) {
+	oper(0);
+}
+
+#endif
+
+// ***** EOF

=== added file 'kmc_tools/nc_utils.cpp'
--- old/kmc_tools/nc_utils.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/nc_utils.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,17 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "nc_utils.h"
+
+
+uchar CNumericConversions::digits[100000*5];
+int CNumericConversions::powOf10[30];
+CNumericConversions::_si CNumericConversions::_init;
\ No newline at end of file

=== added file 'kmc_tools/nc_utils.h'
--- old/kmc_tools/nc_utils.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/nc_utils.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,118 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include <string>
+#include "defs.h"
+#include <cstring>
+
+#ifndef _NC_UTILS_H
+#define _NC_UTILS_H
+class CNumericConversions {
+public:
+    static uchar digits[100000*5];
+	static int powOf10[30];
+    struct _si {
+        _si()
+        {
+            for(int i = 0; i < 100000; ++i)
+            {
+                int dig = i;
+
+                digits[i*5+4] = '0' + (dig % 10);
+                dig /= 10;
+                digits[i*5+3] = '0' + (dig % 10);
+                dig /= 10;
+                digits[i*5+2] = '0' + (dig % 10);
+                dig /= 10;
+                digits[i*5+1] = '0' + (dig % 10);
+                dig /= 10;
+                digits[i*5+0] = '0' + dig;
+            }
+			powOf10[0] = 1;
+			for(int i = 1 ; i < 30 ; ++i)
+			{
+				powOf10[i] = powOf10[i-1]*10;
+			}
+        }
+    } static _init;
+
+    static int NDigits(uint64 val)
+    {
+        if(val >= 10000)
+            return 5;
+        else if(val >= 1000)
+            return 4;
+        else if(val >= 100)
+            return 3;
+        else if(val >= 10)
+            return 2;
+        else
+            return 1;
+    }
+
+    static int Int2PChar(uint64 val, uchar *str)
+    {
+        if(val >= 1000000000000000ull)
+        {
+            uint64 dig1 = val / 1000000000000000ull;
+            val -= dig1 * 1000000000000000ull;
+            uint64 dig2 = val / 10000000000ull;
+            val -= dig2 * 10000000000ull;
+            uint64 dig3 = val / 100000ull;
+            uint64 dig4 = val - dig3 * 100000ull;
+
+            int ndig = NDigits(dig1);
+
+            memcpy(str, digits+dig1*5+(5-ndig), ndig);
+            memcpy(str+ndig, digits+dig2*5, 5);
+            memcpy(str+ndig+5, digits+dig3*5, 5);
+            memcpy(str+ndig+10, digits+dig4*5, 5);
+
+            return ndig+15;
+        }
+        else if(val >= 10000000000ull)
+        {
+            uint64 dig1 = val / 10000000000ull;
+            val -= dig1 * 10000000000ull;
+            uint64 dig2 = val / 100000ull;
+            uint64 dig3 = val - dig2 * 100000ull;
+
+            int ndig = NDigits(dig1);
+
+            memcpy(str, digits+dig1*5+(5-ndig), ndig);
+            memcpy(str+ndig, digits+dig2*5, 5);
+            memcpy(str+ndig+5, digits+dig3*5, 5);
+
+            return ndig+10;
+        }
+        else if(val >= 100000ull)
+        {
+            uint64 dig1 = val / 100000ull;
+            uint64 dig2 = val - dig1 * 100000ull;
+
+            int ndig = NDigits(dig1);
+
+            memcpy(str, digits+dig1*5+(5-ndig), ndig);
+            memcpy(str+ndig, digits+dig2*5, 5);
+
+            return ndig+5;
+        }
+        else
+        {
+            int ndig = NDigits(val);
+
+            memcpy(str, digits+val*5+(5-ndig), ndig);
+
+            return ndig;
+        }
+	}
+};
+
+#endif
\ No newline at end of file

=== added file 'kmc_tools/operations.h'
--- old/kmc_tools/operations.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/operations.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,579 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _OPERATIONS_H
+#define _OPERATIONS_H
+
+
+#ifdef ENABLE_DEBUG
+#include "config.h"
+#endif
+
+#ifdef ENABLE_LOGGER
+#include "develop.h"
+#endif
+
+#include <iostream>
+#include <vector>
+#include "bundle.h"
+
+//************************************************************************************************************
+// C2ArgOper - abstract class representing 2 argument's operation
+//************************************************************************************************************
+template<unsigned SIZE> class C2ArgOper : public CInput<SIZE>
+{
+protected:	
+	CBundle<SIZE>* input1, *input2;
+	CounterOpType counter_op_type;
+public:
+	C2ArgOper(CBundle<SIZE>* input1, CBundle<SIZE>* input2, CounterOpType counter_op_type) :
+		input1(input1), input2(input2), counter_op_type(counter_op_type)
+	{
+	}
+	void EqualsToOuputBundle(CBundle<SIZE>& output_bundle)
+	{
+		switch (counter_op_type)
+		{
+		case CounterOpType::MIN:
+			output_bundle.Insert(input1->TopKmer(), MIN(input1->TopCounter(), input2->TopCounter()));
+			break;
+		case CounterOpType::MAX:
+			output_bundle.Insert(input1->TopKmer(), MAX(input1->TopCounter(), input2->TopCounter()));
+			break;
+		case CounterOpType::SUM:
+			output_bundle.Insert(input1->TopKmer(), input1->TopCounter() + input2->TopCounter());
+			break;
+		case CounterOpType::DIFF:
+			if (input1->TopCounter() > input2->TopCounter())
+				output_bundle.Insert(input1->TopKmer(), input1->TopCounter() - input2->TopCounter());
+			break;
+		case CounterOpType::FROM_DB1:
+			output_bundle.Insert(input1->TopKmer(), input1->TopCounter());
+			break;
+		case CounterOpType::FROM_DB2:
+			output_bundle.Insert(input1->TopKmer(), input2->TopCounter());
+			break;
+		case CounterOpType::NONE:
+			break;
+		default:
+			break;
+		}
+	}
+	void IgnoreRest() override
+	{		
+		input1->IgnoreRest();
+		input2->IgnoreRest();
+	}
+
+	~C2ArgOper() override
+	{
+		delete input1;
+		delete input2;
+	}
+};
+
+//************************************************************************************************************
+// CUnion - implementation of union operation on 2 k-mer's sets.
+//************************************************************************************************************
+template <unsigned SIZE> class CUnion : public C2ArgOper<SIZE>
+{		
+public:
+	CUnion(CBundle<SIZE>* input1, CBundle<SIZE>* input2, CounterOpType counter_op_type) : C2ArgOper<SIZE>(input1, input2, counter_op_type)
+	{
+	}
+	void NextBundle(CBundle<SIZE>& bundle) override
+	{
+		while (!this->input1->Finished() && !this->input2->Finished())
+		{
+			if (bundle.Full())
+			{
+				return;
+			}	
+			if (this->input1->TopKmer() == this->input2->TopKmer())
+			{
+				this->EqualsToOuputBundle(bundle);
+				this->input1->Pop();
+				this->input2->Pop();
+			}
+			else if (this->input1->TopKmer() < this->input2->TopKmer())
+			{
+				bundle.Insert(this->input1->TopKmer(), this->input1->TopCounter());
+				this->input1->Pop();
+			}
+			else
+			{
+				bundle.Insert(this->input2->TopKmer(), this->input2->TopCounter());
+				this->input2->Pop();
+			}
+		}
+		CBundle<SIZE>* non_empty_bundle = this->input1->Finished() ? this->input2 : this->input1;
+		while (!non_empty_bundle->Finished())
+		{
+			if (bundle.Full())
+			{
+				return;
+			}
+			bundle.Insert(non_empty_bundle->TopKmer(), non_empty_bundle->TopCounter());
+			non_empty_bundle->Pop();
+		}
+		this->finished = true;
+	}
+};
+
+//************************************************************************************************************
+// CIntersection - implementation of intersection operation on 2 k-mer's sets.
+//************************************************************************************************************
+template<unsigned SIZE> class CIntersection : public C2ArgOper<SIZE>
+{		
+public:
+	CIntersection(CBundle<SIZE>* input1, CBundle<SIZE>* input2, CounterOpType counter_op_type) : C2ArgOper<SIZE>(input1, input2, counter_op_type)
+	{
+	}
+	void NextBundle(CBundle<SIZE>& bundle) override
+	{
+		while (!this->input1->Finished() && !this->input2->Finished())
+		{
+			if (this->input1->TopKmer() == this->input2->TopKmer())
+			{
+				this->EqualsToOuputBundle(bundle);
+				this->input1->Pop();
+				this->input2->Pop();
+				if (bundle.Full())
+					return;
+			}
+			else if (this->input1->TopKmer() < this->input2->TopKmer())
+				this->input1->Pop();
+			else
+				this->input2->Pop();
+		}
+		if (!this->input1->Finished())
+			this->input1->IgnoreRest();
+		if (!this->input2->Finished())
+			this->input2->IgnoreRest();
+		this->finished = true;
+	}
+};
+
+//************************************************************************************************************
+// CKmersSubtract - implementation of subtraction operation of 2 k-mer's sets.
+// If k-mer exists in both input it is absent in result (counters does not matter).
+//************************************************************************************************************
+template<unsigned SIZE> class CKmersSubtract : public C2ArgOper<SIZE>
+{	
+public:
+	CKmersSubtract(CBundle<SIZE>* input1, CBundle<SIZE>* input2) : C2ArgOper<SIZE>(input1, input2, CounterOpType::NONE)
+	{
+	}
+	void NextBundle(CBundle<SIZE>& bundle) override
+	{
+		while (!this->input1->Finished() && !this->input2->Finished())
+		{
+			if (this->input2->TopKmer() < this->input1->TopKmer())
+				this->input2->Pop();
+			else if (this->input2->TopKmer() == this->input1->TopKmer())
+			{
+				this->input1->Pop();
+				this->input2->Pop();
+			}
+			else
+			{
+				bundle.Insert(this->input1->TopKmer(), this->input1->TopCounter());
+				this->input1->Pop();
+				if (bundle.Full())
+					return;
+			}
+		}
+		
+		if(!this->input2->Finished())
+			this->input2->IgnoreRest(); 
+
+		while (!this->input1->Finished())
+		{
+			if (bundle.Full())
+				return;
+			bundle.Insert(this->input1->TopKmer(), this->input1->TopCounter());
+			this->input1->Pop();
+		}
+		this->finished = true;
+	}
+};
+
+
+
+
+//************************************************************************************************************
+// CCountersSubtract - implementation of subtraction operation of 2 k-mer's sets.
+// If k-mer exists in both input their counters are subtracted.
+//************************************************************************************************************
+template<unsigned SIZE> class CCountersSubtract : public C2ArgOper<SIZE>
+{
+public:
+	CCountersSubtract(CBundle<SIZE>* input1, CBundle<SIZE>* input2, CounterOpType counter_op_type) : C2ArgOper<SIZE>(input1, input2, counter_op_type)
+	{
+	}
+	void NextBundle(CBundle<SIZE>& bundle) override
+	{
+		while (!this->input1->Finished() && !this->input2->Finished())
+		{
+			if (this->input2->TopKmer() < this->input1->TopKmer())
+				this->input2->Pop();
+			else if (this->input2->TopKmer() == this->input1->TopKmer())
+			{
+				this->EqualsToOuputBundle(bundle);
+				this->input1->Pop();
+				this->input2->Pop();
+				if (bundle.Full())
+					return;
+			}
+			else
+			{
+				bundle.Insert(this->input1->TopKmer(), this->input1->TopCounter());
+				this->input1->Pop();
+				if (bundle.Full())
+					return;
+			}
+		}
+
+		if (!this->input2->Finished())
+			this->input2->IgnoreRest();
+
+		while (!this->input1->Finished())
+		{
+			if (bundle.Full())
+				return;			
+			bundle.Insert(this->input1->TopKmer(), this->input1->TopCounter());
+			this->input1->Pop();
+		}
+		this->finished = true;
+	}
+};
+
+template<unsigned SIZE> class CComparer 
+{
+	CBundle<SIZE>* input1, *input2;
+public:
+	CComparer(CBundle<SIZE>* input1, CBundle<SIZE>* input2) : input1(input1), input2(input2)
+	{
+	}
+
+	bool Equals()
+	{		
+		while (!this->input1->Finished() && !this->input2->Finished())
+		{
+			if (this->input1->TopCounter() != this->input2->TopCounter())
+			{
+				this->input1->IgnoreRest();
+				this->input2->IgnoreRest();
+				return false;
+			}
+			if (!(this->input1->TopKmer() == this->input2->TopKmer()))
+			{
+				this->input1->IgnoreRest();
+				this->input2->IgnoreRest();
+				return false;
+			}
+
+			this->input1->Pop();
+			this->input2->Pop();
+		}
+		if (!this->input1->Finished() || !this->input2->Finished())
+		{
+			std::cout << "one of input is not finished\n";
+			this->input1->IgnoreRest();
+			this->input2->IgnoreRest();
+			return false;
+		}
+		return true;
+	}
+};
+
+template<unsigned SIZE>
+class CSimpleOperation
+{
+	CBundle<SIZE>* input1, *input2;
+	std::vector<COutputBundle<SIZE>*>& outputs;
+
+	std::vector<COutputBundle<SIZE>*> outputs_for_equals;
+	std::vector<COutputBundle<SIZE>*> outputs_for_1st_lower;
+	std::vector<COutputBundle<SIZE>*> outputs_for_2nd_lower;
+
+
+public:
+	CSimpleOperation(CBundle<SIZE>* input1, CBundle<SIZE>* input2, std::vector<COutputBundle<SIZE>*>& outputs) :
+		input1(input1),
+		input2(input2),
+		outputs(outputs)
+	{
+		for (auto o : outputs)
+		{
+			auto op = o->GetOpType();
+			if (op == CSimpleOutputDesc::OpType::INTERSECT || op == CSimpleOutputDesc::OpType::UNION || op == CSimpleOutputDesc::OpType::COUNTERS_SUBTRACTION || op == CSimpleOutputDesc::OpType::REVERSE_COUNTERS_SUBTRACTION)
+				outputs_for_equals.push_back(o);
+			if (op == CSimpleOutputDesc::OpType::UNION || op == CSimpleOutputDesc::OpType::KMERS_SUBTRACTION || op == CSimpleOutputDesc::OpType::COUNTERS_SUBTRACTION)
+				outputs_for_1st_lower.push_back(o);
+			if (op == CSimpleOutputDesc::OpType::UNION || op == CSimpleOutputDesc::OpType::REVERSE_KMERS_SUBTRACTION || op == CSimpleOutputDesc::OpType::REVERSE_COUNTERS_SUBTRACTION)
+				outputs_for_2nd_lower.push_back(o);
+			else
+			{
+				//should never be here
+			}
+		}
+	}
+
+	struct CEqNotifier
+	{
+		static void Notify(CSimpleOperation<SIZE>& operation, CKmer<SIZE>& kmer, uint32 counter1, uint32 counter2)
+		{
+			for (auto output : operation.outputs_for_equals)
+			{
+				uint32 c = 0;
+				if (output->GetOpType() == CSimpleOutputDesc::OpType::REVERSE_COUNTERS_SUBTRACTION)
+					c = output->GetCounter(counter2, counter1);
+				else
+					c = output->GetCounter(counter1, counter2);
+				output->InsertAndSendIfFull(kmer, c);
+			}
+		}
+	};
+	struct CEqNotifierEmpty
+	{
+		static void Notify(CSimpleOperation<SIZE>& /*operation*/, CKmer<SIZE>& /*kmer*/, uint32 /*counter1*/, uint32 /*counter2*/){}
+	};
+
+	struct C1stLowerNotifier
+	{
+		static void Notify(CSimpleOperation<SIZE>& operation, CKmer<SIZE>& kmer, uint32 counter)
+		{
+			for (auto output : operation.outputs_for_1st_lower)
+				output->InsertAndSendIfFull(kmer, counter);
+		}
+	};
+	struct CLowerNotifierEmpty
+	{
+		static void Notify(CSimpleOperation<SIZE>& /*operation*/, CKmer<SIZE>& /*kmer*/, uint32 /*counter*/){}
+	};
+
+	struct C2ndLowerNotifier
+	{
+		static void Notify(CSimpleOperation<SIZE>& operation, CKmer<SIZE>& kmer, uint32 counter)
+		{
+			for (auto output : operation.outputs_for_2nd_lower)
+				output->InsertAndSendIfFull(kmer, counter);
+		}
+	};
+	
+	template<typename EQ_NOTIFIER, typename FIRST_LOWER_NOTIFIER, typename SECOND_LOWER_NOTIFIER>
+	void process_impl()
+	{
+		uint32 get1 = 0;
+		uint32 get2 = 0;
+
+		CKmer<SIZE> kmer2 = this->input2->data.kmers_with_counters[get2].kmer;
+		uint32 counter2 = this->input2->data.kmers_with_counters[get2].counter;
+		CKmer<SIZE> kmer1 = this->input1->data.kmers_with_counters[get1].kmer;
+		uint32 counter1 = this->input1->data.kmers_with_counters[get1].counter;
+
+
+		uint32 left1 = this->input1->NRecLeft();
+		uint32 left2 = this->input2->NRecLeft();
+
+
+		while (true)
+		{
+			if (kmer1 == kmer2)
+			{
+				EQ_NOTIFIER::Notify(*this, kmer1, counter1, counter2);
+
+	
+				++get1;
+				++get2;
+
+				if (--left1)
+				{
+
+					kmer1 = this->input1->data.kmers_with_counters[get1].kmer;
+					counter1 = this->input1->data.kmers_with_counters[get1].counter;
+
+				}
+				else
+				{
+					this->input1->data.get_pos = get1;
+					if (!this->input1->Finished())
+					{
+						get1 = 0;
+						kmer1 = this->input1->data.kmers_with_counters[get1].kmer;
+						counter1 = this->input1->data.kmers_with_counters[get1].counter;
+						left1 = this->input1->NRecLeft();
+					}
+					else
+						break;
+				}
+
+				if (--left2)
+				{
+					kmer2 = this->input2->data.kmers_with_counters[get2].kmer;
+					counter2 = this->input2->data.kmers_with_counters[get2].counter;
+				}
+				else
+				{
+					this->input2->data.get_pos = get2;
+					if (!this->input2->Finished())
+					{
+						get2 = 0;
+						kmer2 = this->input2->data.kmers_with_counters[get2].kmer;
+						counter2 = this->input2->data.kmers_with_counters[get2].counter;
+						left2 = this->input2->NRecLeft();
+						
+					}
+					else
+						break;
+				}
+			}
+			else if (kmer1 < kmer2)
+			{
+
+				FIRST_LOWER_NOTIFIER::Notify(*this, kmer1, counter1);
+				++get1;
+				if (--left1)
+				{
+					kmer1 = this->input1->data.kmers_with_counters[get1].kmer;
+					counter1 = this->input1->data.kmers_with_counters[get1].counter;
+				}
+				else
+				{
+					this->input1->data.get_pos = get1;
+					if (!this->input1->Finished())
+					{
+						get1 = 0;
+						kmer1 = this->input1->data.kmers_with_counters[get1].kmer;
+						counter1 = this->input1->data.kmers_with_counters[get1].counter;
+						left1 = this->input1->NRecLeft();
+					}
+					else
+						break;
+				}
+			}
+			else
+			{
+				SECOND_LOWER_NOTIFIER::Notify(*this, kmer2, counter2);
+
+				++get2;
+				if (--left2)
+				{
+					kmer2 = this->input2->data.kmers_with_counters[get2].kmer;
+					counter2 = this->input2->data.kmers_with_counters[get2].counter;
+				}
+				else
+				{
+					this->input2->data.get_pos = get2;
+					if (!this->input2->Finished())
+					{
+						get2 = 0;
+						kmer2 = this->input2->data.kmers_with_counters[get2].kmer;
+						counter2 = this->input2->data.kmers_with_counters[get2].counter;
+						left2 = this->input2->NRecLeft();
+
+					}
+					else
+						break;
+				}
+			}
+		}
+		this->input1->data.get_pos = get1;
+		this->input2->data.get_pos = get2;
+	}
+
+	void Process()
+	{
+
+#ifdef ENABLE_LOGGER
+		CTimer timer;
+		timer.start();
+#endif
+		bool notify_equal = outputs_for_equals.size() != 0;
+		bool notify_1st_lower = outputs_for_1st_lower.size() != 0;
+		bool notify_2nd_lower = outputs_for_2nd_lower.size() != 0;
+
+
+		if (!this->input1->Finished() && !this->input2->Finished())
+		{
+			if (!notify_equal && !notify_1st_lower && notify_2nd_lower)
+			{
+				process_impl<CEqNotifierEmpty, CLowerNotifierEmpty, C2ndLowerNotifier>();
+			}
+			else if (!notify_equal && notify_1st_lower && !notify_2nd_lower)
+			{
+				process_impl<CEqNotifierEmpty, C1stLowerNotifier, CLowerNotifierEmpty>();
+			}
+			else if (!notify_equal && notify_1st_lower && notify_2nd_lower)
+			{
+				process_impl<CEqNotifierEmpty, C1stLowerNotifier, C2ndLowerNotifier>();
+			}
+			else if (notify_equal && !notify_1st_lower && !notify_2nd_lower)
+			{
+				process_impl<CEqNotifier, CLowerNotifierEmpty, CLowerNotifierEmpty>();
+			}
+			else if (notify_equal && !notify_1st_lower && notify_2nd_lower)
+			{
+				process_impl<CEqNotifier, CLowerNotifierEmpty, C2ndLowerNotifier>();
+			}
+			else if (notify_equal && notify_1st_lower && !notify_2nd_lower)
+			{
+				process_impl<CEqNotifier, C1stLowerNotifier, CLowerNotifierEmpty>();
+			}
+			else if (notify_equal && notify_1st_lower && notify_2nd_lower)
+			{
+				process_impl<CEqNotifier, C1stLowerNotifier, C2ndLowerNotifier>();
+			}
+			else
+			{
+				//sould never be here
+			}
+			
+		}
+
+		if (notify_1st_lower)
+		{
+			while (!this->input1->Finished())
+			{
+				for (auto output : outputs_for_1st_lower)
+					output->InsertAndSendIfFull(this->input1->TopKmer(), this->input1->TopCounter());
+				this->input1->Pop();
+			}
+		}
+		else
+			this->input1->IgnoreRest();
+
+		if (notify_2nd_lower)
+		{
+			while (!this->input2->Finished())
+			{
+				for (auto output : outputs_for_2nd_lower)
+					output->InsertAndSendIfFull(this->input2->TopKmer(), this->input2->TopCounter());
+				this->input2->Pop();
+			}
+		}
+		else
+			this->input2->IgnoreRest();
+
+		for (auto output : outputs)
+			output->NotifyFinish();
+
+
+#ifdef ENABLE_LOGGER				
+		CLoger::GetLogger().log_operation("Process", this, timer.get_time());
+#endif
+	}
+};
+
+
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/output_parser.h'
--- old/kmc_tools/output_parser.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/output_parser.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,214 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _OUTPUT_PARSER_H
+#define _OUTPUT_PARSER_H
+
+#include "defs.h"
+#include <list>
+#include <map>
+#include "tokenizer.h"
+#include "expression_node.h"
+
+
+/*****************************************************************************************************************************/
+// This parser validate below grammar:
+// expr -> term sum_op
+// sum_op -> PLUSMINUS term sum_op
+// sum_op -> TERMINATOR
+// 
+// term -> argument term_op
+// term_op -> MUL argument term_op
+// term_op -> TERMINATOR
+// argument -> VARIABLE
+// argument -> OPEN_BRACKET expr CLOSE_BRACKET
+// This code is based on: https://github.com/mikailsheikh/cogitolearning-examples/tree/master/CogPar
+/*****************************************************************************************************************************/
+
+template<unsigned SIZE> class COutputParser
+{
+	std::list<Token> tokens;
+	const std::map<std::string, uint32>& input;
+	Token curr_token;
+	void nextToken();
+	CExpressionNode<SIZE>* argument();
+	CExpressionNode<SIZE>* term_op(CExpressionNode<SIZE>* left);
+	CExpressionNode<SIZE>* term();
+	CExpressionNode<SIZE>* sum_op(CExpressionNode<SIZE>* left);
+	CExpressionNode<SIZE>* expr();
+
+	void modifier(COperNode<SIZE>* exp);
+public:
+	COutputParser(std::list<Token>& tokens, const std::map<std::string, uint32>& input) :
+		tokens(tokens), input(input)
+	{
+		curr_token = tokens.front();
+	}
+
+	CExpressionNode<SIZE>* Parse();
+};
+
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+template<unsigned SIZE>
+CExpressionNode<SIZE>* COutputParser<SIZE>::Parse()
+{
+	CExpressionNode<SIZE>* res = expr();
+	if (curr_token.second != TokenType::TERMINATOR)
+	{
+		std::cerr << "Error: wrong symbol :" << curr_token.first <<"\n";
+		exit(1);
+	}
+#ifdef ENABLE_DEBUG
+	std::cout << "\n";
+	res->Display();
+#endif
+	return res;
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void COutputParser<SIZE>::nextToken()
+{
+	tokens.pop_front();
+	if (tokens.empty())
+		curr_token.second = TokenType::TERMINATOR;
+	else
+		curr_token = tokens.front();
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CExpressionNode<SIZE>* COutputParser<SIZE>::argument()
+{
+	if (curr_token.second == TokenType::VARIABLE)
+	{
+		//check if this variable was defined
+		auto elem = input.find(curr_token.first);
+		if (elem == input.end())
+		{
+			std::cerr << "Error: variable " << curr_token.first << " was not defined\n";
+			exit(1);
+		}
+		CExpressionNode<SIZE>* res = new CInputNode<SIZE>(elem->second);
+		nextToken();
+		return res;
+	}
+	else if (curr_token.second == TokenType::PARENTHESIS_OPEN)
+	{
+		nextToken();
+		CExpressionNode<SIZE>* res = expr();
+		if (curr_token.second != TokenType::PARENTHESIS_CLOSE)
+		{
+			std::cerr << "Error: close  parenthesis expected, but " << curr_token.first << " found\n";
+			exit(1);
+		}
+		nextToken();
+		return res;
+	}
+	return nullptr;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CExpressionNode<SIZE>* COutputParser<SIZE>::term_op(CExpressionNode<SIZE>* left)
+{
+	if (curr_token.second == TokenType::MUL_OPER)
+	{
+		COperNode<SIZE>* res = new CIntersectionNode<SIZE>;
+		res->AddLeftChild(left);
+		nextToken();
+		modifier(res);
+		auto right = argument();
+		res->AddRightChild(right);
+		return term_op(res);
+	}
+	return left;
+}
+template<unsigned SIZE> CExpressionNode<SIZE>* COutputParser<SIZE>::term()
+{
+	auto left = argument();
+	return term_op(left);
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> void COutputParser<SIZE>::modifier(COperNode<SIZE>* exp)
+{	
+	if (curr_token.second == TokenType::DIFF_MODIFIER)
+	{
+		exp->SetCounterOpType(CounterOpType::DIFF);
+		nextToken();
+	}
+	else if (curr_token.second == TokenType::LEFT_MODIFIER)
+	{
+		exp->SetCounterOpType(CounterOpType::FROM_DB1);
+		nextToken();
+	}
+	else if (curr_token.second == TokenType::MAX_MODIFIER)
+	{
+		exp->SetCounterOpType(CounterOpType::MAX);
+		nextToken();
+	}
+	else if (curr_token.second == TokenType::MIN_MODIFIER)
+	{
+		exp->SetCounterOpType(CounterOpType::MIN);
+		nextToken();
+	}
+	else if (curr_token.second == TokenType::RIGHT_MODIFIER)
+	{
+		exp->SetCounterOpType(CounterOpType::FROM_DB2);
+		nextToken();
+	}
+	else if (curr_token.second == TokenType::SUM_MODIFIER)
+	{
+		exp->SetCounterOpType(CounterOpType::SUM);
+		nextToken();
+	}
+}
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CExpressionNode<SIZE>* COutputParser<SIZE>::sum_op(CExpressionNode<SIZE>* left)
+{
+	if (curr_token.second == TokenType::PLUS_OPER || curr_token.second == TokenType::STRICT_MINUS_OPER || curr_token.second == TokenType::COUNTER_MINUS_OPER)
+	{
+		COperNode<SIZE>* res = nullptr;
+		if (curr_token.second == TokenType::PLUS_OPER)
+			res = new CUnionNode<SIZE>;
+		else if (curr_token.second == TokenType::STRICT_MINUS_OPER)
+			res = new CKmersSubtractionNode<SIZE>;
+		else
+			res = new CCountersSubtractionNode<SIZE>;
+		res->AddLeftChild(left);		
+		bool modifier_allowed = !(curr_token.second == TokenType::STRICT_MINUS_OPER);
+		nextToken();		
+		if(modifier_allowed)
+			modifier(res);
+		auto right = term();
+		res->AddRightChild(right);
+		return sum_op(res);
+	}
+	return left;
+}
+
+/*****************************************************************************************************************************/
+template<unsigned SIZE> CExpressionNode<SIZE>* COutputParser<SIZE>::expr()
+{
+	auto left = term();
+	return sum_op(left);
+}
+
+
+
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/parameters_parser.cpp'
--- old/kmc_tools/parameters_parser.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/parameters_parser.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,854 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "parameters_parser.h"
+#include <iostream>
+using namespace std;
+
+
+uint32 CParametersParser::replace_zero(uint32 val, const char* param_name, uint32 value_to_set_if_zero)
+{
+	if (val == 0)
+	{
+		cerr << "Warning: min value for " << param_name << " is " << value_to_set_if_zero << ". Your value will be converted to " << value_to_set_if_zero << "\n";
+		return value_to_set_if_zero;
+	}
+	return val;
+}
+
+
+void CParametersParser::parse_int_or_float(bool& force_float, bool& force_int, float& float_value, uint32& int_val, const char* param_name)
+{
+	if (strchr(argv[pos] + 3, '.'))
+	{
+		float_value = (float)atof(argv[pos++] + 3);
+		if (float_value > 1.0f || float_value < 0.0f)
+		{
+			cerr << "Error: wrong value for fastq input parameter: "<< param_name <<"\n";
+			exit(1);
+		}
+		if (force_int)
+		{
+			cerr << "Error: both -ci, -cx must be specified as real number [0;1] or as integer \n";
+			exit(1);
+		}
+		force_float = true;
+		config.filtering_params.use_float_value = true;
+	}
+	else
+	{
+		int_val = atoi(argv[pos++] + 3);
+		if (force_float)
+		{
+			cerr << "Error: both -ci, -cx must be specified as real number [0;1] or as integer \n";
+			exit(1);
+		}
+		force_int = true;
+		config.filtering_params.use_float_value = false;
+	}
+}
+
+void CParametersParser::parse_global_params()
+{
+	//defaults
+	config.avaiable_threads = thread::hardware_concurrency();
+
+	//override defaults if specified
+	for( ; pos < argc && argv[pos][0] == '-' ; ++pos)
+	{
+		if (strncmp(argv[pos], "-t", 2) == 0)
+		{
+			if (strlen(argv[pos]) < 3)
+			{
+				std::cerr << "Error: -t require value\n";
+				exit(1);
+			}
+			config.avaiable_threads = atoi(argv[pos] + 2);
+			continue;
+		}
+		else if (argv[pos][1] == 'v')
+		{
+			config.verbose = true;
+			continue;
+		}
+		else if (strncmp(argv[pos], "-hp", 3) == 0)
+		{
+			config.percent_progress.Hide();
+			continue;
+		}
+		else
+		{
+			std::cerr << "Error: unknown global option " << argv[pos] << "\n";
+			exit(1);
+		}
+	}
+}
+
+void CParametersParser::read_input_fastq_desc()
+{
+	if (pos >= argc)
+	{
+		cerr << "Error: Input fastq files(s) missed\n";
+		exit(1);
+	}
+	if (strncmp(argv[pos], "-", 1) == 0)
+	{
+		cerr << "Error: Input fastq file(s) required, but " << argv[pos] << " found\n";
+		exit(1);
+	}
+	string input_file_name = argv[pos++];
+	if (input_file_name[0] != '@')
+		config.filtering_params.input_srcs.push_back(input_file_name);
+	else
+	{
+		ifstream in(input_file_name.c_str() + 1);
+		if (!in.good())
+		{
+			cerr << "Error: No " << input_file_name.c_str() + 1 << " file\n";
+			exit(1);
+		}
+		string s;
+		while (getline(in, s))
+		{
+			if (s != "")
+				config.filtering_params.input_srcs.push_back(s);
+		}
+		in.close();
+	}
+
+	bool force_float = false;
+	bool force_int = false;
+	
+	for (int i = 0; i < 3 && pos < argc; ++i)
+	{
+		if(argv[pos][0] != '-')
+			break;
+		if (strncmp(argv[pos], "-ci", 3) == 0)
+		{
+			parse_int_or_float(force_float, force_int, config.filtering_params.f_min_kmers, config.filtering_params.n_min_kmers, "-ci");
+		}
+		else if (strncmp(argv[pos], "-cx", 3) == 0)
+		{
+			parse_int_or_float(force_float, force_int, config.filtering_params.f_max_kmers, config.filtering_params.n_max_kmers, "-cx");
+		}
+		else if (strncmp(argv[pos], "-f", 2) == 0)
+		{
+			switch (argv[pos++][2])
+			{
+			case 'a':
+				config.filtering_params.input_file_type = CFilteringParams::file_type::fasta;
+				break;
+			case 'q':
+				config.filtering_params.input_file_type = CFilteringParams::file_type::fastq;
+				break;
+			default:
+				cerr << "Error: unknow parameter " << argv[pos - 1] << "\n";
+				exit(1);
+				break;
+			}
+		}
+	}
+	config.filtering_params.output_file_type = config.filtering_params.input_file_type;
+}
+
+
+void CParametersParser::read_output_fastq_desc()
+{
+	if (pos >= argc)
+	{
+		cerr << "Error: Output fastq source missed\n";
+		exit(1);
+	}
+	if (strncmp(argv[pos], "-", 1) == 0)
+	{
+		cerr << "Error: Output fastq source required, but " << argv[pos] << "found\n";
+		exit(1);
+	}
+	config.filtering_params.output_src = argv[pos++];
+
+	while (pos < argc && argv[pos][0] == '-')
+	{
+		if (strncmp(argv[pos], "-f", 2) == 0)
+		{
+			switch (argv[pos][2])
+			{
+			case 'q':
+				config.filtering_params.output_file_type = CFilteringParams::file_type::fastq;
+				break;
+			case 'a':
+				config.filtering_params.output_file_type = CFilteringParams::file_type::fasta;
+				break;
+			default:
+				cerr << "Error: unknown parameter " << argv[pos] << "\n";
+				exit(1);
+				break;
+			}
+			if (config.filtering_params.input_file_type == CFilteringParams::file_type::fasta && config.filtering_params.output_file_type == CFilteringParams::file_type::fastq)
+			{
+				cerr << "Error: cannot set -fq for output when -fa is set for input\n";
+				exit(1);
+			}
+		}
+		else
+		{
+			cerr << "Error: Unknown parameter: " << argv[pos] << "\n";
+			exit(1);
+		}
+		++pos;
+	}
+}
+
+void CParametersParser::read_filter_params()
+{
+	while (pos < argc && argv[pos][0] == '-')
+	{
+		if (strncmp(argv[pos], "-t", 2) == 0)
+		{
+			config.filtering_params.filter_mode = CFilteringParams::FilterMode::trim;
+		}
+		else if (strncmp(argv[pos], "-hm", 3) == 0)
+		{
+			config.filtering_params.filter_mode = CFilteringParams::FilterMode::hard_mask;
+		}
+		else
+		{
+			cerr << "Warning: Unknow parameter for filter operation: " << argv[pos] << "\n";
+		}
+		++pos;
+	}
+}
+
+void CParametersParser::read_check_params()
+{
+	if (pos >= argc)
+	{
+		std::cerr << "Error: check operation require k-mer to check\n";
+		exit(1);
+	}
+	config.check_params.kmer = argv[pos++];
+}
+
+void CParametersParser::read_input_desc()
+{
+	if (pos >= argc)
+	{
+		cerr << "Error: Input database source missed\n";
+		exit(1);
+	}
+	if (strncmp(argv[pos], "-", 1) == 0)
+	{
+		cerr << "Error: Input database source required, but " << argv[pos] << "found\n";
+		exit(1);
+	}
+	CInputDesc desc(argv[pos++]);
+	config.input_desc.push_back(desc);
+	for (int i = 0; i < 2 && pos < argc; ++i)
+	{
+		if (strncmp(argv[pos], "-", 1) != 0)
+			break;
+		if (strncmp(argv[pos], "-ci", 3) == 0)
+		{
+			config.input_desc.back().cutoff_min = replace_zero(atoi(argv[pos++] + 3), "-ci", 1);
+		}
+		else if (strncmp(argv[pos], "-cx", 3) == 0)
+		{
+			config.input_desc.back().cutoff_max = replace_zero(atoi(argv[pos++] + 3), "-cx", 1);
+		}
+		else
+		{
+			cerr << "Error: Unknow parameter: " << argv[pos];
+			exit(1);
+		}
+	}
+}
+
+bool CParametersParser::read_output_for_transform()
+{
+	if (pos >= argc)
+		return false;
+
+	CTransformOutputDesc::OpType op_type;
+	uint64 counter_value = 0; //for set_counts only
+	if (strcmp(argv[pos], "sort") == 0)
+	{
+		op_type = CTransformOutputDesc::OpType::SORT;	
+	}
+	else if (strcmp(argv[pos], "reduce") == 0)
+	{
+		op_type = CTransformOutputDesc::OpType::REDUCE;
+	}
+	else if (strcmp(argv[pos], "compact") == 0)
+	{
+		op_type = CTransformOutputDesc::OpType::COMPACT;
+	}
+	else if (strcmp(argv[pos], "histogram") == 0)
+	{
+		op_type = CTransformOutputDesc::OpType::HISTOGRAM;
+	}
+	else if (strcmp(argv[pos], "dump") == 0)
+	{
+		op_type = CTransformOutputDesc::OpType::DUMP;
+	}
+	else if (strcmp(argv[pos], "set_counts") == 0)
+	{
+		op_type = CTransformOutputDesc::OpType::SET_COUNTS;
+		++pos;
+		if (pos >= argc)
+		{
+			cerr << "Error: set_counts operation requires count value\n";
+			Usage();
+			exit(1);
+		}
+		
+		if (strncmp(argv[pos], "-", 1) == 0)
+		{
+			cerr << "Error: Count value expected, but " << argv[pos] << " found\n";
+			exit(1);
+		}
+
+		char* ptr_end = nullptr;
+		counter_value = strtoull(argv[pos], &ptr_end, 10);
+		auto len = strlen(argv[pos]);
+		if (argv[pos] + len != ptr_end)
+		{
+			cerr << "Error: Count value expected, but " << argv[pos] << " found\n";
+			exit(1);
+		}		
+
+		if (counter_value > numeric_limits<uint32>::max())
+		{
+			cerr << "Error: currenlty kmc_tools supports counter values up to " << numeric_limits<uint32>::max() << "\n";
+			exit(1);
+		}
+
+	}
+	else
+	{
+		cerr << "Error: unknown operation: " << argv[pos] << "\n";
+		Usage();
+		exit(1);
+	}
+
+	config.transform_output_desc.emplace_back(op_type);
+
+	++pos;
+
+	//read op paramts
+	while (pos < argc)
+	{
+		if (strncmp(argv[pos], "-", 1) != 0)
+			break;
+		if (strncmp(argv[pos], "-s", 3) == 0)
+		{
+			if (config.transform_output_desc.back().op_type == CTransformOutputDesc::OpType::DUMP)
+			{
+				config.transform_output_desc.back().sorted_output = true;
+			}
+			else
+			{
+				cerr << "Error: -s parameter allowed only for dump operation\n";
+				Usage();
+				exit(1);
+			}
+		}
+		else
+		{
+			cerr << "Error: unknown operation parameter: " << argv[pos] <<"\n";
+			exit(1);
+		}
+		++pos;
+	}
+
+	if (pos >= argc)
+	{
+		cerr << "Error: Output database path missed\n";
+		exit(1);
+	}
+	if (strncmp(argv[pos], "-", 1) == 0)
+	{
+		cerr << "Error: Output database path required, but " << argv[pos] << " found\n";
+		exit(1);
+	}
+
+	config.transform_output_desc.back().file_src = argv[pos++];
+
+	if (op_type == CTransformOutputDesc::OpType::SET_COUNTS)
+		config.transform_output_desc.back().counter_value = counter_value;
+
+	//read database params
+	while (pos < argc)
+	{
+		if (strncmp(argv[pos], "-", 1) != 0)
+			break;
+		if (strncmp(argv[pos], "-ci", 3) == 0)
+		{
+			config.transform_output_desc.back().cutoff_min = replace_zero(atoi(argv[pos++] + 3), "-ci", 1);
+		}
+		else if (strncmp(argv[pos], "-cx", 3) == 0)
+		{
+			config.transform_output_desc.back().cutoff_max = replace_zero(atoi(argv[pos++] + 3), "-cx", 1);
+		}
+		else if (strncmp(argv[pos], "-cs", 3) == 0)
+		{
+			config.transform_output_desc.back().counter_max = replace_zero(atoi(argv[pos++] + 3), "-cs", 1);
+		}
+		else
+		{
+			cerr << "Error: Unknown parameter: " << argv[pos];
+			Usage();
+			exit(1);
+		}
+	}
+	if (op_type == CTransformOutputDesc::OpType::COMPACT)
+	{
+		if (config.transform_output_desc.back().counter_max)
+			cerr << "Warning: -cs can not be specified for compact operation, value specified will be ignored\n";
+		config.transform_output_desc.back().counter_max = 1;
+	}	
+	if (op_type == CTransformOutputDesc::OpType::SET_COUNTS)
+	{
+		auto& tmp = config.transform_output_desc.back();
+		if (tmp.counter_max || tmp.cutoff_max || tmp.cutoff_min)
+			cerr << "Warning: -cs, -cx, -ci cannot be specified for set_counts operation, values will be ignored\n";
+		tmp.counter_max = tmp.cutoff_max = numeric_limits<uint32>::max();
+		tmp.cutoff_min = 1;
+	}
+
+	return true;
+}
+
+bool CParametersParser::read_output_desc_for_simple()
+{
+	if (pos >= argc)
+		return false;
+
+	//get op name
+	CSimpleOutputDesc::OpType op_type;
+	if (strcmp(argv[pos], "intersect") == 0)
+	{
+		op_type = CSimpleOutputDesc::OpType::INTERSECT;
+	}
+	else if (strcmp(argv[pos], "kmers_subtract") == 0)
+	{
+		op_type = CSimpleOutputDesc::OpType::KMERS_SUBTRACTION;
+	}
+	else if (strcmp(argv[pos], "counters_subtract") == 0)
+	{
+		op_type = CSimpleOutputDesc::OpType::COUNTERS_SUBTRACTION;
+	}
+	else if (strcmp(argv[pos], "union") == 0)
+	{
+		op_type = CSimpleOutputDesc::OpType::UNION;
+	}
+	else if (strcmp(argv[pos], "reverse_kmers_subtract") == 0)
+	{
+		op_type = CSimpleOutputDesc::OpType::REVERSE_KMERS_SUBTRACTION;
+	}
+	else if (strcmp(argv[pos], "reverse_counters_subtract") == 0)
+	{
+		op_type = CSimpleOutputDesc::OpType::REVERSE_COUNTERS_SUBTRACTION;
+	}
+	else
+	{
+		cerr << "Error: unknown operation: " << argv[pos] << "\n";
+		Usage();
+		exit(1);
+	}
+
+	++pos;
+	if (pos >= argc)
+	{
+		cerr << "Error: Output database path missed\n";
+		exit(1);
+	}
+	if (strncmp(argv[pos], "-", 1) == 0)
+	{
+		cerr << "Error: Output database path required, but " << argv[pos] << " found\n";
+		exit(1);
+	}
+	config.simple_output_desc.emplace_back(op_type);
+	config.simple_output_desc.back().file_src = argv[pos++];
+
+	while (pos < argc)
+	{
+		if (strncmp(argv[pos], "-", 1) != 0)
+			break;
+		if (strncmp(argv[pos], "-ci", 3) == 0)
+		{
+			config.simple_output_desc.back().cutoff_min = replace_zero(atoi(argv[pos++] + 3), "-ci", 1);
+		}
+		else if (strncmp(argv[pos], "-cx", 3) == 0)
+		{
+			config.simple_output_desc.back().cutoff_max = replace_zero(atoi(argv[pos++] + 3), "-cx", 1);
+		}
+		else if (strncmp(argv[pos], "-cs", 3) == 0)
+		{
+			config.simple_output_desc.back().counter_max = replace_zero(atoi(argv[pos++] + 3), "-cs", 1);
+		}
+		else if (strncmp(argv[pos], "-oc", 3) == 0)
+		{
+			if (op_type == CSimpleOutputDesc::OpType::KMERS_SUBTRACTION || op_type == CSimpleOutputDesc::OpType::REVERSE_KMERS_SUBTRACTION)
+			{
+				cerr << "Error: -oc not allowed for kmers_subtract and reverse_kmers_subtract as it doesn't make sense (equal k-mers form both input will not be present in output)\n";
+				exit(1);
+			}
+			char* mode = argv[pos] + 3;
+			if (strcmp(mode, "min") == 0)
+			{
+				config.simple_output_desc.back().counter_op = CounterOpType::MIN;
+			}
+			else if (strcmp(mode, "max") == 0)
+			{
+				config.simple_output_desc.back().counter_op = CounterOpType::MAX;
+			}
+			else if (strcmp(mode, "sum") == 0)
+			{
+				config.simple_output_desc.back().counter_op = CounterOpType::SUM;
+			}
+			else if (strcmp(mode, "diff") == 0)
+			{
+				config.simple_output_desc.back().counter_op = CounterOpType::DIFF;
+			}
+			else if (strcmp(mode, "left") == 0)
+			{
+				config.simple_output_desc.back().counter_op = CounterOpType::FROM_DB1;
+			}
+			else if (strcmp(mode, "right") == 0)
+			{
+				config.simple_output_desc.back().counter_op = CounterOpType::FROM_DB2;
+			}
+			else
+			{
+				cerr << "Error: unknown counter calculation mode: " << mode << ". Allowed values: min, max, sum, diff, left, right\n";
+				exit(1);
+			}
+			++pos;
+		}
+		else
+		{
+			cerr << "Error: Unknow parameter: " << argv[pos];
+			Usage();
+			exit(1);
+		}
+
+	}
+	return true;
+}
+
+void CParametersParser::Usage()
+{
+	CUsageDisplayerFactory disp(CConfig::GetInstance().mode);
+	disp.GetUsageDisplayer().Display();
+}
+
+CParametersParser::CParametersParser(int argc, char** argv) :argc(argc), argv(argv), config(CConfig::GetInstance())
+{
+	pos = 0;
+	if (argc < 2)
+	{
+		Usage();
+		exit(1);
+	}
+}
+
+void CParametersParser::Parse()
+{
+	pos = 1;
+	parse_global_params();
+
+	if (strcmp(argv[pos], "complex") == 0)
+	{
+		config.mode = CConfig::Mode::COMPLEX;
+	}
+	else if (strcmp(argv[pos], "transform") == 0)
+	{
+		config.mode = CConfig::Mode::TRANSFORM;
+	}
+	else if (strcmp(argv[pos], "simple") == 0)
+	{
+		config.mode = CConfig::Mode::SIMPLE_SET;
+	}
+	else if (strcmp(argv[pos], "compare") == 0)
+	{
+		config.mode = CConfig::Mode::COMPARE;
+	}
+	else if (strcmp(argv[pos], "filter") == 0)
+	{
+		config.mode = CConfig::Mode::FILTER;
+	}
+	else if (strcmp(argv[pos], "info") == 0)
+	{
+		config.mode = CConfig::Mode::INFO;
+	}
+	else if (strcmp(argv[pos], "check") == 0)
+	{
+		config.mode = CConfig::Mode::CHECK;
+	}
+	else
+	{
+		cerr << "Error: Unknow mode: " << argv[pos] << "\n";
+		Usage();
+		exit(1);
+	}
+
+	if (argc == 2)
+	{
+		Usage();
+		exit(1);
+	}
+
+	pos++;
+	if (config.mode == CConfig::Mode::FILTER)
+	{
+		read_filter_params();
+		read_input_desc(); //kmc db
+		read_input_fastq_desc(); //fastq input
+		read_output_fastq_desc();
+		if (config.filtering_params.use_float_value && config.filtering_params.filter_mode != CFilteringParams::FilterMode::normal)
+		{
+			cerr << "Error: trim (-t) and soft mask (-hm) are not compatibile with float values of cut off (-ci -cx)\n";
+			exit(1);
+		}
+	}
+	else if (config.mode == CConfig::Mode::COMPLEX)
+	{
+		if (strncmp(argv[pos], "-", 1) == 0)
+		{
+			cerr << "Error: operations description file expected but " << argv[2] << " found\n";
+			exit(1);
+		}		
+		complex_parser = make_unique<CParser>(argv[pos]);
+		complex_parser->ParseInputs();
+		complex_parser->ParseOutput();
+	}
+	else if (config.mode == CConfig::Mode::TRANSFORM)
+	{
+		read_input_desc();
+		while (read_output_for_transform())
+			;
+
+		if (config.transform_output_desc.size() == 0)
+		{
+			cerr << "Error: output missed\n";
+			Usage();
+			exit(1);
+		}
+
+	}
+	else if (config.mode == CConfig::Mode::SIMPLE_SET)
+	{
+		read_input_desc(); //first input
+		read_input_desc(); //second input
+		while (read_output_desc_for_simple())
+			;
+		if (config.simple_output_desc.size() == 0)
+		{
+			cerr << "Error: output missed\n";
+			Usage();
+			exit(1);
+		}
+	}
+	else if (config.mode == CConfig::Mode::INFO)
+	{
+		read_input_desc();
+	}
+	else if (config.mode == CConfig::Mode::CHECK)
+	{
+		read_input_desc();
+		read_check_params();		
+	}
+	else if (config.mode == CConfig::Mode::COMPARE)
+	{
+		read_input_desc();
+		read_input_desc();
+	}
+}
+
+uint32 CParametersParser::get_min_cutoff_min()
+{
+	uint32 min_cutoff_min = config.input_desc.front().cutoff_min;
+	for (uint32 i = 0; i < config.input_desc.size(); ++i)
+	{
+		if (config.input_desc[i].cutoff_min < min_cutoff_min)
+			min_cutoff_min = config.input_desc[i].cutoff_min;
+	}
+	return min_cutoff_min;
+}
+
+uint32 CParametersParser::get_max_cutoff_max()
+{
+	uint32 max_cutoff_max = config.input_desc.front().cutoff_max;
+	for (uint32 i = 0; i < config.input_desc.size(); ++i)
+	{
+		if (config.input_desc[i].cutoff_max > max_cutoff_max)
+			max_cutoff_max = config.input_desc[i].cutoff_max;
+	}
+	return max_cutoff_max;
+}
+
+uint32 CParametersParser::get_max_counter_max()
+{
+	uint32 max_counter_max = config.headers.front().counter_size;
+	for (uint32 i = 0; i < config.headers.size(); ++i)
+	{
+		if (config.headers[i].counter_size> max_counter_max)
+			max_counter_max = config.headers[i].counter_size;
+	}
+	max_counter_max = (uint32)((1ull << (max_counter_max << 3)) - 1);
+	return max_counter_max;
+}
+
+bool CParametersParser::validate_input_dbs()
+{
+	config.headers.push_back(CKMC_header(config.input_desc.front().file_src));
+
+	uint32 kmer_len = config.headers.front().kmer_len;
+	uint32 mode = config.headers.front().mode;
+	if (mode == 1)
+	{
+		cerr << "Error: quality counters are not supported in kmc tools\n"; 
+		return false;
+	}
+	for (uint32 i = 1; i < config.input_desc.size(); ++i)
+	{
+		config.headers.push_back(CKMC_header(config.input_desc[i].file_src));
+		CKMC_header& h = config.headers.back();
+		if (h.mode != mode)
+		{
+			cerr << "Error: quality/direct based counters conflict!\n";
+			return false;
+		}
+		if (h.kmer_len != kmer_len)
+		{
+			cerr << "Database " << config.input_desc.front().file_src << " contains " << kmer_len << "-mers, but database " << config.input_desc[i].file_src << " contains " << h.kmer_len << "-mers\n";
+			return false;
+		}
+	}
+	config.kmer_len = kmer_len;
+
+
+	//update cutoff_min and coutoff_max if it was not set with parameters
+	for (uint32 i = 0; i < config.input_desc.size(); ++i)
+	{
+		if (config.input_desc[i].cutoff_min == 0)
+			config.input_desc[i].cutoff_min = config.headers[i].min_count;
+		if (config.input_desc[i].cutoff_max == 0)
+			config.input_desc[i].cutoff_max = config.headers[i].max_count;
+	}
+
+	//update output description if it was not set with parameters
+	if (config.mode == CConfig::Mode::SIMPLE_SET)
+	{
+		uint32 min_cutoff_min = get_min_cutoff_min();
+		uint32 max_cutoff_max = get_max_cutoff_max();
+		uint32 max_counter_max = get_max_counter_max();
+		
+		for (auto& desc : config.simple_output_desc)
+		{
+			if (desc.cutoff_min == 0)
+				desc.cutoff_min = min_cutoff_min;
+			if (desc.cutoff_max == 0)
+				desc.cutoff_max = max_cutoff_max;
+			if (desc.counter_max == 0)
+				desc.counter_max = max_counter_max;
+		}
+	}
+	else if (config.mode == CConfig::Mode::TRANSFORM)
+	{
+		uint32 min_cutoff_min = get_min_cutoff_min();
+		uint32 max_cutoff_max = get_max_cutoff_max();
+		uint32 max_counter_max = get_max_counter_max();
+
+		for (auto& desc : config.transform_output_desc)
+		{
+			if (desc.op_type == CTransformOutputDesc::OpType::SET_COUNTS)
+				continue;
+
+			if (desc.cutoff_min == 0)
+				desc.cutoff_min = min_cutoff_min;
+			if (desc.cutoff_max == 0)
+			{
+				if (desc.op_type == CTransformOutputDesc::OpType::HISTOGRAM)//for histogram default value differs				
+				{
+					desc.cutoff_max = MIN(config.headers.front().max_count, MIN(HISTOGRAM_MAX_COUNTER_DEFAULT, (uint32)((1ull << (8 * config.headers.front().counter_size)) - 1)));					
+				}
+				else
+					desc.cutoff_max = max_cutoff_max;
+			}
+			if (desc.counter_max == 0)
+				desc.counter_max = max_counter_max;
+		}
+	}
+	else if (config.mode == CConfig::Mode::COMPLEX)
+	{
+		if (config.output_desc.cutoff_min == 0)
+		{
+			uint32 min_cutoff_min = get_min_cutoff_min();
+			config.output_desc.cutoff_min = min_cutoff_min;
+			if (config.verbose)
+				cerr << "Warning: -ci was not specified for output. It will be set to " << min_cutoff_min << "\n";
+		}
+		if (config.output_desc.cutoff_max == 0)
+		{
+			uint32 max_cutoff_max = get_max_cutoff_max();
+			config.output_desc.cutoff_max = max_cutoff_max;
+			if (config.verbose)
+				cerr << "Warning: -cx was not specified for output. It will be set to " << config.output_desc.cutoff_max << "\n";
+		}
+		if (config.output_desc.counter_max == 0)
+		{
+			uint32 max_counter_max = get_max_counter_max();
+			config.output_desc.counter_max = max_counter_max;
+			if (config.verbose)
+				cerr << "Warning: -cs was not specified for output. It will be set to " << max_counter_max << "\n";
+		}
+	}
+	return true;
+}
+	
+void CParametersParser::SetThreads()
+{
+	uint32 threads_left = config.avaiable_threads;
+	//threads distribution: as many as possible for kmc2 database input, 1 thread for main thread which make operations calculation
+	vector<reference_wrapper<CInputDesc>> kmc2_desc;
+
+	if (config.IsSeparateThreadForMainProcessingNeeded())
+		threads_left = MAX(1, threads_left - 1);
+	
+	for (uint32 i = 0; i < config.headers.size(); ++i)
+	{
+		if (config.headers[i].IsKMC2())
+		{
+			kmc2_desc.push_back(ref(config.input_desc[i]));
+		}
+		else
+		{
+			if (threads_left > 2)
+				threads_left -= 2; //2 threads for kmc1 input
+			else
+				threads_left = 1;
+		}
+	}
+	if (kmc2_desc.size())
+	{
+		uint32 per_signle_kmc2_input = MAX(1, (uint32)(threads_left / kmc2_desc.size()));
+		uint32 per_last_kmc2_input = MAX(1, (uint32)((threads_left + kmc2_desc.size() - 1) / kmc2_desc.size()));
+
+		for (uint32 i = 0; i < kmc2_desc.size() - 1; ++i)
+			kmc2_desc[i].get().threads = per_signle_kmc2_input;
+
+		kmc2_desc.back().get().threads = per_last_kmc2_input;
+	}
+}
+
+
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/parameters_parser.h'
--- old/kmc_tools/parameters_parser.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/parameters_parser.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,61 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _PARAMETERS_PARSER_H
+#define _PARAMETERS_PARSER_H
+
+#include "defs.h"
+#include "parser.h"
+#include <memory>
+class CParametersParser
+{	
+	std::unique_ptr<CParser> complex_parser;
+	int argc;
+	char** argv;
+	int pos;
+	CConfig& config;
+
+	uint32 replace_zero(uint32 val, const char* param_name, uint32 value_to_set_if_zero);
+	void parse_int_or_float(bool& force_float, bool& force_int, float& float_value, uint32& int_val, const char* param_name);
+	void parse_global_params();
+	void read_input_fastq_desc();
+	void read_output_fastq_desc();
+	void read_input_desc();
+	void read_check_params();
+	void read_filter_params();
+	bool read_output_desc_for_simple();
+	bool read_output_for_transform();
+
+	uint32 get_max_counter_max();
+	uint32 get_max_cutoff_max();
+	uint32 get_min_cutoff_min();
+public:
+	
+	CParametersParser(int argc, char** argv);
+	void Usage();
+	
+	template<unsigned SIZE>
+	CExpressionNode<SIZE>* GetExpressionRoot();
+
+	void Parse();
+	bool validate_input_dbs();
+	void SetThreads();
+	
+};
+
+template<unsigned SIZE>
+CExpressionNode<SIZE>* CParametersParser::GetExpressionRoot()
+{
+	return complex_parser->GetExpressionRoot<SIZE>();
+}
+#endif
+
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/parser.cpp'
--- old/kmc_tools/parser.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/parser.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,244 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "parser.h"
+#include "tokenizer.h"
+#include "output_parser.h"
+#include "config.h"
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+
+CParser::CParser(const std::string& src):
+	config(CConfig::GetInstance())
+{
+	line_no = 0;
+	file.open(src);
+	if (!file.is_open())
+	{
+		std::cerr << "Error: cannot open file: " << src << "\n";
+		exit(1);
+	}
+	//input_line_pattern = "\\s*(\\w*)\\s*=\\s*(.*)$";
+	input_line_pattern = "^\\s*([\\w-+]*)\\s*=\\s*(.*)$";
+	output_line_pattern = "^\\s*(.*)\\s*=\\s*(.*)$"; //TODO: consider valid file name	
+	empty_line_pattern = "^\\s*$";
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+void CParser::ParseInputs()
+{
+	std::string line;
+	while (true)
+	{
+		if (!nextLine(line))
+		{
+			std::cerr << "Error: 'INPUT:' missing\n";
+			exit(1);
+		}
+		if (line.find("INPUT:") != std::string::npos)
+			break;
+	}
+
+	if (!nextLine(line) || line.find("OUTPUT:") != std::string::npos)
+	{
+		std::cerr << "Error: None input was defined\n";
+		exit(1);
+	}
+
+	while (true)
+	{
+		parseInputLine(line);
+		if (!nextLine(line))
+		{
+			std::cerr << "Error: 'OUTPUT:' missing\n";
+			exit(1);
+		}
+		if (line.find("OUTPUT:") != std::string::npos)
+			break;
+	}
+}
+//************************************************************************************************************
+ void CParser::ParseOutput()
+ {
+	std::string line;
+	if (!nextLine(line) || line.find("OUTPUT_PARAMS:") != std::string::npos)
+	{
+ 		std::cerr << "Error: None output was defined\n";
+ 		exit(1);
+	}
+ 
+	parseOutputLine(line);
+ 
+	while (nextLine(line))
+	{
+ 		if (line.find("OUTPUT_PARAMS:") != std::string::npos)
+ 		{
+ 			parseOtuputParamsLine();
+ 			break;
+ 		}
+	}
+ }
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+void CParser::parseInputLine(const std::string& line)
+{
+	std::smatch match;
+	if (std::regex_search(line, match, input_line_pattern))
+	{
+#ifdef ENABLE_DEBUG
+		std::cout << "\ninput name: " << match[1];
+		std::cout << "\nafter = " << match[2];
+#endif
+		if (input.find(match[1]) != input.end())
+		{
+			std::cerr << "Error: Name redefinition(" << match[1] << ")" << " line: " << line_no << "\n";
+			exit(1);
+		}
+		if (CTokenizer::GetKeywords().find(match[1]) != CTokenizer::GetKeywords().end())
+		{
+			std::cerr << "Error: `" << match[1] << "` is not valid name, line: " << line_no << "\n";
+			exit(1);
+		}
+		else
+		{
+			std::string file_name;
+			std::istringstream stream(match[2]);
+			
+			CInputDesc desc;
+
+			if (!(stream >> desc.file_src))
+			{
+				std::cerr << "Error: file name for " << match[1] << " was not specified, line: "<< line_no <<"\n";
+				exit(1);
+			}
+			std::string tmp;
+			while (stream >> tmp)
+			{
+				if (strncmp(tmp.c_str(), "-ci", 3) == 0)
+				{
+					desc.cutoff_min = atoi(tmp.c_str() + 3);
+					continue;
+				}
+				else if (strncmp(tmp.c_str(), "-cx", 3) == 0)
+				{
+					desc.cutoff_max = atoi(tmp.c_str() + 3);
+					continue;
+				}
+				std::cerr << "Error: Unknow parameter " << tmp << " for variable " << match[1] << ", line: "<< line_no <<"\n";
+				exit(1);
+			}
+
+			config.input_desc.push_back(desc);
+			input[match[1]] = (uint32)(config.input_desc.size() - 1);				
+		}
+	}
+	else
+	{
+		std::cerr << "Error: wrong line format, line: " << line_no << "\n";
+		exit(1);
+	}
+}
+
+
+//************************************************************************************************************
+void CParser::parseOutputLine(const std::string& line)
+{
+	std::smatch match;
+	if (std::regex_search(line, match, output_line_pattern))
+	{
+#ifdef ENABLE_DEBUG
+		std::cout << "out file name " << match[1] << "\n";
+		std::cout << "rest of output " << match[2] << "\n";
+
+		std::cout << "Tokenize resf of output\n";
+#endif
+		config.output_desc.file_src = match[1];
+
+		//trim whitespaces at the end
+		static const std::string whitespace = " \t\r\n\v\f";
+		auto end = config.output_desc.file_src.find_last_not_of(whitespace);
+		config.output_desc.file_src.erase(end + 1);
+		if (config.output_desc.file_src == "")
+		{
+			std::cerr << "Error: wrong line format, line: " << line_no << " (output file name is not specified)\n";
+			exit(1);
+		}
+
+		tokenizer.Tokenize(match[2], tokens);
+	}
+	else
+	{
+		std::cerr << "Error: wrong line format, line: " << line_no << "\n";
+		exit(1);
+	}
+}
+
+/*****************************************************************************************************************************/
+void CParser::parseOtuputParamsLine()
+{
+	std::string line;
+
+	if (!nextLine(line))
+	{
+		std::cerr << "Warning: OUTPUT_PARAMS exists, but no parameters are defined\n";
+	}
+	else
+	{
+		std::istringstream stream(line);
+		std::string tmp;
+		while (stream >> tmp)
+		{
+			if (strncmp(tmp.c_str(), "-ci", 3) == 0)
+			{				
+				config.output_desc.cutoff_min = atoi(tmp.c_str() + 3);
+				continue;
+			}
+			else if (strncmp(tmp.c_str(), "-cx", 3) == 0)
+			{			
+				config.output_desc.cutoff_max = atoi(tmp.c_str() + 3);
+				continue;
+			}
+			else if ((strncmp(tmp.c_str(), "-cs", 3) == 0))
+			{
+				config.output_desc.counter_max = atoi(tmp.c_str() + 3);
+				continue;
+			}
+			std::cerr << "Error: Unknow parameter " << tmp << " for variable " << tmp << ", line: " << line_no << "\n";
+			exit(1);
+		}
+	}
+}
+
+/*****************************************************************************************************************************/
+bool CParser::nextLine(std::string& line)
+{
+	while (true)
+	{
+		if (file.eof())
+			return false;
+		std::getline(file, line);
+		++line_no;
+		std::smatch match;
+		if (!std::regex_search(line, match, empty_line_pattern))
+			return true;
+	}
+}
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/parser.h'
--- old/kmc_tools/parser.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/parser.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,65 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _PARSER_H
+#define _PARSER_H
+#include "defs.h"
+#include "expression_node.h"
+#include "tokenizer.h"
+#include "output_parser.h"
+
+#include <iostream>
+#include <fstream>
+#include <regex>
+#include <map>
+#include <list>
+
+
+//************************************************************************************************************
+// CParser - parser for complex operations
+//************************************************************************************************************
+class CParser
+{	
+	std::ifstream file;
+	uint32 line_no;
+	std::regex input_line_pattern;
+	std::regex output_line_pattern;
+	std::regex empty_line_pattern;	
+	std::map<std::string, uint32> input;	
+	void parseInputLine(const std::string& line);
+
+	void parseOutputLine(const std::string& line);
+	void parseOtuputParamsLine();
+	bool nextLine(std::string& line);
+	CConfig& config;
+	
+	CTokenizer tokenizer;
+	std::list<Token> tokens;
+
+public:
+	CParser(const std::string& src);
+	void ParseInputs();
+	void ParseOutput();
+
+	template<unsigned SIZE>
+	CExpressionNode<SIZE>* GetExpressionRoot();
+
+};
+
+//************************************************************************************************************
+template<unsigned SIZE> CExpressionNode<SIZE>* CParser::GetExpressionRoot()
+{
+	COutputParser<SIZE> out_parser(tokens, input);
+	return out_parser.Parse();
+}
+
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/percent_progress.cpp'
--- old/kmc_tools/percent_progress.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/percent_progress.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,104 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "percent_progress.h"
+#include <iostream>
+#include <string>
+using namespace std;
+
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+uint32 CPercentProgress::RegisterItem(const std::string& name, uint64 max_value)
+{
+	items.emplace_back(name, max_value);
+	return static_cast<uint32>(items.size() - 1);
+}
+
+/*****************************************************************************************************************************/
+uint32 CPercentProgress::RegisterItem(uint64 max_value)
+{
+	items.emplace_back("in" + std::to_string(items.size() + 1), max_value);
+	display();
+	return static_cast<uint32>(items.size() - 1);
+}
+
+/*****************************************************************************************************************************/
+void CPercentProgress::UpdateItem(uint32 id)
+{
+	--items[id].to_next_update;
+	if (!items[id].to_next_update)
+	{
+		items[id].to_next_update = items[id].to_next_update_pattern;
+		UpdateItem(id, items[id].to_next_update_pattern);
+	}
+}
+
+/*****************************************************************************************************************************/
+void CPercentProgress::UpdateItem(uint32 id, uint32 offset)
+{
+	items[id].cur_val += offset;
+	uint32 prev = items[id].cur_percent;
+	if (items[id].max_val)
+		items[id].cur_percent = static_cast<uint32>((items[id].cur_val * 100) / items[id].max_val);
+	else
+		items[id].cur_percent = 100;
+	if (prev != items[id].cur_percent)
+		display();
+}
+
+/*****************************************************************************************************************************/
+void CPercentProgress::Complete(uint32 id)
+{
+	if (items[id].cur_percent != 100)
+	{
+		items[id].cur_percent = 100;
+		display();		
+	}
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+/*****************************************************************************************************************************/
+void CPercentProgress::display()
+{
+	if (hide_progress || ignore_rest)
+		return;
+	std::cerr << "\r";
+	bool finished = true;
+	for (auto& item : items)
+	{
+		std::cerr << item.name << ": " << item.cur_percent << "% ";
+		if (item.cur_percent != 100)
+			finished = false;
+	}
+
+	if (finished)
+	{
+		cerr << "\n";
+		ignore_rest = true;
+	}
+	std::cerr.flush();
+}
+
+/*****************************************************************************************************************************/
+CPercentProgress::CDisplayItem::CDisplayItem(const std::string name, uint64 max_val) : name(name), max_val(max_val)
+{
+	to_next_update_pattern = (uint32)MAX(1, max_val / 100);
+	to_next_update = to_next_update_pattern;
+}
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/percent_progress.h'
--- old/kmc_tools/percent_progress.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/percent_progress.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,49 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _PERCENT_PROGRESS_H
+#define _PERCENT_PROGRESS_H
+
+#include "defs.h"
+#include <vector>
+#include <string>
+//************************************************************************************************************
+// CPercentProgress - class to display progress of reading inputs
+//************************************************************************************************************
+class CPercentProgress
+{
+	bool ignore_rest = false;
+	bool hide_progress = false;
+	struct CDisplayItem
+	{
+		std::string name;
+		uint64 cur_val = 0;
+		uint64 max_val;
+		uint32 cur_percent = 0;
+
+		uint32 to_next_update;
+		uint32 to_next_update_pattern;
+	public:
+		CDisplayItem(const std::string name, uint64 max_val);
+	};
+	std::vector<CDisplayItem> items;
+	void display();
+public:
+	uint32 RegisterItem(const std::string& name, uint64 max_value);
+	uint32 RegisterItem(uint64 max_value);
+	void UpdateItem(uint32 id);
+	void Complete(uint32 id);
+	void UpdateItem(uint32 id, uint32 offset);
+	void Hide(){ hide_progress = true; }
+};
+
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/queues.h'
--- old/kmc_tools/queues.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/queues.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,409 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _QUEUES_H_
+#define _QUEUES_H_
+
+#include "defs.h"
+#include "bundle.h"
+#include <mutex>
+#include <vector>
+#include <condition_variable>
+#include <list>
+#include <queue>
+
+
+
+class CSufWriteQueue
+{
+	uint32 buf_size;
+	uint32 max_inside;
+
+	using elem_t = std::pair<uchar*, uint32>;
+	std::list<elem_t> content;
+
+	mutable std::mutex mtx;
+	uint32 n_writers;
+	std::condition_variable cv_pop, cv_push;
+public:
+	void init(uint32 _buf_size, uint32 _max_inside)
+	{
+		buf_size = _buf_size;
+		max_inside = _max_inside;
+		n_writers = 1;
+	}
+
+	void push(uchar* &buf, uint32 size)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv_push.wait(lck, [this]{return content.size() < max_inside; });
+
+		bool was_empty = content.empty();
+
+		content.push_back(std::make_pair(buf, size));
+
+		buf = new uchar[buf_size];
+
+		if (was_empty)
+			cv_pop.notify_all();
+	}
+
+	bool pop(uchar* &buf, uint32& size)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+			cv_pop.wait(lck, [this]{return !content.empty() || !n_writers; });
+		if (!n_writers && content.empty())
+			return false;
+
+		bool was_full = max_inside == content.size();
+
+		buf = content.front().first;
+		size = content.front().second;
+		content.pop_front();
+
+		if (was_full)
+			cv_push.notify_all();
+
+		return true;
+	}
+
+
+	void mark_completed()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		--n_writers;
+		if (!n_writers)
+			cv_pop.notify_all();
+	}
+};
+
+
+template<unsigned SIZE> class CCircularQueue
+{
+	std::vector<CBundleData<SIZE>> buff;
+	bool full, is_completed;
+	int start, end;
+	mutable std::mutex mtx;
+
+	std::condition_variable cv_push;
+	std::condition_variable cv_pop;
+	bool forced_to_finish = false;
+
+public:
+	CCircularQueue(int size, uint32 bundle_size) : full(false), is_completed(false), start(0), end(0)
+	{
+		buff.reserve(size);
+		for (int i = 0; i < size; ++i)
+			buff.emplace_back(bundle_size);
+	}
+	CCircularQueue(int size) : buff(size), full(false), is_completed(false), start(0), end(0)
+	{
+
+	}
+
+	bool push(CBundleData<SIZE>& bundle_data)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+
+		cv_push.wait(lck, [this]{return !full || forced_to_finish; });
+		
+		if (forced_to_finish)
+		{
+			return false;
+		}
+
+		bool was_empty = start == end;
+
+		std::swap(buff[end], bundle_data);
+		bundle_data.Clear();
+		end = (end + 1) % buff.size();
+
+		if (end == start)
+			full = true;
+
+		if (was_empty)
+			cv_pop.notify_all();
+
+		return true;
+	}
+
+	bool pop(CBundleData<SIZE>& bundle_data)
+	{
+		std::unique_lock<std::mutex> lck(mtx);		
+		cv_pop.wait(lck, [this]{ return start != end || full || is_completed || forced_to_finish; });
+
+		if (forced_to_finish)
+			return false;
+
+		if (is_completed && !full && start == end)
+			return false;
+
+		bool was_full = full;		
+		std::swap(buff[start], bundle_data);
+		buff[start].Clear();
+		start = (start + 1) % buff.size();
+		full = false;
+		if (was_full)
+			cv_push.notify_all();
+		return true;
+	}
+
+	void mark_completed()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		is_completed = true;
+		cv_pop.notify_all();
+	}
+
+	void force_finish()
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+		forced_to_finish = true;
+		cv_pop.notify_all();
+		cv_push.notify_all();
+	}
+
+};
+
+class CInputFilesQueue {
+	typedef std::string elem_t;
+	typedef std::queue<elem_t, std::list<elem_t>> queue_t;
+
+	queue_t q;
+
+	mutable std::mutex mtx;								// The mutex to synchronise on
+
+public:
+	CInputFilesQueue(const std::vector<std::string> &file_names) {
+		std::unique_lock<std::mutex> lck(mtx);
+
+		for (auto p = file_names.cbegin(); p != file_names.cend(); ++p)
+			q.push(*p);
+
+	};
+
+	bool pop(std::string &file_name) {
+		std::lock_guard<std::mutex> lck(mtx);
+
+		if (q.empty())
+			return false;
+
+		file_name = q.front();
+		q.pop();
+
+		return true;
+	}
+};
+
+class CMemoryPool {
+	int64 total_size;
+	int64 part_size;
+	int64 n_parts_total;
+	int64 n_parts_free;
+
+	uchar *buffer, *raw_buffer;
+	uint32 *stack;
+
+	mutable std::mutex mtx;							// The mutex to synchronise on
+	std::condition_variable cv;						// The condition to wait for
+
+public:
+	CMemoryPool(int64 _total_size, int64 _part_size) {
+		raw_buffer = NULL;
+		buffer = NULL;
+		stack = NULL;
+		prepare(_total_size, _part_size);
+	}
+	~CMemoryPool() {
+		release();
+	}
+
+	void prepare(int64 _total_size, int64 _part_size) {
+		release();
+
+		n_parts_total = _total_size / _part_size;
+		part_size = (_part_size + 15) / 16 * 16;			// to allow mapping pointer to int*
+		n_parts_free = n_parts_total;
+
+		total_size = n_parts_total * part_size;
+
+		raw_buffer = new uchar[total_size + 64];
+		buffer = raw_buffer;
+		while (((uint64)buffer) % 64)
+			buffer++;
+
+		stack = new uint32[n_parts_total];
+		for (uint32 i = 0; i < n_parts_total; ++i)
+			stack[i] = i;
+	}
+
+	void release(void) {
+		if (raw_buffer)
+			delete[] raw_buffer;
+		raw_buffer = NULL;
+		buffer = NULL;
+
+		if (stack)
+			delete[] stack;
+		stack = NULL;
+	}
+
+	// Allocate memory buffer - uchar*
+	void reserve(uchar* &part)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv.wait(lck, [this]{return n_parts_free > 0; });
+
+		part = buffer + stack[--n_parts_free] * part_size;
+	}
+	// Allocate memory buffer - char*
+	void reserve(char* &part)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv.wait(lck, [this]{return n_parts_free > 0; });
+
+		part = (char*)(buffer + stack[--n_parts_free] * part_size);
+	}
+	// Allocate memory buffer - uint32*
+	void reserve(uint32* &part)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv.wait(lck, [this]{return n_parts_free > 0; });
+
+		part = (uint32*)(buffer + stack[--n_parts_free] * part_size);
+	}
+	// Allocate memory buffer - uint64*
+	void reserve(uint64* &part)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv.wait(lck, [this]{return n_parts_free > 0; });
+
+		part = (uint64*)(buffer + stack[--n_parts_free] * part_size);
+	}
+	// Allocate memory buffer - double*
+	void reserve(double* &part)
+	{
+		std::unique_lock<std::mutex> lck(mtx);
+		cv.wait(lck, [this]{return n_parts_free > 0; });
+
+		part = (double*)(buffer + stack[--n_parts_free] * part_size);
+	}
+
+	// Deallocate memory buffer - uchar*
+	void free(uchar* part)
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+
+		stack[n_parts_free++] = (uint32)((part - buffer) / part_size);
+
+		cv.notify_all();
+	}
+	// Deallocate memory buffer - char*
+	void free(char* part)
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+
+		stack[n_parts_free++] = (uint32)(((uchar*)part - buffer) / part_size);
+		cv.notify_all();
+	}
+	// Deallocate memory buffer - uint32*
+	void free(uint32* part)
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+
+		stack[n_parts_free++] = (uint32)((((uchar *)part) - buffer) / part_size);
+		cv.notify_all();
+	}
+	// Deallocate memory buffer - uint64*
+	void free(uint64* part)
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+
+		stack[n_parts_free++] = (uint32)((((uchar *)part) - buffer) / part_size);
+		cv.notify_all();
+	}
+	// Deallocate memory buffer - double*
+	void free(double* part)
+	{
+		std::lock_guard<std::mutex> lck(mtx);
+
+		stack[n_parts_free++] = (uint32)((((uchar *)part) - buffer) / part_size);
+		cv.notify_all();
+	}
+};
+
+class CPartQueue {
+	typedef std::pair<uchar *, uint64> elem_t;
+	typedef std::queue<elem_t, std::list<elem_t>> queue_t;
+
+	queue_t q;
+	bool is_completed;
+	int n_readers;
+
+	mutable std::mutex mtx;								// The mutex to synchronise on
+	std::condition_variable cv_queue_empty;
+
+public:
+	CPartQueue(int _n_readers) {
+		std::unique_lock<std::mutex> lck(mtx);
+		is_completed = false;
+		n_readers = _n_readers;
+	};
+	~CPartQueue() {};
+
+	bool empty() {
+		std::lock_guard<std::mutex> lck(mtx);
+		return q.empty();
+	}
+	bool completed() {
+		std::lock_guard<std::mutex> lck(mtx);
+		return q.empty() && !n_readers;
+	}
+	void mark_completed() {
+		std::lock_guard<std::mutex> lck(mtx);
+		n_readers--;
+		if (!n_readers)
+			cv_queue_empty.notify_all();
+	}
+	void push(uchar *part, uint64 size) {
+		std::unique_lock<std::mutex> lck(mtx);
+
+		bool was_empty = q.empty();
+		q.push(std::make_pair(part, size));
+
+		if (was_empty)
+			cv_queue_empty.notify_all();
+	}
+	bool pop(uchar *&part, uint64 &size) {
+		std::unique_lock<std::mutex> lck(mtx);
+		cv_queue_empty.wait(lck, [this]{return !this->q.empty() || !this->n_readers; });
+
+		if (q.empty())
+			return false;
+
+		std::tie(part, size) = q.front();
+		q.pop();
+
+		return true;
+	}
+};
+
+
+struct CFilteringQueues
+{
+	CInputFilesQueue *input_files_queue;
+	CPartQueue *input_part_queue, *filtered_part_queue;
+	CMemoryPool *pmm_fastq_reader;
+	CMemoryPool *pmm_fastq_filter;
+};
+
+
+#endif
+

=== added file 'kmc_tools/stdafx.cpp'
--- old/kmc_tools/stdafx.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/stdafx.cpp	2015-12-19 14:54:48 +0000
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// kmc_tools.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file

=== added file 'kmc_tools/stdafx.h'
--- old/kmc_tools/stdafx.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/stdafx.h	2015-12-19 14:54:48 +0000
@@ -0,0 +1,17 @@
+#ifdef WIN32
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#pragma once
+
+#include "targetver.h"
+
+#include <stdio.h>
+#include <tchar.h>
+
+
+
+// TODO: reference additional headers your program requires here
+#endif
\ No newline at end of file

=== added file 'kmc_tools/targetver.h'
--- old/kmc_tools/targetver.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/targetver.h	2015-12-19 14:54:48 +0000
@@ -0,0 +1,8 @@
+#pragma once
+
+// Including SDKDDKVer.h defines the highest available Windows platform.
+
+// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
+// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
+
+#include <SDKDDKVer.h>

=== added file 'kmc_tools/thread_watch.cpp'
--- old/kmc_tools/thread_watch.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/thread_watch.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,165 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.0.0
+  Date   : 2017-01-28
+*/
+
+#include "stdafx.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include <cstdio> // NULL
+#include "thread_watch.h"
+
+
+#ifdef WIN32
+
+typedef struct
+{
+	ULARGE_INTEGER start;
+	ULARGE_INTEGER stop;
+} thread_watch_t;
+
+class CThreadWatchImpl
+{
+	thread_watch_t timer_kernel, timer_user;
+	LARGE_INTEGER frequency;
+	double LIToSecs(LARGE_INTEGER & L);
+public:
+	CThreadWatchImpl();
+	void StartTimer();
+	void StopTimer();
+	double GetElapsedTime();
+};
+
+double CThreadWatchImpl::LIToSecs(LARGE_INTEGER & L)
+{
+	return ((double)L.QuadPart / (double)frequency.QuadPart);
+}
+
+// **********************************************************
+CThreadWatchImpl::CThreadWatchImpl()
+{
+	timer_kernel.start.QuadPart = 0;
+	timer_kernel.stop.QuadPart = 0;
+	timer_user.start.QuadPart = 0;
+	timer_user.stop.QuadPart = 0;
+	//	QueryPerformanceFrequency( &frequency );
+	//	frequency = 1;		// 100ns
+}
+
+// **********************************************************
+void CThreadWatchImpl::StartTimer()
+{
+	FILETIME CreationTime, ExitTime, KernelTime, UserTime;
+	GetThreadTimes(GetCurrentThread(), &CreationTime, &ExitTime, &KernelTime, &UserTime);
+
+	timer_kernel.start.LowPart = KernelTime.dwLowDateTime;
+	timer_kernel.start.HighPart = KernelTime.dwHighDateTime;
+	timer_user.start.LowPart = UserTime.dwLowDateTime;
+	timer_user.start.HighPart = UserTime.dwHighDateTime;
+}
+
+// **********************************************************
+void CThreadWatchImpl::StopTimer()
+{
+	FILETIME CreationTime, ExitTime, KernelTime, UserTime;
+	GetThreadTimes(GetCurrentThread(), &CreationTime, &ExitTime, &KernelTime, &UserTime);
+	//    QueryPerformanceCounter(&timer.stop);
+	timer_kernel.stop.LowPart = KernelTime.dwLowDateTime;
+	timer_kernel.stop.HighPart = KernelTime.dwHighDateTime;
+	timer_user.stop.LowPart = UserTime.dwLowDateTime;
+	timer_user.stop.HighPart = UserTime.dwHighDateTime;
+}
+
+// **********************************************************
+double CThreadWatchImpl::GetElapsedTime()
+{
+	/*	LARGE_INTEGER time;
+	time.QuadPart = timer.stop.QuadPart - timer.start.QuadPart;
+	return LIToSecs( time) ;*/
+	LARGE_INTEGER time;
+
+	time.QuadPart = (timer_kernel.stop.QuadPart - timer_kernel.start.QuadPart);
+	time.QuadPart += (timer_user.stop.QuadPart - timer_user.start.QuadPart);
+
+	return (double)time.QuadPart / 1e7;			// 100ns clock
+}
+
+
+
+CThreadWatch::CThreadWatch()
+{
+	pimpl = new CThreadWatchImpl;
+}
+void CThreadWatch::StartTimer()
+{
+	pimpl->StartTimer();
+}
+void CThreadWatch::StopTimer()
+{
+	pimpl->StopTimer();
+}
+double CThreadWatch::GetElapsedTime()
+{
+	return pimpl->GetElapsedTime();
+}
+CThreadWatch::~CThreadWatch()
+{
+	delete pimpl;
+}
+#else
+
+// **********************************************************
+
+// **********************************************************
+// CThreadWatch
+// **********************************************************
+/*double CThreadWatch::LIToSecs( LARGE_INTEGER & L)
+{
+return 1.0;
+}*/
+
+// **********************************************************
+CThreadWatch::CThreadWatch()
+{
+}
+
+// **********************************************************
+void CThreadWatch::StartTimer()
+{
+	rusage usage;
+	getrusage(RUSAGE_THREAD, &usage);
+	start_user = usage.ru_utime;
+	start_kernel = usage.ru_stime;
+}
+
+// **********************************************************
+void CThreadWatch::StopTimer()
+{
+	rusage usage;
+	getrusage(RUSAGE_THREAD, &usage);
+	stop_user = usage.ru_utime;
+	stop_kernel = usage.ru_stime;
+}
+
+// **********************************************************
+double CThreadWatch::GetElapsedTime()
+{
+	double ret = 0.0;
+
+	ret += stop_user.tv_sec + stop_user.tv_usec / 1000000.0;
+	ret += stop_kernel.tv_sec + stop_kernel.tv_usec / 1000000.0;
+	ret -= start_user.tv_sec + start_user.tv_usec / 1000000.0;
+	ret -= start_kernel.tv_sec + start_kernel.tv_usec / 1000000.0;
+
+	return ret;
+}
+
+#endif
\ No newline at end of file

=== added file 'kmc_tools/thread_watch.h'
--- old/kmc_tools/thread_watch.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/thread_watch.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,52 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _THREAD_WATCH_H
+#define _THREAD_WATCH_H
+
+#ifdef WIN32
+
+class CThreadWatchImpl;
+
+// **********************************************************
+class CThreadWatch
+{	
+	CThreadWatchImpl* pimpl;
+public:
+	CThreadWatch();
+	void StartTimer();
+	void StopTimer();
+	double GetElapsedTime();
+	~CThreadWatch();
+};
+
+#else
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+typedef timeval thread_watch_t;
+
+class CThreadWatch
+{
+	thread_watch_t start_kernel, start_user;
+	thread_watch_t stop_kernel, stop_user;
+
+public:
+	CThreadWatch();
+	void StartTimer();
+	void StopTimer();
+	double GetElapsedTime();
+};
+
+#endif
+
+#endif
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/timer.h'
--- old/kmc_tools/timer.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/timer.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,33 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _TIMER_H
+#define _TIMER_H
+
+#include <chrono>
+class CTimer
+{
+	using time_p = decltype(std::chrono::high_resolution_clock::now());
+	time_p _start, _end;
+public:
+	void start()
+	{
+		_start = std::chrono::high_resolution_clock::now();
+	}
+	double get_time()
+	{
+		auto time = std::chrono::high_resolution_clock::now() - _start;
+		return static_cast<double>(std::chrono::duration_cast<std::chrono::microseconds>(time).count() / 1000000.0);
+	}
+};
+
+
+
+#endif 

=== added file 'kmc_tools/tokenizer.cpp'
--- old/kmc_tools/tokenizer.cpp	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/tokenizer.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,91 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2017-01-28
+*/
+
+#include "stdafx.h"
+#include "tokenizer.h"
+
+/*****************************************************************************************************************************/
+/******************************************************** CONSTRUCTOR ********************************************************/
+/*****************************************************************************************************************************/
+
+CTokenizer::CTokenizer()
+{
+	token_patterns.resize(13);
+	token_patterns[0] = std::make_pair("^(\\()", TokenType::PARENTHESIS_OPEN);
+	token_patterns[1] = std::make_pair("^(\\))", TokenType::PARENTHESIS_CLOSE);
+	token_patterns[2] = std::make_pair("^(\\-)", TokenType::STRICT_MINUS_OPER);
+	token_patterns[3] = std::make_pair("^(\\~)", TokenType::COUNTER_MINUS_OPER);
+	token_patterns[4] = std::make_pair("^(\\+)", TokenType::PLUS_OPER);
+	token_patterns[5] = std::make_pair("^(\\*)", TokenType::MUL_OPER);
+	
+	//those are keywords
+	token_patterns[6] = std::make_pair("^(min)", TokenType::MIN_MODIFIER);
+	token_patterns[7] = std::make_pair("^(max)", TokenType::MAX_MODIFIER);
+	token_patterns[8] = std::make_pair("^(diff)", TokenType::DIFF_MODIFIER);
+	token_patterns[9] = std::make_pair("^(sum)", TokenType::SUM_MODIFIER);
+	token_patterns[10] = std::make_pair("^(left)", TokenType::LEFT_MODIFIER);
+	token_patterns[11] = std::make_pair("^(right)", TokenType::RIGHT_MODIFIER);
+
+	token_patterns[12] = std::make_pair("^(\\w*)", TokenType::VARIABLE);
+}
+
+
+/*****************************************************************************************************************************/
+/********************************************************** PUBLIC ***********************************************************/
+/*****************************************************************************************************************************/
+
+
+const std::set<std::string>& CTokenizer::GetKeywords()
+{
+	static std::set<std::string> keywords = {"min", "max", "sum", "diff", "left", "right"}; //related to tokens created in CTokenizer ctor
+	return keywords;
+}
+
+void CTokenizer::Tokenize(const std::string& _expression, std::list<Token>& tokens)
+{
+	std::string expression = _expression;
+	std::smatch match;
+	leftTrimString(expression, 0);
+	while (!expression.empty())
+	{
+		bool valid_token = false;
+		for (const auto& pattern : token_patterns)
+		{
+			if (std::regex_search(expression, match, pattern.first))
+			{
+#ifdef ENABLE_DEBUG
+				std::cout << match[1];
+#endif
+				tokens.push_back(std::make_pair(match[1], pattern.second));
+				leftTrimString(expression, (int)match[1].length());
+				valid_token = true;
+				break;
+			}
+		}
+		if (!valid_token)
+		{
+			std::cerr << "Error: wrong output format near : " << expression << "\n";
+			exit(1);
+		}
+	}
+}
+
+/*****************************************************************************************************************************/
+/********************************************************** PRIVATE **********************************************************/
+/*****************************************************************************************************************************/
+
+void CTokenizer::leftTrimString(std::string& str, int start_pos)
+{
+	static const std::string whitespace = " \t\r\n\v\f";
+	auto next_pos = str.find_first_not_of(whitespace, start_pos);
+	str.erase(0, next_pos);
+}
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmc_tools/tokenizer.h'
--- old/kmc_tools/tokenizer.h	1970-01-01 00:00:00 +0000
+++ new/kmc_tools/tokenizer.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,41 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _TOKENIZER_H
+#define _TOKENIZER_H
+
+#include "defs.h"
+#include <vector>
+#include <regex>
+#include <list>
+#include <set>
+#include <iostream>
+
+enum class TokenType{ VARIABLE, PLUS_OPER, STRICT_MINUS_OPER, COUNTER_MINUS_OPER, MUL_OPER, PARENTHESIS_OPEN, PARENTHESIS_CLOSE, TERMINATOR, DIFF_MODIFIER, SUM_MODIFIER, MIN_MODIFIER, MAX_MODIFIER, LEFT_MODIFIER, RIGHT_MODIFIER };
+using Token = std::pair<std::string, TokenType>;
+
+//************************************************************************************************************
+// CTokenizer - Tokenizer for k-mers set operations
+//************************************************************************************************************
+class CTokenizer
+{
+public:
+	static const std::set<std::string>& GetKeywords();
+	CTokenizer();
+	void Tokenize(const std::string& _expression, std::list<Token>& tokens);
+
+private:
+	std::vector<std::pair<std::regex, TokenType>> token_patterns;
+	void leftTrimString(std::string& str, int start_pos);	
+};
+
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added directory 'kmer_counter'
=== added file 'kmer_counter.sln'
--- old/kmer_counter.sln	1970-01-01 00:00:00 +0000
+++ new/kmer_counter.sln	2020-12-10 18:04:42 +0000
@@ -0,0 +1,89 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmer_counter", "kmer_counter\kmer_counter.vcxproj", "{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmc_dump", "kmc_dump\kmc_dump.vcxproj", "{8939AD12-23D5-469C-806B-DC3F98F8A514}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmc_dump_sample", "kmc_dump_sample\kmc_dump_sample.vcxproj", "{17823F37-86DE-4E58-B354-B84DA9EDA6A1}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmc_tools", "kmc_tools\kmc_tools.vcxproj", "{F3B0CC94-9DD0-4642-891C-EA08BDA50260}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "py_kmc_api", "py_kmc_api\py_kmc_api.vcxproj", "{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Mixed Platforms = Debug|Mixed Platforms
+		Debug|Win32 = Debug|Win32
+		Debug|x64 = Debug|x64
+		Release|Mixed Platforms = Release|Mixed Platforms
+		Release|Win32 = Release|Win32
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Debug|Win32.ActiveCfg = Debug|Win32
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Debug|Win32.Build.0 = Debug|Win32
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Debug|x64.ActiveCfg = Debug|x64
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Debug|x64.Build.0 = Debug|x64
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Release|Mixed Platforms.ActiveCfg = Release|x64
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Release|Mixed Platforms.Build.0 = Release|x64
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Release|Win32.ActiveCfg = Release|Win32
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Release|Win32.Build.0 = Release|Win32
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Release|x64.ActiveCfg = Release|x64
+		{8C8B90DA-28B7-4D82-81F3-C0E7CE52D59F}.Release|x64.Build.0 = Release|x64
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Debug|Win32.ActiveCfg = Debug|Win32
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Debug|Win32.Build.0 = Debug|Win32
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Debug|x64.ActiveCfg = Debug|Win32
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Release|Mixed Platforms.ActiveCfg = Release|x64
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Release|Mixed Platforms.Build.0 = Release|x64
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Release|Win32.ActiveCfg = Release|Win32
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Release|Win32.Build.0 = Release|Win32
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Release|x64.ActiveCfg = Release|x64
+		{8939AD12-23D5-469C-806B-DC3F98F8A514}.Release|x64.Build.0 = Release|x64
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Debug|Win32.ActiveCfg = Debug|Win32
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Debug|Win32.Build.0 = Debug|Win32
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Debug|x64.ActiveCfg = Debug|Win32
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Release|Mixed Platforms.ActiveCfg = Release|x64
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Release|Mixed Platforms.Build.0 = Release|x64
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Release|Win32.ActiveCfg = Release|Win32
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Release|Win32.Build.0 = Release|Win32
+		{17823F37-86DE-4E58-B354-B84DA9EDA6A1}.Release|x64.ActiveCfg = Release|x64
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Debug|Win32.ActiveCfg = Debug|Win32
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Debug|Win32.Build.0 = Debug|Win32
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Debug|x64.ActiveCfg = Debug|x64
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Release|Mixed Platforms.Build.0 = Release|Win32
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Release|Win32.ActiveCfg = Release|Win32
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Release|Win32.Build.0 = Release|Win32
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Release|x64.ActiveCfg = Release|x64
+		{F3B0CC94-9DD0-4642-891C-EA08BDA50260}.Release|x64.Build.0 = Release|x64
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Debug|Win32.ActiveCfg = Debug|Win32
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Debug|Win32.Build.0 = Debug|Win32
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Debug|x64.ActiveCfg = Debug|x64
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Debug|x64.Build.0 = Debug|x64
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Release|Mixed Platforms.ActiveCfg = Release|Win32
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Release|Mixed Platforms.Build.0 = Release|Win32
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Release|Win32.ActiveCfg = Release|Win32
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Release|Win32.Build.0 = Release|Win32
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Release|x64.ActiveCfg = Release|x64
+		{785C60CA-BFEA-4208-BA3B-3D8E2A79FD9C}.Release|x64.Build.0 = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		VisualSVNWorkingCopyRoot = .
+	EndGlobalSection
+EndGlobal

=== added file 'kmer_counter/bam_utils.h'
--- old/kmer_counter/bam_utils.h	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/bam_utils.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,38 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _BAM_UTILS_H
+#define _BAM_UTILS_H
+#include "defs.h"
+#include <cinttypes>
+
+static inline void read_int32_t(int32_t& out, uint8_t* in, uint64_t& pos)
+{
+	out = 0;
+	for (int j = 0; j < 4; ++j)
+		out |= (uint32_t)in[pos++] << (j * 8);
+}
+
+static inline void read_uint32_t(uint32_t& out, uint8_t* in, uint64_t& pos)
+{
+	out = 0;
+	for (int j = 0; j < 4; ++j)
+		out |= (uint32_t)in[pos++] << (j * 8);
+}
+
+static inline void read_uint16_t(uint16_t& out, uint8_t* in, uint64_t& pos)
+{
+	out = 0;
+	for (int j = 0; j < 2; ++j)
+		out |= (uint16_t)in[pos++] << (j * 8);
+}
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmer_counter/binary_reader.h'
--- old/kmer_counter/binary_reader.h	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/binary_reader.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,423 @@
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _BINARY_READER_H
+#define _BINARY_READER_H
+
+#include "defs.h"
+#include "params.h"
+#include "queues.h"
+#include "percent_progress.h"
+#include "bam_utils.h"
+#include <sys/stat.h>
+
+class CBinaryFilesReader
+{
+	bool is_file(const char* path)
+	{
+#ifdef WIN32
+		typedef struct _stat64 stat_struct;
+		const auto& stat_func = _stat64;
+#else
+		typedef struct stat stat_struct;
+		const auto& stat_func = stat;
+#endif
+		stat_struct buf;
+		if (stat_func(path, &buf) == -1)
+			return false;
+		return (buf.st_mode & S_IFMT) == S_IFREG;
+	}
+	uint32 part_size;
+	CInputFilesQueue* input_files_queue;
+	CMemoryPool *pmm_binary_file_reader;
+	vector<CBinaryPackQueue*> binary_pack_queues;
+	CBamTaskManager* bam_task_manager = nullptr;
+	uint64 total_size;
+	uint64 predicted_size;	
+	CPercentProgress percent_progress;
+
+	bool bam_input = false; //for bam input behaviour of this class is quite different, for example only one file is readed at once
+	
+	void notify_readed(uint64 readed)
+	{		
+		percent_progress.NotifyProgress(readed);
+	}
+	CompressionType get_compression_type(const string& name)
+	{
+		if (name.size() > 3 && string(name.end() - 3, name.end()) == ".gz")
+			return CompressionType::gzip;
+		else if (name.size() > 4 && string(name.end() - 4, name.end()) == ".bz2")
+			return CompressionType::bzip2;
+		else
+			return CompressionType::plain;
+	}
+
+	void OpenFile(const string& file_name, FILE* &f, CompressionType& mode)
+	{
+		f = fopen(file_name.c_str(), "rb");
+		if (!f)
+		{
+			std::cerr << "Error: cannot open file: " << file_name << " for reading\n";
+			exit(1);
+		}
+
+		setvbuf(f, nullptr, _IONBF, 0);
+
+		// Set mode according to the extension of the file name
+		mode = get_compression_type(file_name);
+	}
+
+	uint64_t skipSingleBGZFBlock(uchar* buff)
+	{
+		uint64_t pos = 0;
+		if (buff[0] == 0x1f && buff[1] == 0x8b)
+			;
+		else
+		{
+			cerr << "Fail: this is not gzip file\n";
+			exit(1);
+		}
+
+		if (buff[2] != 8)
+		{
+			cerr << "Error: CM flag is set to " << buff[2] << " instead of 8 \n";
+			exit(1);
+		}
+
+		if (!((buff[3] >> 2) & 1))
+		{
+			cerr << "Error: FLG.FEXTRA is not set\n";
+			exit(1);
+		}
+
+		pos = 10;
+
+		uint16_t XLEN;
+		read_uint16_t(XLEN, buff, pos);
+
+		uchar SI1 = buff[pos++];
+		uchar SI2 = buff[pos++];
+		if (SI1 != 66 || SI2 != 67)
+		{
+			cerr << "Error: SI1 != 66 || SI2 != 67\n";
+			exit(1);
+		}
+		uint16_t LEN;
+		read_uint16_t(LEN, buff, pos);
+		if (LEN != 2)
+		{
+			cerr << "Error: SLEN is " << LEN << " instead of 2\n";
+			exit(1);
+		}
+
+		uint16_t BGZF_block_size_tmp;
+		read_uint16_t(BGZF_block_size_tmp, buff, pos);
+
+		uint64_t BGZF_block_size = BGZF_block_size_tmp + 1ull;  //This integer gives the size of the containing BGZF block minus one.
+		return BGZF_block_size;
+	}
+
+	uint64_t findLastBGZFBlockEnd(uchar* data, uint64_t size)
+	{
+		//header of BGZF block is 18 Bytes long
+		uint64_t pos = 0;
+		while (pos + 18 < size)
+		{
+			uint64_t npos = skipSingleBGZFBlock(data + pos);
+			if (pos + npos > size)
+				break;
+			pos += npos;
+		}
+		return pos;
+	}
+
+	void ProcessSingleBamFile(const string& fname, uint32 file_no, uint32& id, bool& forced_to_finish)
+	{
+		FILE* file = fopen(fname.c_str(), "rb");
+		if (!file)
+		{
+			cerr << "Error: cannot open file " << fname << "\n";
+			exit(1);
+		}
+		setvbuf(file, nullptr, _IONBF, 0);
+
+		unsigned char eof_marker[] = { 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x06, 0x00, 0x42, 0x43, 0x02, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+		unsigned char eof_to_chech[sizeof(eof_marker)];
+		fseek(file, -static_cast<int>(sizeof(eof_marker)), SEEK_END);
+		if (sizeof(eof_marker) != fread(eof_to_chech, 1, sizeof(eof_marker), file))
+		{
+			cerr << "Error: cannot check EOF marker of BAM file: " << fname << "\n";
+			exit(1);
+		}
+		if (!equal(begin(eof_marker), end(eof_marker), begin(eof_to_chech)))
+		{
+			cerr << "Error: wrong EOF marker of BAM file: " << fname << "\n";
+			exit(1);
+		}
+		fseek(file, 0, SEEK_SET);
+
+		uchar* data;
+		pmm_binary_file_reader->reserve(data);
+
+		uint64 size = 0;
+		
+		uint64 readed;
+		
+		while (!forced_to_finish && (readed = fread(data + size, 1, part_size - size, file)))
+		{
+			notify_readed(readed);
+			size += readed;
+			uint64_t lastBGFBlockEnd = findLastBGZFBlockEnd(data, size);
+			uchar* newData;
+			pmm_binary_file_reader->reserve(newData);
+
+			uint64_t tail = size - lastBGFBlockEnd;
+			memcpy(newData, data + lastBGFBlockEnd, tail);
+			size = lastBGFBlockEnd;
+			
+			if (!bam_task_manager->PushBinaryPack(data, size, id, file_no))
+			{
+				pmm_binary_file_reader->free(data);
+				forced_to_finish = true;
+			}
+			else
+				id++;
+			
+			data = newData;
+			size = tail;
+		}
+		if (!bam_task_manager->PushBinaryPack(data, size, id, file_no)) //last, possibly empty
+		{
+			pmm_binary_file_reader->free(data);			
+			forced_to_finish = true;
+		}
+		else
+			id++;
+		fclose(file);
+	}
+
+	void ProcessBam()
+	{
+		uint32 file_no = 0;
+		uint32 id = 0;
+		string fname;
+		bool forced_to_finish = false;
+		
+		while (!forced_to_finish && input_files_queue->pop(fname))
+		{
+			ProcessSingleBamFile(fname, file_no, id, forced_to_finish);
+			++file_no;
+		}
+		bam_task_manager->NotifyBinaryReaderCompleted(id-1);		
+	}
+
+public:
+	CBinaryFilesReader(CKMCParams &Params, CKMCQueues &Queues, bool _show_progress)
+		:
+		percent_progress("Stage 1: ", _show_progress)
+	{
+		part_size = (uint32)Params.mem_part_pmm_binary_file_reader;
+		input_files_queue = Queues.input_files_queue;
+		pmm_binary_file_reader = Queues.pmm_binary_file_reader;
+		binary_pack_queues = Queues.binary_pack_queues;		
+		bam_task_manager = Queues.bam_task_manager;
+		auto files_copy = input_files_queue->GetCopy();		
+		total_size = 0;
+		predicted_size = 0;
+		bam_input = Params.file_type == bam;
+
+		while (!files_copy.empty())
+		{
+			string& f_name = files_copy.front();
+			FILE* f = fopen(f_name.c_str(), "rb");
+			if(!is_file(f_name.c_str()))
+			{
+				cerr << "Error: " << f_name << " is not a file\n";
+				exit(1);
+			}
+			if (!f)
+			{
+				cerr << "Cannot open file: " << f_name << "\n";
+				exit(1);
+			}
+			my_fseek(f, 0, SEEK_END);
+			total_size += my_ftell(f);
+			if (bam_input)
+			{
+				predicted_size += (uint64)(0.7 * my_ftell(f)); //TODO: is this correct?
+			}
+			else
+			{
+				CompressionType compression = get_compression_type(f_name);
+				switch (compression)
+				{
+				case CompressionType::plain:
+					predicted_size += my_ftell(f);
+					break;
+				case CompressionType::gzip:
+					predicted_size += (uint64)(3.2 * my_ftell(f));
+					break;
+				case CompressionType::bzip2:
+					predicted_size += (uint64)(4.0 * my_ftell(f));
+					break;
+				default:
+					break;
+				}
+			}
+			fclose(f);
+			files_copy.pop();
+		}
+		percent_progress.SetMaxVal(total_size);
+	}
+	uint64 GetTotalSize()
+	{
+		return total_size;
+	}
+	uint64 GetPredictedSize()
+	{
+		return predicted_size;
+	}
+
+	void Process()
+	{
+		if (bam_input)
+		{
+			ProcessBam();
+			return;
+		}
+		std::string file_name;
+		vector<tuple<FILE*, CBinaryPackQueue*, CompressionType>> files;
+		files.reserve(binary_pack_queues.size());
+		uchar* part = nullptr;
+
+		uint32 completed = 0;
+		notify_readed(0);
+
+		for (uint32 i = 0; i < binary_pack_queues.size() && input_files_queue->pop(file_name); ++i)
+		{
+			CBinaryPackQueue* q = binary_pack_queues[i];
+			CompressionType mode;
+			FILE* f = nullptr;
+			OpenFile(file_name, f, mode);
+			files.push_back(make_tuple(f, q, mode));
+			pmm_binary_file_reader->reserve(part);
+			uint64 readed = fread(part, 1, part_size, f);
+			notify_readed(readed);
+			if (!q->push(part, readed, FilePart::Begin, mode))
+			{
+				pmm_binary_file_reader->free(part);
+				fclose(f);
+				get<0>(files.back()) = nullptr;
+				++completed;
+			}
+		}
+
+		bool forced_to_finish = false;
+
+		while (completed < files.size() && !forced_to_finish)
+		{
+			for (auto& f : files)
+			{
+				if (!get<0>(f))
+					continue;
+
+				pmm_binary_file_reader->reserve(part);				
+				uint64 readed = fread(part, 1, part_size, get<0>(f));				
+				notify_readed(readed);
+				if (readed == 0) //end of file, need to open next one if exists
+				{
+					pmm_binary_file_reader->free(part);
+					if (!get<1>(f)->push(nullptr, 0, FilePart::End, get<2>(f)))
+					{
+						forced_to_finish = true;
+						break;
+					}
+					fclose(get<0>(f));
+
+					if (input_files_queue->pop(file_name))
+					{
+						OpenFile(file_name, get<0>(f), get<2>(f));
+						pmm_binary_file_reader->reserve(part);
+						readed = fread(part, 1, part_size, get<0>(f));
+						notify_readed(readed);
+						if (!get<1>(f)->push(part, readed, FilePart::Begin, get<2>(f)))
+						{
+							pmm_binary_file_reader->free(part);
+							forced_to_finish = true;
+							break;
+						}
+					}
+					else
+					{						
+						++completed;
+						get<0>(f) = nullptr;
+						get<1>(f)->mark_completed();
+					}
+				}
+				else
+				{
+					if (!get<1>(f)->push(part, readed, FilePart::Middle, get<2>(f)))
+					{
+						pmm_binary_file_reader->free(part);
+						forced_to_finish = true;
+						break;
+					}
+				}
+			}
+		}
+
+		for (auto& f : files)
+		{
+			if (get<0>(f))
+			{
+				fclose(get<0>(f));
+				get<0>(f) = nullptr;
+			}
+		}
+
+		//user may specify more fastq_readers than input files
+		for (auto& e : binary_pack_queues)
+			e->mark_completed();
+	}
+};
+
+
+class CWBinaryFilesReader
+{
+	CBinaryFilesReader *reader;
+public:
+	CWBinaryFilesReader(CKMCParams &Params, CKMCQueues &Queues, bool show_progress = true)
+	{
+		reader = new CBinaryFilesReader(Params, Queues, show_progress);
+	}
+
+	uint64 GetPredictedSize()
+	{
+		return reader->GetPredictedSize();
+	}
+	uint64 GetTotalSize()
+	{
+		return reader->GetTotalSize();
+	}
+
+	void operator()()
+	{
+		reader->Process();
+	}
+	~CWBinaryFilesReader()
+	{
+		delete reader;
+	}
+};
+
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmer_counter/bkb_merger.h'
--- old/kmer_counter/bkb_merger.h	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/bkb_merger.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,280 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _HBH_MERGER_H
+#define _HBH_MERGER_H
+#include "bkb_subbin.h"
+
+
+//************************************************************************************************************
+// CBigKmerBinMerger - merger sorted k-mers from number of subbins 
+//************************************************************************************************************
+template<unsigned SIZE>
+class CBigKmerBinMerger
+{
+	vector<CSubBin<SIZE>*> sub_bins;
+	std::vector<std::tuple<CKmer<SIZE>, uint32, uint32>> curr_min;
+	CDiskLogger* disk_logger;
+	uint32 size;
+	CBigBinDesc* bbd;
+	CBigBinKmerPartQueue* bbkpq;
+	CCompletedBinsCollector* sm_cbc;
+	uint32 kmer_len;
+	uint32 lut_prefix_len;	
+	uint32 cutoff_min, cutoff_max, counter_max;
+	CMemoryPool* sm_pmm_merger_suff, *sm_pmm_merger_lut, *sm_pmm_sub_bin_suff, *sm_pmm_sub_bin_lut;
+	int64 sm_mem_part_merger_suff, sm_mem_part_merger_lut, sm_mem_part_sub_bin_suff, sm_mem_part_sub_bin_lut;
+	uchar *sub_bin_suff_buff, *sub_bin_lut_buff;
+public:
+	CBigKmerBinMerger(CKMCParams& Params, CKMCQueues& Queues);
+	void init(int32 bin_id, uint32 _size);
+	bool get_min(CKmer<SIZE>& kmer, uint32& count);
+	void Process();
+	~CBigKmerBinMerger();
+};
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE>
+CBigKmerBinMerger<SIZE>::CBigKmerBinMerger(CKMCParams& Params, CKMCQueues& Queues) 
+{
+	disk_logger = Queues.disk_logger;
+	bbd = Queues.bbd;
+	bbkpq = Queues.bbkpq;
+	sm_cbc = Queues.sm_cbc;
+	kmer_len = Params.kmer_len;
+	lut_prefix_len = Params.lut_prefix_len;
+	cutoff_min = Params.cutoff_min;
+	cutoff_max = (uint32)Params.cutoff_max;
+	counter_max = (uint32)Params.counter_max;
+	sm_pmm_merger_suff = Queues.sm_pmm_merger_suff;
+	sm_pmm_merger_lut = Queues.sm_pmm_merger_lut;
+	sm_pmm_sub_bin_suff = Queues.sm_pmm_sub_bin_suff;
+	sm_pmm_sub_bin_lut = Queues.sm_pmm_sub_bin_lut;
+	sm_mem_part_sub_bin_suff = Params.sm_mem_part_sub_bin_suff;
+	sm_mem_part_merger_suff = Params.sm_mem_part_merger_suff;
+	sm_mem_part_merger_lut = Params.sm_mem_part_merger_lut;
+	sm_mem_part_sub_bin_lut = Params.sm_mem_part_sub_bin_lut;
+
+	sm_pmm_sub_bin_lut->reserve(sub_bin_lut_buff);
+	sm_pmm_sub_bin_suff->reserve(sub_bin_suff_buff);
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE>
+CBigKmerBinMerger<SIZE>::~CBigKmerBinMerger()
+{
+	for (auto p : sub_bins)
+		delete p;
+	sm_pmm_sub_bin_lut->free(sub_bin_lut_buff);
+	sm_pmm_sub_bin_suff->free(sub_bin_suff_buff);
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE>
+void CBigKmerBinMerger<SIZE>::init(int32 bin_id, uint32 _size)
+{
+	size = _size;
+	uint32 prev_size = (uint32)sub_bins.size();
+	int32 sub_bin_id;
+	if (size > prev_size)
+	{
+		sub_bins.resize(size);
+		curr_min.resize(size);
+		for (uint32 i = prev_size; i < size; ++i)
+		{
+			sub_bins[i] = new CSubBin<SIZE>(disk_logger);
+		}
+	}
+
+	uint32 lut_prefix_len = 0;;
+	uint64 n_kmers = 0;
+	uint64 file_size = 0;
+	FILE* file = nullptr;
+	string name;
+	uint32 per_sub_bin_lut_size = (uint32)(sm_mem_part_sub_bin_lut / size);
+	uint32 per_sub_bin_suff_size = (uint32)(sm_mem_part_sub_bin_suff / size);
+	for (uint32 i = 0; i < size; ++i)
+	{
+		bbd->next_sub_bin(bin_id, sub_bin_id, lut_prefix_len, n_kmers, file, name, file_size);
+		sub_bins[i]->init(file, file_size, lut_prefix_len, n_kmers, name, kmer_len, sub_bin_lut_buff + i * per_sub_bin_lut_size, per_sub_bin_lut_size, sub_bin_suff_buff + i * per_sub_bin_suff_size, per_sub_bin_suff_size);
+		get<2>(curr_min[i]) = i;
+		sub_bins[i]->get_min(get<0>(curr_min[i]), get<1>(curr_min[i]));
+	}
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE>
+bool CBigKmerBinMerger<SIZE>::get_min(CKmer<SIZE>& kmer, uint32& count)
+{
+	if (!size)
+		return false;
+	uint32 min = 0;
+	for (uint32 i = 1; i < size; ++i)
+		if (get<0>(curr_min[i]) < get<0>(curr_min[min]))
+			min = i;
+
+	kmer = get<0>(curr_min[min]);
+	count = get<1>(curr_min[min]);
+	if (sub_bins[get<2>(curr_min[min])]->get_min(get<0>(curr_min[min]), get<1>(curr_min[min])))
+		;
+	else
+		curr_min[min] = curr_min[--size];
+	return true;
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE>
+void CBigKmerBinMerger<SIZE>::Process()
+{
+	int32 bin_id;
+	uint32 size = 0;
+	uint32 counter_size = min(BYTE_LOG(cutoff_max), BYTE_LOG(counter_max));
+	uint32 lut_recs = 1 << 2 * lut_prefix_len;
+	uint32 kmer_symbols = (kmer_len - lut_prefix_len);
+	uint32 kmer_bytes = kmer_symbols / 4;
+	uint32 suff_rec_bytes = kmer_bytes + counter_size;
+	uint64 suff_buff_size = sm_mem_part_merger_suff / suff_rec_bytes * suff_rec_bytes;
+	uint64 suff_buff_pos = 0;
+	uint64 n_unique, n_cutoff_min, n_cutoff_max, n_total;
+	CKmer<SIZE> kmer, next_kmer;
+	kmer.clear();
+	next_kmer.clear();
+	uint32 count_tmp = 0, count = 0;
+	int32 max_in_lut = (int32)(sm_mem_part_merger_lut / sizeof(uint64));
+
+	while (sm_cbc->pop(bin_id))
+	{
+		bbd->get_n_sub_bins(bin_id, size);
+		uchar *raw_lut;
+		sm_pmm_merger_lut->reserve(raw_lut);
+		uint64 *lut = (uint64*)raw_lut;
+		uchar* suff_buff;
+		sm_pmm_merger_suff->reserve(suff_buff);
+		suff_buff_pos = 0;
+		n_unique = n_cutoff_min = n_cutoff_max = n_total = 0;
+		fill_n(lut, max_in_lut, 0);
+		init(bin_id, size);
+
+		get_min(kmer, count_tmp);
+		count = count_tmp;
+		uint32 lut_offset = 0;
+		uint64 prefix;
+		while (get_min(next_kmer, count_tmp))
+		{
+			if (kmer == next_kmer)
+				count += count_tmp;
+			else
+			{
+				++n_unique;
+				n_total += count;
+				if (count < cutoff_min)
+					n_cutoff_min++;
+				else if (count > cutoff_max)
+					n_cutoff_max++;
+				else
+				{
+					if (count > counter_max)
+						count = counter_max;
+
+					//store
+					prefix = kmer.remove_suffix(2 * kmer_symbols);
+					if (prefix >= max_in_lut + lut_offset)
+					{
+						bbkpq->push(bin_id, nullptr, 0, raw_lut, max_in_lut * sizeof(uint64), 0, 0, 0, 0, false);
+						lut_offset += max_in_lut;
+						sm_pmm_merger_lut->reserve(raw_lut);
+						lut = (uint64*)raw_lut;
+						fill_n(lut, max_in_lut, 0);
+					}
+
+					lut[prefix - lut_offset]++;
+
+					for (int32 j = (int32)kmer_bytes - 1; j >= 0; --j)
+						suff_buff[suff_buff_pos++] = kmer.get_byte(j);
+					for (int32 j = 0; j < (int32)counter_size; ++j)
+						suff_buff[suff_buff_pos++] = (count >> (j * 8)) & 0xFF;
+
+					if (suff_buff_pos >= suff_buff_size)
+					{
+						bbkpq->push(bin_id, suff_buff, suff_buff_pos, nullptr, 0, 0, 0, 0, 0, false);
+						suff_buff_pos = 0;
+						sm_pmm_merger_suff->reserve(suff_buff);
+					}
+				}
+				count = count_tmp;
+				kmer = next_kmer;
+			}
+		}
+		++n_unique;
+		n_total += count;
+		if (count < cutoff_min)
+			++n_cutoff_min;
+		else if (count > cutoff_max)
+			++n_cutoff_max;
+		else
+		{
+			if (count > counter_max)
+				count = counter_max;
+
+			//store
+			lut[kmer.remove_suffix(2 * kmer_symbols)]++;
+
+			for (int32 j = (int32)kmer_bytes - 1; j >= 0; --j)
+				suff_buff[suff_buff_pos++] = kmer.get_byte(j);
+			for (int32 j = 0; j < (int32)counter_size; ++j)
+				suff_buff[suff_buff_pos++] = (count >> (j * 8)) & 0xFF;
+		}
+		bbkpq->push(bin_id, suff_buff, suff_buff_pos, raw_lut, (lut_recs - lut_offset) * sizeof(uint64), n_unique, n_cutoff_min, n_cutoff_max, n_total, true);
+	}
+
+	bbkpq->mark_completed();
+}
+
+
+//************************************************************************************************************
+// CWBigKmerBinMerger - wrapper for multithreading purposes
+//************************************************************************************************************
+template<unsigned SIZE>
+class CWBigKmerBinMerger
+{
+	CBigKmerBinMerger<SIZE> *merger;
+public:
+	CWBigKmerBinMerger(CKMCParams& Params, CKMCQueues& Queues);
+	~CWBigKmerBinMerger();
+	void operator()();
+};
+
+//----------------------------------------------------------------------------------
+// Constructor
+template<unsigned SIZE>
+CWBigKmerBinMerger<SIZE>::CWBigKmerBinMerger(CKMCParams& Params, CKMCQueues& Queues)
+{
+	merger = new CBigKmerBinMerger<SIZE>(Params, Queues);
+}
+
+//----------------------------------------------------------------------------------
+// Destructor
+template<unsigned SIZE>
+CWBigKmerBinMerger<SIZE>::~CWBigKmerBinMerger()
+{
+	delete merger;
+}
+
+#endif
+
+//----------------------------------------------------------------------------------
+// Execution
+template<unsigned SIZE>
+void CWBigKmerBinMerger<SIZE>::operator()()
+{
+	merger->Process();
+}
+
+// ***** EOF 
\ No newline at end of file

=== added file 'kmer_counter/bkb_reader.cpp'
--- old/kmer_counter/bkb_reader.cpp	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/bkb_reader.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,99 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "bkb_reader.h"
+
+
+//************************************************************************************************************
+// CBigKmerBinReader 
+//************************************************************************************************************
+
+//----------------------------------------------------------------------------------
+CBigKmerBinReader::CBigKmerBinReader(CKMCParams& Params, CKMCQueues& Queues)
+{
+	tlbq = Queues.tlbq;
+	disk_logger = Queues.disk_logger;
+	bd   = Queues.bd;
+	bbpq = Queues.bbpq;
+	sm_pmm_input_file = Queues.sm_pmm_input_file;
+	sm_mem_part_input_file = Params.sm_mem_part_input_file;
+}
+
+//----------------------------------------------------------------------------------
+void CBigKmerBinReader::ProcessBigBin()
+{
+	int32 bin_id;
+	CMemDiskFile *file;
+	string name;
+	uint64 size, n_rec, n_plus_x_recs, in_buffer, end_pos;
+	uint32 buffer_size, kmer_len;		
+	uchar *file_buff, *tmp;
+	
+	while (tlbq->get_next(bin_id))
+	{
+		bd->read(bin_id, file, name, size, n_rec, n_plus_x_recs, buffer_size, kmer_len);
+		cerr << "*";
+		file->Rewind();
+		end_pos = 0;
+		sm_pmm_input_file->reserve(file_buff);
+		while ( (in_buffer = end_pos + file->Read(file_buff + end_pos, 1, sm_mem_part_input_file - end_pos)) )
+		{
+			end_pos = 0;
+			for (; end_pos + 1 + (file_buff[end_pos] + kmer_len + 3) / 4 <= in_buffer; end_pos += 1 + (file_buff[end_pos] + kmer_len + 3) / 4);
+			uint64 rest = in_buffer - end_pos;
+			sm_pmm_input_file->reserve(tmp);
+			memcpy(tmp, file_buff + end_pos, rest);
+			bbpq->push(bin_id, file_buff, end_pos);
+			file_buff = tmp;
+			end_pos = rest;
+		}
+		sm_pmm_input_file->free(file_buff);
+		file->Close();
+
+		//Remove file
+		file->Remove();
+		disk_logger->log_remove(size);
+	}
+	bbpq->mark_completed();
+}
+
+//----------------------------------------------------------------------------------
+CBigKmerBinReader::~CBigKmerBinReader()
+{
+
+}
+
+//************************************************************************************************************
+// CWBigKmerBinReader - wrapper for multithreading purposes
+//************************************************************************************************************
+
+//----------------------------------------------------------------------------------
+// Constructor
+CWBigKmerBinReader::CWBigKmerBinReader(CKMCParams& Params, CKMCQueues& Queues)
+{
+	bkb_reader = new CBigKmerBinReader(Params, Queues);
+}
+
+//----------------------------------------------------------------------------------
+// Destructor
+CWBigKmerBinReader::~CWBigKmerBinReader()
+{
+	delete bkb_reader;
+}
+
+//----------------------------------------------------------------------------------
+// Execution
+void CWBigKmerBinReader::operator()()
+{
+	bkb_reader->ProcessBigBin();
+}
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmer_counter/bkb_reader.h'
--- old/kmer_counter/bkb_reader.h	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/bkb_reader.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,51 @@
+
+/*
+This file is a part of KMC software distributed under GNU GPL 3 licence.
+The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+
+Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+
+Version: 3.1.1
+Date   : 2019-05-19
+*/
+
+#ifndef _BKB_READER_H_
+#define  _BKB_READER_H_
+
+#include "params.h"
+
+//************************************************************************************************************
+// CBigKmerBinReader - reader of bins from distribution phase. Only in strict memory mode
+//************************************************************************************************************
+
+class CBigKmerBinReader
+{
+	CTooLargeBinsQueue * tlbq;
+	CDiskLogger* disk_logger;
+	CBinDesc* bd;
+	CBigBinPartQueue* bbpq;
+	CMemoryPool* sm_pmm_input_file;
+
+	uint64 sm_mem_part_input_file;
+public:
+	CBigKmerBinReader(CKMCParams& Params, CKMCQueues& Queues);
+	~CBigKmerBinReader();
+	void ProcessBigBin();
+};
+
+
+//************************************************************************************************************
+// CWBigKmerBinReader - wrapper for multithreading purposes
+//************************************************************************************************************
+class CWBigKmerBinReader
+{
+	CBigKmerBinReader* bkb_reader;
+public:
+	CWBigKmerBinReader(CKMCParams& Params, CKMCQueues& Queues);
+	~CWBigKmerBinReader();
+	void operator()();
+};
+
+#endif 
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmer_counter/bkb_sorter.h'
--- old/kmer_counter/bkb_sorter.h	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/bkb_sorter.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,511 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _BKB_SORTER_H
+#define _BKB_SORTER_H
+
+#include "radix.h"
+#include "raduls.h"
+#include "kxmer_set.h"
+#include "params.h"
+
+//************************************************************************************************************
+// CBigKmerBinSorter - sorter for part of bin, only in strict memory mode
+//************************************************************************************************************
+template<unsigned SIZE>
+class CBigKmerBinSorter
+{			
+	CBigBinKXmersQueue* bbkq;
+	CBigBinDesc* bbd;	
+	CBigBinSortedPartQueue* bbspq;
+	CMemoryPool *pmm_radix_buf, *sm_pmm_expand, *sm_pmm_sorter_suffixes, *sm_pmm_sorter_lut, *sm_pmm_sort;
+
+	uchar* _raw_kxmers;
+
+	int64 sm_mem_part_suffixes;
+
+	CKXmerSet<SIZE> kxmer_set;
+
+
+	CKmer<SIZE>* kxmers;
+	CKmer<SIZE>* sort_tmp;
+	CKmer<SIZE>* sorted_kxmers;
+	uint32 *kxmers_counters;
+	uint64 kxmers_size;
+	uint64 kxmers_pos;
+	
+	uint32 *kxmer_counters;
+
+	int32 lut_prefix_len;
+	int n_sorting_threads;
+	int32 bin_id;
+	uint32 sub_bin_id;
+
+	uint32 max_x;
+	uint32 kmer_len;	
+	uint64 sum_n_rec, sum_n_plus_x_rec;
+
+	SortFunction<CKmer<SIZE>> sort_func;
+
+	void PostProcessKmers();
+	void PostProcessKxmers();
+	void PreCompactKxmers(uint64& compacted_count, uint32* counters);
+	uint64 FindFirstSymbOccur(uint64 start_pos, uint64 end_pos, uint32 offset, uchar symb);
+	void InitKXMerSet(uint64 start_pos, uint64 end_pos, uint32 offset, uint32 depth);
+	void PostProcessSort();
+
+	void Sort();	
+
+public:
+	CBigKmerBinSorter(CKMCParams& Params, CKMCQueues& Queues, SortFunction<CKmer<SIZE>> sort_func);
+	~CBigKmerBinSorter();
+	void Process();
+	
+};
+
+
+//************************************************************************************************************
+// CBigKmerBinSorter
+//************************************************************************************************************
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinSorter<SIZE>::Process()
+{
+	int32 curr_bin_id = -1;
+	bin_id = -1;
+	uchar* data = nullptr;
+	uint64 size = 0;	
+	kxmers_pos = 0;
+	sub_bin_id = 0;
+	
+	while (bbkq->pop(curr_bin_id, data, size))
+	{		
+		if (bin_id == -1)
+			bin_id = curr_bin_id;
+
+		if (curr_bin_id != bin_id) //new bin
+		{
+			if (kxmers_pos)
+			{			
+				Sort();			
+				PostProcessSort();				
+				kxmers_pos = 0;
+			}
+			bin_id = curr_bin_id;
+			sub_bin_id = 0;			
+		}
+
+		if (kxmers_pos + size < kxmers_size)
+		{			
+			memcpy(kxmers + kxmers_pos, data, size * sizeof(CKmer<SIZE>));
+			sm_pmm_expand->free(data);
+			kxmers_pos += size;
+		}
+		else
+		{
+			Sort();
+			PostProcessSort();
+			++sub_bin_id;
+			memcpy(kxmers, data, size * sizeof(CKmer<SIZE>));
+			sm_pmm_expand->free(data);
+			kxmers_pos = size;
+		}
+	}
+	if (kxmers_pos)
+	{
+		Sort();
+		PostProcessSort();
+	}	
+	bbspq->mark_completed();
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE>
+CBigKmerBinSorter<SIZE>::CBigKmerBinSorter(CKMCParams& Params, CKMCQueues& Queues, SortFunction<CKmer<SIZE>> sort_func) : 
+	kxmer_set(Params.kmer_len), 
+	sort_func(sort_func)
+{	
+	sorted_kxmers = nullptr;
+	kxmer_counters = nullptr;
+	bbkq = Queues.bbkq;	
+	bbspq = Queues.bbspq;
+	pmm_radix_buf = Queues.pmm_radix_buf;
+	sm_pmm_expand = Queues.sm_pmm_expand;
+	sm_pmm_sorter_suffixes = Queues.sm_pmm_sorter_suffixes;
+	sm_pmm_sorter_lut = Queues.sm_pmm_sorter_lut;
+	sm_pmm_sort = Queues.sm_pmm_sort;
+
+	kxmers_size = Params.sm_mem_part_sort / 2 / sizeof(CKmer<SIZE>);
+
+	sm_mem_part_suffixes = Params.sm_mem_part_suffixes;
+		
+	sm_pmm_sort->reserve(_raw_kxmers);
+	kxmers = (CKmer<SIZE>*)_raw_kxmers;
+	
+	sort_tmp = kxmers + kxmers_size;
+	max_x = Params.max_x;
+	bbd = Queues.bbd;	
+	kmer_len = Params.kmer_len;	
+
+	lut_prefix_len = Params.lut_prefix_len;
+
+	n_sorting_threads = Params.sm_n_sorting_threads;
+	
+	sum_n_rec = sum_n_plus_x_rec = 0;	
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> CBigKmerBinSorter<SIZE>::~CBigKmerBinSorter()
+{	
+	sm_pmm_sort->free(_raw_kxmers);
+}
+
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE>
+void CBigKmerBinSorter<SIZE>::Sort()
+{
+	uint32 rec_len;
+	uint64 sort_rec = kxmers_pos;	
+	if (max_x)
+	{
+		rec_len = (kmer_len + max_x + 1 + 3) / 4;
+	}
+	else
+	{
+		rec_len = (kmer_len + 3) / 4;
+	}
+	sum_n_plus_x_rec += kxmers_pos;
+
+	sort_func(kxmers, sort_tmp, sort_rec, rec_len - 1, n_sorting_threads, pmm_radix_buf);
+	
+	if (rec_len % 2)
+	{
+		kxmers_counters = (uint32*)kxmers;
+		sorted_kxmers = sort_tmp;
+	}
+	else
+	{
+		kxmers_counters = (uint32*)sort_tmp;
+		sorted_kxmers = kxmers;
+	}
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinSorter<SIZE>::PostProcessKmers()
+{
+	uint32 best_lut_prefix_len = 0;
+	uint32 local_lut_prefix_len;
+	uint64 best_mem_amount = 1ull << 62;
+
+	uint32 counter_size = sizeof(uint32);
+
+	for (local_lut_prefix_len = 2; local_lut_prefix_len < 13; ++local_lut_prefix_len)
+	{
+		uint32 suffix_len = kmer_len - local_lut_prefix_len;
+		if (suffix_len % 4)
+			continue;
+
+		uint64 suf_mem = (suffix_len / 4 + counter_size) * kxmers_pos;
+		uint64 lut_mem = (1ull << (2 * local_lut_prefix_len)) * sizeof(uint64);
+		if (suf_mem + lut_mem < best_mem_amount)
+		{
+			best_mem_amount = suf_mem + lut_mem;
+			best_lut_prefix_len = local_lut_prefix_len;
+		}
+	}
+	local_lut_prefix_len = best_lut_prefix_len;
+
+	uint32 kmer_symbols = kmer_len - local_lut_prefix_len;
+	uint64 kmer_bytes = kmer_symbols / 4;
+
+	uint32 suffix_rec_bytes = (kmer_len - local_lut_prefix_len) / 4 + counter_size;
+	uint64 lut_recs = 1ull << 2 * local_lut_prefix_len;
+
+
+	uchar* suff_buff;
+	sm_pmm_sorter_suffixes->reserve(suff_buff);
+	uchar* _raw_lut;
+	sm_pmm_sorter_lut->reserve(_raw_lut);
+	uint64* lut = (uint64*)_raw_lut;
+	fill_n(lut, lut_recs, 0);
+
+	uint64 suff_buff_size = sm_mem_part_suffixes / suffix_rec_bytes * suffix_rec_bytes;
+
+	uint64 suff_buff_pos = 0;
+	uint64 n_recs = 0;
+	CKmer<SIZE> *act_kmer;
+	uint32 count;
+	uint64 i;
+	act_kmer = &kxmers[0];
+	count = 1;	
+	for (i = 1; i < kxmers_pos; ++i)
+	{
+		if (*act_kmer == kxmers[i])
+			count++;
+		else
+		{
+			lut[act_kmer->remove_suffix(2 * kmer_symbols)]++;
+			for (int32 j = (int32)kmer_bytes - 1; j >= 0; --j)
+				suff_buff[suff_buff_pos++] = act_kmer->get_byte(j);
+			for (int32 j = 0; j < (int32)counter_size; ++j)
+				suff_buff[suff_buff_pos++] = (count >> (j * 8)) & 0xFF;
+			++n_recs;
+
+			if (suff_buff_pos >= suff_buff_size)
+			{
+				bbspq->push(bin_id, sub_bin_id, suff_buff, suff_buff_pos, nullptr, 0, false);
+				sm_pmm_sorter_suffixes->reserve(suff_buff);
+				suff_buff_pos = 0;
+			}
+
+			count = 1;
+			act_kmer = &kxmers[i];
+		}
+	}
+
+	lut[act_kmer->remove_suffix(2 * kmer_symbols)]++;
+
+	for (int32 j = (int32)kmer_bytes - 1; j >= 0; --j)
+		suff_buff[suff_buff_pos++] = act_kmer->get_byte(j);
+	for (int32 j = 0; j < (int32)counter_size; ++j)
+		suff_buff[suff_buff_pos++] = (count >> (j * 8)) & 0xFF;
+
+	++n_recs;
+
+	bbspq->push(bin_id, sub_bin_id, suff_buff, suff_buff_pos, nullptr, 0, false);
+	bbspq->push(bin_id, sub_bin_id, nullptr, 0, lut, lut_recs, true);
+	bbd->push(bin_id, sub_bin_id, local_lut_prefix_len, n_recs, nullptr, "", 0);
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinSorter<SIZE>::PreCompactKxmers(uint64& compacted_count, uint32* counters)
+{
+	compacted_count = 0;
+
+	CKmer<SIZE> *act_kmer;
+	act_kmer = &sorted_kxmers[0];
+	counters[compacted_count] = 1;
+
+	for (uint32 i = 1; i < kxmers_pos; ++i)
+	{
+		if (*act_kmer == sorted_kxmers[i])
+			++counters[compacted_count];
+		else
+		{
+			sorted_kxmers[compacted_count++] = *act_kmer;
+			counters[compacted_count] = 1;
+			act_kmer = &sorted_kxmers[i];
+		}
+	}
+	sorted_kxmers[compacted_count++] = *act_kmer;
+}
+
+//----------------------------------------------------------------------------------
+//Binary search position of first occurrence of symbol 'symb' in [start_pos,end_pos). Offset defines which symbol in k+x-mer is taken.
+template <unsigned SIZE> uint64 CBigKmerBinSorter<SIZE>::FindFirstSymbOccur(uint64 start_pos, uint64 end_pos, uint32 offset, uchar symb)
+{
+	uint32 kxmer_offset = (kmer_len + max_x - offset) * 2;
+	uint64 middle_pos;
+	uchar middle_symb;
+	while (start_pos < end_pos)
+	{
+		middle_pos = (start_pos + end_pos) / 2;
+		middle_symb = sorted_kxmers[middle_pos].get_2bits(kxmer_offset);
+		if (middle_symb < symb)
+			start_pos = middle_pos + 1;
+		else
+			end_pos = middle_pos;
+	}
+	return end_pos;
+}
+
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinSorter<SIZE>::InitKXMerSet(uint64 start_pos, uint64 end_pos, uint32 offset, uint32 depth)
+{
+	if (start_pos == end_pos)
+		return;
+	uint32 shr = max_x + 1 - offset;
+	kxmer_set.init_add(start_pos, end_pos, shr);
+
+	--depth;
+	if (depth > 0)
+	{
+		uint64 pos[5];
+		pos[0] = start_pos;
+		pos[4] = end_pos;
+		for (uint32 i = 1; i < 4; ++i)
+			pos[i] = FindFirstSymbOccur(pos[i - 1], end_pos, offset, i);
+		for (uint32 i = 1; i < 5; ++i)
+			InitKXMerSet(pos[i - 1], pos[i], offset + 1, depth);
+	}
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinSorter<SIZE>::PostProcessKxmers()
+{
+	kxmer_set.clear();
+	kxmer_set.set_buffer(sorted_kxmers);
+
+	uint32 best_lut_prefix_len = 0;
+	uint32 local_lut_prefix_len;
+	uint64 best_mem_amount = 1ull << 62;
+
+	uint32 counter_size = sizeof(uint32);
+
+	for (local_lut_prefix_len = 2; local_lut_prefix_len < 13; ++local_lut_prefix_len) 
+	{
+		uint32 suffix_len = kmer_len - local_lut_prefix_len;
+		if(suffix_len % 4)
+			continue;
+
+		uint64 suf_mem = (suffix_len / 4 + counter_size) * kxmers_pos;
+		uint64 lut_mem = (1ull << (2 * local_lut_prefix_len)) * sizeof(uint64);
+		if (suf_mem + lut_mem < best_mem_amount)
+		{
+			best_mem_amount = suf_mem + lut_mem;
+			best_lut_prefix_len = local_lut_prefix_len;
+		}
+	}
+	local_lut_prefix_len = best_lut_prefix_len;
+
+
+	uint32 kmer_symbols = kmer_len - local_lut_prefix_len;
+	uint64 kmer_bytes = kmer_symbols / 4;
+
+	uint32 suffix_rec_bytes = (kmer_len - local_lut_prefix_len) / 4 + counter_size;
+	uint64 lut_recs = 1ull << 2 * local_lut_prefix_len;
+	
+
+	uchar* suff_buff;
+	sm_pmm_sorter_suffixes->reserve(suff_buff);
+	uchar* _raw_lut;
+	sm_pmm_sorter_lut->reserve(_raw_lut);
+	uint64* lut = (uint64*)_raw_lut;
+	fill_n(lut, lut_recs, 0);
+
+	uint64 suff_buff_size = sm_mem_part_suffixes / suffix_rec_bytes * suffix_rec_bytes;
+	
+	uint64 suff_buff_pos = 0;
+	uint64 n_recs = 0;
+
+	uint64 compacted_count;
+	PreCompactKxmers(compacted_count, kxmers_counters);
+	
+
+	uint64 pos[5];
+	pos[0] = 0;
+	pos[4] = compacted_count;
+	for(uint32 i = 1 ; i < 4 ; ++i)
+		pos[i] = FindFirstSymbOccur(pos[i - 1], compacted_count, 0, i);
+	for (uint32 i = 1; i < 5; ++i)
+		InitKXMerSet(pos[i - 1], pos[i], max_x + 2 - i, i);
+
+
+	uint64 counter_pos = 0;
+
+	CKmer<SIZE> kmer, next_kmer;
+	kmer.clear();
+	next_kmer.clear();
+	CKmer<SIZE> kmer_mask;
+	uint32 count;
+	kmer_mask.set_n_1(kmer_len * 2);
+	kxmer_set.get_min(counter_pos, kmer);
+	count = kxmers_counters[counter_pos];
+
+	while (kxmer_set.get_min(counter_pos, next_kmer))
+	{
+		if (kmer == next_kmer)
+			count += kxmers_counters[counter_pos];
+		else
+		{
+			lut[kmer.remove_suffix(2 * kmer_symbols)]++;			
+			for (int32 j = (int32)kmer_bytes - 1; j >= 0; --j)
+				suff_buff[suff_buff_pos++] = kmer.get_byte(j);
+			for (int32 j = 0; j < (int32)counter_size; ++j)
+				suff_buff[suff_buff_pos++] = (count >> (j * 8)) & 0xFF;
+			++n_recs;
+
+			if (suff_buff_pos >= suff_buff_size)
+			{				
+				bbspq->push(bin_id, sub_bin_id, suff_buff, suff_buff_pos, nullptr, 0, false);
+				sm_pmm_sorter_suffixes->reserve(suff_buff);
+				suff_buff_pos = 0;
+			}
+			
+			count = kxmers_counters[counter_pos];
+			kmer = next_kmer;
+		}
+	}
+			
+	lut[kmer.remove_suffix(2 * kmer_symbols)]++;
+
+	for (int32 j = (int32)kmer_bytes - 1; j >= 0; --j)
+		suff_buff[suff_buff_pos++] = kmer.get_byte(j);
+	for (int32 j = 0; j < (int32)counter_size; ++j)
+		suff_buff[suff_buff_pos++] = (count >> (j * 8)) & 0xFF;
+
+	++n_recs;
+
+	bbspq->push(bin_id, sub_bin_id, suff_buff, suff_buff_pos, nullptr, 0, false);
+	bbspq->push(bin_id, sub_bin_id, nullptr, 0, lut, lut_recs, true);
+	bbd->push(bin_id, sub_bin_id, local_lut_prefix_len, n_recs, nullptr, "", 0);
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinSorter<SIZE>::PostProcessSort()
+{
+	if (max_x)
+		PostProcessKxmers();
+	else
+		PostProcessKmers();
+}
+
+
+//************************************************************************************************************
+// CWBigKmerBinSorter - wrapper for multithreading purposes
+//************************************************************************************************************
+template<unsigned SIZE>
+class CWBigKmerBinSorter
+{
+	CBigKmerBinSorter<SIZE>* bkb_sorter;
+public:
+	CWBigKmerBinSorter(CKMCParams& Params, CKMCQueues& Queues, SortFunction<CKmer<SIZE>> sort_func);
+	~CWBigKmerBinSorter();
+	void operator()();
+};
+
+//----------------------------------------------------------------------------------
+// Constructor
+template<unsigned SIZE>
+CWBigKmerBinSorter<SIZE>::CWBigKmerBinSorter(CKMCParams& Params, CKMCQueues& Queues, SortFunction<CKmer<SIZE>> sort_func)
+{
+	bkb_sorter = new CBigKmerBinSorter<SIZE>(Params, Queues, sort_func);
+}
+
+//----------------------------------------------------------------------------------
+// Destructor
+template<unsigned SIZE>
+CWBigKmerBinSorter<SIZE>::~CWBigKmerBinSorter()
+{
+	delete bkb_sorter;
+}
+
+//----------------------------------------------------------------------------------
+// Execution
+template<unsigned SIZE>
+void CWBigKmerBinSorter<SIZE>::operator()()
+{
+	bkb_sorter->Process();
+}
+#endif  
+
+// ***** EOF 
\ No newline at end of file

=== added file 'kmer_counter/bkb_subbin.h'
--- old/kmer_counter/bkb_subbin.h	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/bkb_subbin.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,152 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _BKB_SUBBIN_H
+#define _BKB_SUBBIN_H
+
+//************************************************************************************************************
+// CSubBin - sorted k-mers (part of some bin), used in strict memory mode 
+//************************************************************************************************************
+template<unsigned SIZE>
+class CSubBin
+{
+	CDiskLogger* disk_logger;
+	uchar* raw_lut;
+	uint64* lut;
+	uint32 current_prefix;
+	uchar* suff_buff;
+	uint64 suff_buff_size, max_in_suff_buff, lut_start_pos_in_file;	
+	uint64 left_to_read, n_kmers, in_current_prefix;
+	uint32 kmer_len, lut_size, lut_buff_recs, lut_offset, cur_in_suff_buff, suff_buff_pos;
+	string name;
+	FILE* file;		
+	uint32 suff_rec_len, lut_prefix_len, counter_size, suffix_bytes;	
+	uint64 size;
+	void read_next_lut_part();
+public:
+	bool get_min(CKmer<SIZE>& kmer, uint32& count);
+	CSubBin(CDiskLogger* _disk_logger)
+	{
+		lut_size = 0;
+		disk_logger = _disk_logger;
+	}
+	void init(FILE* _file, uint64 _size, uint32 _lut_prefix_len, uint64 _n_kmers, string _name, uint32 _kmer_len, uchar* _lut_buff, uint32 _lut_buff_size, uchar* _suff_buff, uint64 _suff_buff_size);	
+};
+
+//--------------------------------------------------------------------------
+template<unsigned SIZE>
+void CSubBin<SIZE>::read_next_lut_part()
+{
+	uint32 to_read = MIN(lut_size - lut_offset, lut_buff_recs);
+	lut_offset += lut_buff_recs;
+	if (to_read)
+	{
+		uint64 prev_pos = my_ftell(file);
+		my_fseek(file, lut_start_pos_in_file + (lut_offset - lut_buff_recs) * sizeof(uint64), SEEK_SET);
+		if (fread(lut, sizeof(uint64), to_read, file) != to_read)
+		{
+			cerr << "Error while reading file : " << name << "\n";
+			exit(1);
+		}
+		my_fseek(file, prev_pos, SEEK_SET);
+	}
+}
+
+//--------------------------------------------------------------------------
+template<unsigned SIZE>
+bool CSubBin<SIZE>::get_min(CKmer<SIZE>& kmer, uint32& count)
+{
+	while (true)
+	{
+		if (current_prefix >= lut_offset)
+		{
+			read_next_lut_part();
+		}
+		if (in_current_prefix >= lut[current_prefix + lut_buff_recs - lut_offset])
+		{
+			++current_prefix;
+			in_current_prefix = 0;
+		}
+		else
+		{
+			++in_current_prefix;
+			break;
+		}
+		if (current_prefix >= lut_size)
+		{
+			fclose(file);
+			remove(name.c_str());
+			disk_logger->log_remove(size);
+			return false;
+		}
+	}
+
+	uchar *suf_rec = suff_buff + suff_buff_pos * suff_rec_len;
+	uint32 tmp = current_prefix;
+	uint32 pos = suffix_bytes;
+	kmer.load(suf_rec, suffix_bytes);
+	while (tmp)
+	{
+		kmer.set_byte(pos++, (uchar)tmp & 0xFF);
+		tmp >>= 8;
+	}
+
+	count = 0;
+	for (uint32 i = 0; i < counter_size; ++i)
+		count += (*suf_rec++) << (8 * i);
+
+	suff_buff_pos++;
+
+	if (suff_buff_pos >= cur_in_suff_buff)
+	{
+		cur_in_suff_buff = (uint32)fread(suff_buff, 1, MIN(suff_rec_len * max_in_suff_buff, left_to_read), file) / suff_rec_len;
+		suff_buff_pos = 0;
+		left_to_read -= cur_in_suff_buff * suff_rec_len;
+	}
+	return true;
+}
+
+//--------------------------------------------------------------------------
+template<unsigned SIZE>
+void CSubBin<SIZE>::init(FILE* _file, uint64 _size, uint32 _lut_prefix_len, uint64 _n_kmers, string _name, uint32 _kmer_len, uchar* _lut_buff, uint32 _lut_buff_size, uchar* _suff_buff, uint64 _suff_buff_size)
+{
+	size = _size;
+	lut = (uint64*)_lut_buff;
+	lut_buff_recs = _lut_buff_size / sizeof(uint64);
+	suff_buff = _suff_buff;
+	suff_buff_size = _suff_buff_size;
+	lut_offset = 0;
+
+	lut_prefix_len = _lut_prefix_len;
+	kmer_len = _kmer_len;
+	suffix_bytes = (kmer_len - lut_prefix_len) / 4;
+	file = _file;
+	n_kmers = _n_kmers;
+	name = _name;
+	counter_size = sizeof(uint32);
+
+	lut_size = (1 << lut_prefix_len * 2);
+
+	suff_rec_len = (kmer_len - lut_prefix_len) / 4 + counter_size;
+	left_to_read = suff_rec_len * n_kmers;
+	max_in_suff_buff = suff_buff_size / suff_rec_len;
+	lut_start_pos_in_file = n_kmers * suff_rec_len;
+	rewind(file);
+	read_next_lut_part();
+	cur_in_suff_buff = (uint32)fread(suff_buff, 1, MIN(max_in_suff_buff * suff_rec_len, left_to_read), file) / suff_rec_len;
+	left_to_read -= cur_in_suff_buff * suff_rec_len;
+	current_prefix = 0;
+	in_current_prefix = 0;
+	suff_buff_pos = 0;
+}
+
+#endif
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmer_counter/bkb_uncompactor.h'
--- old/kmer_counter/bkb_uncompactor.h	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/bkb_uncompactor.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,653 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _BKB_UNCOMPACTOR_H
+#define _BKB_UNCOMPACTOR_H
+
+#include "params.h"
+#include "kmer.h"
+#include "rev_byte.h"
+
+
+//************************************************************************************************************
+// CBigKmerBinUncompactor - Unpacking super k-mers to k+x-mers, only in strict memory mode
+//************************************************************************************************************
+template<unsigned SIZE>
+class CBigKmerBinUncompactor
+{
+	CBigBinPartQueue* bbpq;
+	CBigBinKXmersQueue* bbkq;
+	CMemoryPool *sm_pmm_expand;
+	uint32 max_x;
+	bool both_strands;
+	uint32 kmer_len;
+
+	CKmer<SIZE>* kxmers;
+	int64 sm_mem_part_expand;
+	uint32 kxmers_size;
+	int32 bin_id;
+
+	uchar* input_data;
+	uint64 input_data_size;
+
+	void GetNextSymb(uchar& symb, uchar& byte_shift, uint64& pos, uchar* data_p);
+	void Uncompact();
+	void ExpandKxmersBoth();
+	void ExpandKxmersAll();
+	void ExpandKmersBoth();
+	void ExpandKmersAll();
+
+	public:
+	CBigKmerBinUncompactor(CKMCParams& Params, CKMCQueues& Queues);
+	~CBigKmerBinUncompactor();
+	void Uncompact(int32 _bin_id, uchar* _data, uint64 _size);
+	
+};
+
+//************************************************************************************************************
+// CBigKmerBinUncompactor
+//************************************************************************************************************
+
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> CBigKmerBinUncompactor<SIZE>::CBigKmerBinUncompactor(CKMCParams& Params, CKMCQueues& Queues)
+{	
+	sm_pmm_expand = Queues.sm_pmm_expand;
+	bbpq = Queues.bbpq;
+	bbkq = Queues.bbkq;
+	kmer_len = Params.kmer_len;
+	max_x = Params.max_x;
+	both_strands = Params.both_strands;
+	sm_mem_part_expand = Params.sm_mem_part_expand;
+	kxmers_size = (uint32)(sm_mem_part_expand / sizeof(CKmer<SIZE>)); 
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinUncompactor<SIZE>::Uncompact(int32 _bin_id, uchar* _data, uint64 _size)
+{
+	bin_id = _bin_id;
+	input_data = _data;
+	input_data_size = _size;
+	Uncompact();
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> CBigKmerBinUncompactor<SIZE>::~CBigKmerBinUncompactor()
+{
+
+}
+
+
+//----------------------------------------------------------------------------------
+template <unsigned SIZE> inline void CBigKmerBinUncompactor<SIZE>::GetNextSymb(uchar& symb, uchar& byte_shift, uint64& pos, uchar* data_p)
+{
+	symb = (data_p[pos] >> byte_shift) & 3;
+	if (byte_shift == 0)
+	{
+		++pos;
+		byte_shift = 6;
+	}
+	else
+		byte_shift -= 2;
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinUncompactor<SIZE>::ExpandKxmersBoth()
+{
+	uchar* _raw_buffer;
+	sm_pmm_expand->reserve(_raw_buffer);
+	kxmers = (CKmer<SIZE>*)_raw_buffer;
+
+	CKmer<SIZE> kmer, rev_kmer, kmer_mask;
+	CKmer<SIZE> kxmer_mask;
+	bool kmer_lower;
+	uint32 x, additional_symbols;
+	uchar symb;
+	uint32 kmer_bytes = (kmer_len + 3) / 4;
+	uint32 rev_shift = kmer_len * 2 - 2;
+	uchar* data_p = input_data;
+	kmer_mask.set_n_1(kmer_len * 2);
+	uint32 kmer_shr = SIZE * 32 - kmer_len;
+
+	kxmer_mask.set_n_1((kmer_len + max_x + 1) * 2);
+
+	uint64 kxmers_pos = 0;
+	uint64 pos = 0;
+	while (pos < input_data_size)
+	{
+		kmer.clear();
+		rev_kmer.clear();
+		additional_symbols = data_p[pos++];
+
+		//build kmer
+		for (uint32 i = 0, kmer_pos = 8 * SIZE - 1, kmer_rev_pos = 0; i < kmer_bytes; ++i, --kmer_pos, ++kmer_rev_pos)
+		{
+			kmer.set_byte(kmer_pos, data_p[pos + i]);
+			rev_kmer.set_byte(kmer_rev_pos, CRev_byte::lut[data_p[pos + i]]);
+		}
+		pos += kmer_bytes;
+
+		uchar byte_shift = 6 - (kmer_len % 4) * 2;
+		if (byte_shift != 6)
+			--pos;
+
+		if (kmer_shr)
+			kmer.SHR(kmer_shr);
+
+		kmer.mask(kmer_mask);
+		rev_kmer.mask(kmer_mask);
+
+		kmer_lower = kmer < rev_kmer;
+		x = 0;
+
+		if (kmer_lower)
+			kxmers[kxmers_pos].set(kmer);
+		else
+			kxmers[kxmers_pos].set(rev_kmer);
+
+		uint32 symbols_left = additional_symbols;
+		while (symbols_left)
+		{
+			GetNextSymb(symb, byte_shift, pos, data_p);
+			kmer.SHL_insert_2bits(symb);
+			kmer.mask(kmer_mask);
+			rev_kmer.SHR_insert_2bits(3 - symb, rev_shift);
+			--symbols_left;
+
+			if (kmer_lower)
+			{
+				if (kmer < rev_kmer)
+				{
+					kxmers[kxmers_pos].SHL_insert_2bits(symb);
+					++x;
+					if (x == max_x)
+					{
+						if(!symbols_left)
+							break;
+
+						kxmers[kxmers_pos++].set_2bits(x, kmer_len * 2 + max_x * 2);
+						if (kxmers_pos >= kxmers_size)
+						{
+							bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+							kxmers_pos = 0;
+							sm_pmm_expand->reserve(_raw_buffer);
+							kxmers = (CKmer<SIZE>*)_raw_buffer;
+						}
+						x = 0;
+
+						GetNextSymb(symb, byte_shift, pos, data_p);
+						kmer.SHL_insert_2bits(symb);
+						kmer.mask(kmer_mask);
+						rev_kmer.SHR_insert_2bits(3 - symb, rev_shift);
+						--symbols_left;
+
+						kmer_lower = kmer < rev_kmer;
+						if (kmer_lower)
+							kxmers[kxmers_pos].set(kmer);
+						else
+							kxmers[kxmers_pos].set(rev_kmer);
+					}
+				}
+				else
+				{
+					kxmers[kxmers_pos++].set_2bits(x, kmer_len * 2 + max_x * 2);
+					if (kxmers_pos >= kxmers_size)
+					{
+						bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+						kxmers_pos = 0;
+						sm_pmm_expand->reserve(_raw_buffer);
+						kxmers = (CKmer<SIZE>*)_raw_buffer;
+					}
+					x = 0;
+
+					kmer_lower = false;
+					kxmers[kxmers_pos].set(rev_kmer);
+				}
+			}
+			else
+			{
+				if (!(kmer < rev_kmer))
+				{
+					kxmers[kxmers_pos].set_2bits(3 - symb, kmer_len * 2 + x * 2);
+					++x;
+					if (x == max_x)
+					{
+						if(!symbols_left)
+							break;
+
+						kxmers[kxmers_pos++].set_2bits(x, kmer_len * 2 + max_x * 2);
+						if (kxmers_pos >= kxmers_size)
+						{
+							bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+							kxmers_pos = 0;
+							sm_pmm_expand->reserve(_raw_buffer);
+							kxmers = (CKmer<SIZE>*)_raw_buffer;
+						}
+						x = 0;
+
+						GetNextSymb(symb, byte_shift, pos, data_p);
+						kmer.SHL_insert_2bits(symb);
+						kmer.mask(kmer_mask);
+						rev_kmer.SHR_insert_2bits(3 - symb, rev_shift);
+						--symbols_left;
+
+						kmer_lower = kmer < rev_kmer;
+
+						if (kmer_lower)
+							kxmers[kxmers_pos].set(kmer);
+						else
+							kxmers[kxmers_pos].set(rev_kmer);
+					}
+				}
+				else
+				{
+					kxmers[kxmers_pos++].set_2bits(x, kmer_len * 2 + max_x * 2);
+					if (kxmers_pos >= kxmers_size)
+					{
+						bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+						kxmers_pos = 0;
+						sm_pmm_expand->reserve(_raw_buffer);
+						kxmers = (CKmer<SIZE>*)_raw_buffer;
+					}
+					x = 0;
+					
+					kxmers[kxmers_pos].set(kmer);
+					kmer_lower = true;
+				}
+			}
+
+		}
+		kxmers[kxmers_pos++].set_2bits(x, kmer_len * 2 + max_x * 2);
+		if (kxmers_pos >= kxmers_size)
+		{
+			bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+			kxmers_pos = 0;
+			sm_pmm_expand->reserve(_raw_buffer);
+			kxmers = (CKmer<SIZE>*)_raw_buffer;
+		}
+		if (byte_shift != 6)
+			++pos;
+	}
+
+	if (kxmers_pos)
+	{
+		bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+	}
+	else
+	{
+		sm_pmm_expand->free(_raw_buffer);
+	}
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinUncompactor<SIZE>::ExpandKxmersAll()
+{
+	uchar* _raw_buffer;
+	sm_pmm_expand->reserve(_raw_buffer);
+	kxmers = (CKmer<SIZE>*)_raw_buffer;
+
+	uint64 pos = 0;
+	CKmer<SIZE> kmer_mask, kxmer, kxmer_mask;
+	kxmer_mask.set_n_1((kmer_len + max_x) * 2);
+	uchar *data_p = input_data;
+
+	kmer_mask.set_n_1(kmer_len * 2);
+	uint64 kxmers_pos = 0;
+
+	while (pos < input_data_size)
+	{		
+		kxmer.clear();
+		uint32 additional_symbols = data_p[pos++];
+		uchar symb;
+
+		uint32 kmer_bytes = (kmer_len + 3) / 4;
+
+		//building kmer
+		for (uint32 i = 0, kmer_pos = 8 * SIZE - 1; i < kmer_bytes; ++i, --kmer_pos)
+		{
+			kxmer.set_byte(kmer_pos, data_p[pos + i]);
+		}
+
+		pos += kmer_bytes;
+		uchar byte_shift = 6 - (kmer_len % 4) * 2;
+		if (byte_shift != 6)
+			--pos;
+
+		uint32 kmer_shr = SIZE * 32 - kmer_len;
+
+		if (kmer_shr)
+			kxmer.SHR(kmer_shr);
+
+		kxmer.mask(kmer_mask);
+
+		uint32 tmp = MIN(max_x, additional_symbols);
+
+		for (uint32 i = 0; i < tmp; ++i)
+		{
+			GetNextSymb(symb, byte_shift, pos, data_p);
+			kxmer.SHL_insert_2bits(symb);
+		}
+		kxmer.set_2bits(tmp, (kmer_len + max_x) * 2);
+
+		kxmers[kxmers_pos++].set(kxmer);
+		if (kxmers_pos >= kxmers_size)
+		{
+			bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+			kxmers_pos = 0;
+			sm_pmm_expand->reserve(_raw_buffer);
+			kxmers = (CKmer<SIZE>*)_raw_buffer;
+		}
+		additional_symbols -= tmp;
+
+		uint32 kxmers_count = additional_symbols / (max_x + 1);
+		uint32 kxmer_rest = additional_symbols % (max_x + 1);
+
+		for (uint32 j = 0; j < kxmers_count; ++j)
+		{
+			for (uint32 i = 0; i < max_x + 1; ++i)
+			{
+				GetNextSymb(symb, byte_shift, pos, data_p);
+				kxmer.SHL_insert_2bits(symb);
+			}
+			kxmer.mask(kxmer_mask);
+
+			kxmer.set_2bits(max_x, (kmer_len + max_x) * 2);
+
+			kxmers[kxmers_pos++].set(kxmer);
+			if (kxmers_pos >= kxmers_size)
+			{
+				bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+				kxmers_pos = 0;
+				sm_pmm_expand->reserve(_raw_buffer);
+				kxmers = (CKmer<SIZE>*)_raw_buffer;
+			}
+		}
+		if (kxmer_rest)
+		{
+			uint32 i = 0;
+			GetNextSymb(symb, byte_shift, pos, data_p);
+			kxmer.SHL_insert_2bits(symb);
+			kxmer.mask(kmer_mask);
+			--kxmer_rest;
+			for (; i < kxmer_rest; ++i)
+			{
+				GetNextSymb(symb, byte_shift, pos, data_p);
+				kxmer.SHL_insert_2bits(symb);
+			}
+
+			kxmer.set_2bits(kxmer_rest, (kmer_len + max_x) * 2);
+
+			kxmers[kxmers_pos++].set(kxmer);
+			if (kxmers_pos >= kxmers_size)
+			{
+				
+				bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+				kxmers_pos = 0;
+				sm_pmm_expand->reserve(_raw_buffer);
+				kxmers = (CKmer<SIZE>*)_raw_buffer;
+			}
+		}
+		if (byte_shift != 6)
+			++pos;
+	}
+	if (kxmers_pos)
+	{
+		bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+	}
+	else
+	{
+		sm_pmm_expand->free(_raw_buffer);
+	}
+
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinUncompactor<SIZE>::ExpandKmersBoth()
+{
+	uchar* _raw_buffer;
+	sm_pmm_expand->reserve(_raw_buffer);
+	kxmers = (CKmer<SIZE>*)_raw_buffer;
+
+	CKmer<SIZE> kmer, rev_kmer, kmer_can, kmer_mask;
+
+	uint32 kmer_bytes = (kmer_len + 3) / 4;
+	uint32 kmer_len_shift = (kmer_len - 1) * 2;
+	kmer_mask.set_n_1(kmer_len * 2);
+	uchar *data_p = input_data;
+
+	uint64 kxmers_pos = 0;
+	uint64 pos = 0;
+
+
+	while (pos < input_data_size)
+	{
+		kmer.clear();
+		rev_kmer.clear();
+		uint32 additional_symbols = data_p[pos++];
+		uchar symb;
+
+		//building kmer
+		for (uint32 i = 0, kmer_pos = 8 * SIZE - 1, kmer_rev_pos = 0; i < kmer_bytes; ++i, --kmer_pos, ++kmer_rev_pos)
+		{
+			kmer.set_byte(kmer_pos, data_p[pos + i]);
+			rev_kmer.set_byte(kmer_rev_pos, CRev_byte::lut[data_p[pos + i]]);
+		}
+		pos += kmer_bytes;
+		uchar byte_shift = 6 - (kmer_len % 4) * 2;
+		if (byte_shift != 6)
+			--pos;
+
+		uint32 kmer_shr = SIZE * 32 - kmer_len;
+
+		if (kmer_shr)
+			kmer.SHR(kmer_shr);
+
+		kmer.mask(kmer_mask);
+		rev_kmer.mask(kmer_mask);
+
+		kmer_can = kmer < rev_kmer ? kmer : rev_kmer;
+		kxmers[kxmers_pos++].set(kmer_can);
+		if (kxmers_pos >= kxmers_size)
+		{
+			bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+			kxmers_pos = 0;
+			sm_pmm_expand->reserve(_raw_buffer);
+			kxmers = (CKmer<SIZE>*)_raw_buffer;
+		}
+
+		for (uint32 i = 0; i < additional_symbols; ++i)
+		{
+			symb = (data_p[pos] >> byte_shift) & 3;
+			if (byte_shift == 0)
+			{
+				++pos;
+				byte_shift = 6;
+			}
+			else
+				byte_shift -= 2;
+			kmer.SHL_insert_2bits(symb);
+			kmer.mask(kmer_mask);
+			rev_kmer.SHR_insert_2bits(3 - symb, kmer_len_shift);
+			kmer_can = kmer < rev_kmer ? kmer : rev_kmer;
+			kxmers[kxmers_pos++].set(kmer_can);
+			if (kxmers_pos >= kxmers_size)
+			{
+				bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+				kxmers_pos = 0;
+				sm_pmm_expand->reserve(_raw_buffer);
+				kxmers = (CKmer<SIZE>*)_raw_buffer;
+			}
+		}
+		if (byte_shift != 6)
+			++pos;
+	}
+	if (kxmers_pos)
+	{
+		bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+	}
+	else
+	{
+		sm_pmm_expand->free(_raw_buffer);
+	}
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinUncompactor<SIZE>::ExpandKmersAll()
+{	
+	uchar* _raw_buffer;
+	sm_pmm_expand->reserve(_raw_buffer);
+	kxmers = (CKmer<SIZE>*)_raw_buffer;
+
+	uint64 kxmers_pos = 0;
+	uint64 pos = 0;
+	CKmer<SIZE> kmer;
+	uint32 kmer_bytes = (kmer_len + 3) / 4;
+
+	CKmer<SIZE> kmer_mask;
+	kmer_mask.set_n_1(kmer_len * 2);
+	uchar *data_p = input_data;
+
+	while (pos < input_data_size)
+	{
+		kmer.clear();
+		uint32 additional_symbols = data_p[pos++];		
+		for (uint32 i = 0, kmer_pos = 8 * SIZE - 1; i < kmer_bytes; ++i, --kmer_pos)
+		{
+			kmer.set_byte(kmer_pos, data_p[pos + i]);
+		}
+		pos += kmer_bytes;
+		uchar byte_shift = 6 - (kmer_len % 4) * 2;
+		if (byte_shift != 6)
+			--pos;
+
+		uint32 kmer_shr = SIZE * 32 - kmer_len;
+
+		if (kmer_shr)
+			kmer.SHR(kmer_shr);
+
+		kmer.mask(kmer_mask);
+		kxmers[kxmers_pos++].set(kmer);
+		if (kxmers_pos >= kxmers_size)
+		{
+			bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+			kxmers_pos = 0;
+			sm_pmm_expand->reserve(_raw_buffer);
+			kxmers = (CKmer<SIZE>*)_raw_buffer;
+		}
+		for (uint32 i = 0; i < additional_symbols; ++i)
+		{
+			uchar symb = (data_p[pos] >> byte_shift) & 3;
+			if (byte_shift == 0)
+			{
+				++pos;
+				byte_shift = 6;
+			}
+			else
+				byte_shift -= 2;
+			kmer.SHL_insert_2bits(symb);
+			kmer.mask(kmer_mask);
+			kxmers[kxmers_pos++].set(kmer);
+			if (kxmers_pos >= kxmers_size)
+			{
+				bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+				kxmers_pos = 0;
+				sm_pmm_expand->reserve(_raw_buffer);
+				kxmers = (CKmer<SIZE>*)_raw_buffer;
+			}
+		}
+		if (byte_shift != 6)
+			++pos;
+	}
+
+	if (kxmers_pos)
+	{
+		bbkq->push(bin_id, (uchar*)kxmers, kxmers_pos);
+	}
+	else
+	{
+		sm_pmm_expand->free(_raw_buffer);
+	}
+}
+
+//----------------------------------------------------------------------------------
+template<unsigned SIZE> void CBigKmerBinUncompactor<SIZE>::Uncompact()
+{
+	if (max_x)
+	{
+		if (both_strands)
+			ExpandKxmersBoth();
+		else
+			ExpandKxmersAll();
+	}
+	else
+	{
+		if (both_strands)
+			ExpandKmersBoth();
+		else
+			ExpandKmersAll();
+	}
+}
+
+
+//************************************************************************************************************
+// CWBigKmerBinUncompactor - wrapper for multithreading purposes
+//************************************************************************************************************
+template<unsigned SIZE>
+class CWBigKmerBinUncompactor
+{
+	CBigKmerBinUncompactor<SIZE>* bkb_uncompactor;
+	CBigBinPartQueue* bbpq;
+	CBigBinKXmersQueue* bbkq;
+	CMemoryPool* sm_pmm_input_file;
+public:
+	CWBigKmerBinUncompactor(CKMCParams& Params, CKMCQueues& Queues);
+	~CWBigKmerBinUncompactor();
+	void operator()();
+};
+
+//----------------------------------------------------------------------------------
+// Constructor
+template<unsigned SIZE>
+CWBigKmerBinUncompactor<SIZE>::CWBigKmerBinUncompactor(CKMCParams& Params, CKMCQueues& Queues)
+{
+	bkb_uncompactor = new CBigKmerBinUncompactor<SIZE>(Params, Queues);
+	bbpq = Queues.bbpq;
+	bbkq = Queues.bbkq;
+	sm_pmm_input_file = Queues.sm_pmm_input_file;
+}
+
+//----------------------------------------------------------------------------------
+// Destructor
+template<unsigned SIZE>
+CWBigKmerBinUncompactor<SIZE>::~CWBigKmerBinUncompactor()
+{
+	delete bkb_uncompactor;
+}
+
+//----------------------------------------------------------------------------------
+// Execution
+template<unsigned SIZE>
+void CWBigKmerBinUncompactor<SIZE>::operator()()
+{
+	int32 bin_id;
+	uchar* data;
+	uint64 size;
+	while (bbpq->pop(bin_id, data, size))
+	{
+		bkb_uncompactor->Uncompact(bin_id, data, size);		
+		sm_pmm_input_file->free(data);
+	}
+	bbkq->mark_completed();
+}
+
+
+#endif 
+
+// ***** EOF
\ No newline at end of file

=== added file 'kmer_counter/bkb_writer.cpp'
--- old/kmer_counter/bkb_writer.cpp	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/bkb_writer.cpp	2020-12-10 18:04:42 +0000
@@ -0,0 +1,142 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#include "stdafx.h"
+#include "bkb_writer.h"
+
+//************************************************************************************************************
+// CBigKmerBinWriter
+//************************************************************************************************************
+
+//----------------------------------------------------------------------------------
+CBigKmerBinWriter::CBigKmerBinWriter(CKMCParams& Params, CKMCQueues& Queues)
+{
+	disk_logger = Queues.disk_logger;
+	bbspq = Queues.bbspq;
+	sm_pmm_sorter_suffixes = Queues.sm_pmm_sorter_suffixes;
+	sm_pmm_sorter_lut = Queues.sm_pmm_sorter_lut;
+	working_directory = Params.working_directory;
+	bbd = Queues.bbd;
+	sm_cbc = Queues.sm_cbc;
+}
+
+//----------------------------------------------------------------------------------
+void CBigKmerBinWriter::Process()
+{	
+	int32 curr_bin_id = -1;
+	uchar* suff_buff = nullptr;
+	uint64 suff_buff_size;
+	uint64* lut = nullptr;
+	uint64 lut_size = 0;
+	bool last_one_in_sub_bin;
+	bool first_in_sub_bin = true;
+	FILE* file = nullptr;
+	string name;
+	uint64 file_size = 0;
+	while (bbspq->pop(bin_id, sub_bin_id, suff_buff, suff_buff_size, lut, lut_size, last_one_in_sub_bin))
+	{
+		if (curr_bin_id != bin_id)
+		{
+			if (curr_bin_id != -1)			
+				sm_cbc->push(curr_bin_id);			
+			curr_bin_id = bin_id;
+		}
+		if (first_in_sub_bin)
+		{
+			file_size = 0;			
+			name = GetName();
+			file = fopen(name.c_str(), "wb+");
+			if (!file)
+			{
+				cerr << "Error: can not open file : " << name;
+				exit(1);
+			}
+			setbuf(file, nullptr);
+		}
+		first_in_sub_bin = false;
+
+		if (suff_buff_size)
+		{
+			disk_logger->log_write(suff_buff_size);
+			if (fwrite(suff_buff, 1, suff_buff_size, file) != suff_buff_size)
+			{
+				cerr << "Error while writing to file : " << name;
+				exit(1);
+			}
+			file_size += suff_buff_size;
+			sm_pmm_sorter_suffixes->free(suff_buff);
+		}
+
+		if (lut_size)
+		{
+			disk_logger->log_write(lut_size * sizeof(uint64));
+			if (fwrite(lut, sizeof(uint64), lut_size, file) != lut_size)
+			{
+				cerr << "Error while writing to file : " << name;
+				exit(1);
+			}
+			file_size += lut_size * sizeof(uint64);
+			sm_pmm_sorter_lut->free((uchar*)lut);
+		}
+		
+		if (last_one_in_sub_bin)
+		{			
+			bbd->push(bin_id, sub_bin_id, 0, 0, file, name, file_size);
+			first_in_sub_bin = true;
+		}
+	}
+	if(curr_bin_id != -1)
+		sm_cbc->push(curr_bin_id);
+	sm_cbc->mark_completed();
+}
+
+//----------------------------------------------------------------------------------
+string CBigKmerBinWriter::GetName()
+{	
+	string s_tmp = std::to_string(bin_id);
+	while (s_tmp.length() < 5)
+		s_tmp = string("0") + s_tmp;	
+	string s1 = std::to_string(sub_bin_id);
+	while (s1.length() < 3)
+		s1 = string("0") + s1;
+
+	if (*working_directory.rbegin() != '/' && *working_directory.rbegin() != '\\')
+		working_directory += "/";
+	return working_directory + "kmc_" + s_tmp + "_" + s1 + "_" + s1 + ".bin";
+}
+
+
+//************************************************************************************************************
+// CWBigKmerBinWriter - wrapper for multithreading purposes
+//************************************************************************************************************
+
+
+//----------------------------------------------------------------------------------
+// Constructor
+CWBigKmerBinWriter::CWBigKmerBinWriter(CKMCParams& Params, CKMCQueues& Queues)
+{
+	bkb_writer = new CBigKmerBinWriter(Params, Queues);
+}
+
+//----------------------------------------------------------------------------------
+// Destructor
+CWBigKmerBinWriter::~CWBigKmerBinWriter()
+{
+	delete bkb_writer;
+}
+
+//----------------------------------------------------------------------------------
+// Execution
+void CWBigKmerBinWriter::operator()()
+{
+	bkb_writer->Process();
+}
+
+// ***** EOF 
\ No newline at end of file

=== added file 'kmer_counter/bkb_writer.h'
--- old/kmer_counter/bkb_writer.h	1970-01-01 00:00:00 +0000
+++ new/kmer_counter/bkb_writer.h	2020-12-10 18:04:42 +0000
@@ -0,0 +1,52 @@
+/*
+  This file is a part of KMC software distributed under GNU GPL 3 licence.
+  The homepage of the KMC project is http://sun.aei.polsl.pl/kmc
+  
+  Authors: Sebastian Deorowicz, Agnieszka Debudaj-Grabysz, Marek Kokot
+  
+  Version: 3.1.1
+  Date   : 2019-05-19
+*/
+
+#ifndef _BKB_WRITER_H
+#define _BKB_WRITER_H
+
+#include "params.h"
+
+//************************************************************************************************************
+// CBigKmerBinWriter - Write sub bins to  HDD
+//************************************************************************************************************
+class CBigKmerBinWriter
+{
+	int32 bin_id, sub_bin_id;
+	CBigBinSortedPartQueue* bbspq;
+	CCompletedBinsCollector* sm_cbc;
+	CDiskLogger* disk_logger;
+	CMemoryPool * sm_pmm_sorter_suffixes;
+	CMemoryPool * sm_pmm_sorter_lut;
+	
+	string working_directory;
+	CBigBinDesc* bbd;
+	string GetName();
+public:
+	CBigKmerBinWriter(CKMCParams& Params, CKMCQueues& Queues);
+	void Process();	
+};
+
+//************************************************************************************************************
+// CWBigKmerBinWriter - wrapper for multithreading purposes
+//********************************************************************************************************