[med-svn] [Git][med-team/kissplice][master] 7 commits: New upstream version 2.6.1

Andreas Tille (@tille) gitlab at salsa.debian.org
Thu Jun 30 15:45:12 BST 2022



Andreas Tille pushed to branch master at Debian Med / kissplice


Commits:
f824c786 by Andreas Tille at 2022-04-06T12:36:39+02:00
New upstream version 2.6.1
- - - - -
d801d0ad by Andreas Tille at 2022-06-30T16:12:12+02:00
New upstream version 2.6.2
- - - - -
f0b6bdd8 by Andreas Tille at 2022-06-30T16:12:12+02:00
routine-update: New upstream version

- - - - -
b1a398e5 by Andreas Tille at 2022-06-30T16:12:30+02:00
Update upstream source from tag 'upstream/2.6.2'

Update to upstream version '2.6.2'
with Debian dir 311a418d9ffae78defecc0657f7106e11a534613
- - - - -
5b73eaca by Andreas Tille at 2022-06-30T16:12:30+02:00
routine-update: Standards-Version: 4.6.1

- - - - -
5ad99901 by Andreas Tille at 2022-06-30T16:27:11+02:00
Refresh patch

- - - - -
9a0a1a18 by Andreas Tille at 2022-06-30T16:28:08+02:00
Upload to unstable

- - - - -


24 changed files:

- CMakeLists.txt
- ChangeLog
- README.md
- debian/changelog
- debian/control
- debian/patches/tests-2to3.patch
- doc/CMakeLists.txt
- doc/quiet.sh
- + doc/rm_pdf_id.sh
- doc/user_guide.in.tex
- kissplice.in.py
- man/CMakeLists.txt
- man/kissplice.in.1
- tests/integration_tests/CMakeLists.txt
- tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0
- tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0
- tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0
- tests/integration_tests/kisspliceDBGTest.py
- tests/integration_tests/kisspliceGraphAndSequenceTest.py
- tests/integration_tests/kisspliceTwoSequencesTest.py
- thirdparty/kissreads/include/coherence_algorithm.h
- thirdparty/kissreads/src/coherent_algorithm.cpp
- thirdparty/kissreads/src/extension_algorithm.cpp
- − utils/cmake/cmake_uninstall.cmake.in


Changes:

=====================================
CMakeLists.txt
=====================================
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.1)
 
 project(
   kissplice
-  VERSION 2.6.1 # Definition which is propagated through PROJECT_VERSION
+  VERSION 2.6.2 # Definition which is propagated through PROJECT_VERSION
   LANGUAGES CXX
 )
 
@@ -23,6 +23,9 @@ add_compile_options(-Wall)
 # Profiling with gprof requires -pg which is better to add manually with -DCMAKE_(C|CXX)_FLAGS=-pg
 # `perf` is a more modern replacement anyway and does not require special compilation.
 
+# Date for manpage and pdf doc
+string(TIMESTAMP CONFIGURE_DATE "%Y-%m-%d" UTC)
+
 ###############################################################################
 # structure and paths
 # kissplice is composed of:
@@ -97,13 +100,3 @@ add_subdirectory(doc) # user guide
 enable_testing()
 add_subdirectory(tests)
 
-# ============================================================================
-# Add custom uninstall target
-# ============================================================================
-configure_file(
-    "${CMAKE_CURRENT_SOURCE_DIR}/utils/cmake/cmake_uninstall.cmake.in"
-    "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
-    IMMEDIATE @ONLY)
-
-add_custom_target(uninstall
-    COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)


=====================================
ChangeLog
=====================================
@@ -1,3 +1,7 @@
+UNRELEASED
+	Improvements:
+		- manpage and user_guide.pdf build process should now be reproducible
+
 2022-04-05 Francois Gindraud <francois.gindraud at inria.fr>
 	* 2.6.0 -> 2.6.1
 	Bug fix:


=====================================
README.md
=====================================
@@ -4,14 +4,19 @@ Main page: http://kissplice.prabi.fr/
 
 [User guide](https://gitlab.inria.fr/erable/kissplice/-/jobs/artifacts/master/file/prefix/share/doc/kissplice/user_guide.pdf?job=check_install_and_user_guide) from latest build.
 
-# Docker
+# Pre-packaged versions
 
+## Docker
 You can find the latest version of KisSplice, KisSplice2RefGenome and KissDE [on Docker Hub](https://hub.docker.com/repository/docker/dwishsan/kissplice-pipeline).
 
 We also propose a [stand-alone Docker image for the KissDE Shiny App](https://hub.docker.com/repository/docker/dwishsan/kde-shiny) for KisSplice results exploration.
 
-# Building:
+## Linux
+A package for kissplice is available on Debian / Ubuntu, but it may lag the current version.
 
+# Build from source
+
+## Dependencies
 Required:
 - cmake >= 3.1
 - C/C++11 compiler toolchain (binutils, gcc or clang)
@@ -21,9 +26,12 @@ Recommended but optional:
 - [bcalm](https://github.com/GATB/bcalm) >= v2.2.2 ; as a fallback a locally compiled version of bcalm can be used by passing `-DUSE_BUNDLED_BCALM=TRUE` to cmake. A debian/ubuntu package is available. 
 - latex toolchain with standard packages to build the user_guide ; this can be disabled by passing `-DUSER_GUIDE=OFF` to cmake
 
+## Build
+Download from the latest [release](https://gitlab.inria.fr/erable/kissplice/-/releases) and uncompress it.
+You can also clone the git repository, but the latest code may be broken..
+Then compile using :
 ```
-git clone https://gitlab.inria.fr/erable/kissplice
-cd kissplice/
+cd kissplice/ # Replace by the directory where the release tar.gz was uncompressed, or where the git was cloned
 mkdir build
 cd build
 cmake -DCMAKE_BUILD_TYPE=Release ..
@@ -39,7 +47,7 @@ make
 make install
 ```
 
-# Running on the sample test:
+# Running on the sample test
 ```
 cd kissplice/build
 ./bin/kissplice -r ../sample_example/mock1.fq -r ../sample_example/mock2.fq -r ../sample_example/virus1.fq -r ../sample_example/virus2.fq


=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+kissplice (2.6.2-1) unstable; urgency=medium
+
+  * New upstream version
+  * Standards-Version: 4.6.1 (routine-update)
+
+ -- Andreas Tille <tille at debian.org>  Thu, 30 Jun 2022 16:27:19 +0200
+
 kissplice (2.6.1-1) unstable; urgency=medium
 
   [ Andreas Tille ]


=====================================
debian/control
=====================================
@@ -6,13 +6,13 @@ Section: science
 Priority: optional
 Build-Depends: debhelper-compat (= 13),
                dh-python,
-               cmake (>= 3.1),
+               cmake,
                python3-dev,
                libpython3-stdlib,
                zlib1g-dev,
                texlive-latex-recommended,
-               bcalm (>= 2.2.2) <!nocheck>
-Standards-Version: 4.6.0
+               bcalm <!nocheck>
+Standards-Version: 4.6.1
 Vcs-Browser: https://salsa.debian.org/med-team/kissplice
 Vcs-Git: https://salsa.debian.org/med-team/kissplice.git
 Homepage: http://kissplice.prabi.fr/
@@ -23,7 +23,7 @@ Architecture: any-amd64 any-arm64 any-mips64 any-mips64el any-ia64 any-ppc64 any
 Depends: ${shlibs:Depends},
          ${misc:Depends},
          ${python3:Depends},
-         bcalm (>= 2.2.2)
+         bcalm
 Description: Detection of various kinds of polymorphisms in RNA-seq data
  KisSplice is a piece of software that enables the analysis of RNA-seq data
  with or without a reference genome. It is an exact local transcriptome


=====================================
debian/patches/tests-2to3.patch
=====================================
@@ -4,30 +4,24 @@ Last-Update: Sun, 12 Apr 2020 18:40:00 +0200
 Forwarded: in https://bugs.debian.org/936798 to
            LACROIX VINCENT <Vincent.Lacroix at univ-lyon1.fr>, David Parsons <david.parsons at inria.fr>
 
-Index: kissplice/tests/functional_tests/ProcessLauncher.py
-===================================================================
---- kissplice.orig/tests/functional_tests/ProcessLauncher.py
-+++ kissplice/tests/functional_tests/ProcessLauncher.py
+--- a/tests/functional_tests/ProcessLauncher.py
++++ b/tests/functional_tests/ProcessLauncher.py
 @@ -1,4 +1,4 @@
 -#!/usr/bin/env python3
 +#!/usr/bin/python3
  from sys import exit
  import shutil
  import shlex
-Index: kissplice/tests/functional_tests/ksBubbleEnumerationTest.py
-===================================================================
---- kissplice.orig/tests/functional_tests/ksBubbleEnumerationTest.py
-+++ kissplice/tests/functional_tests/ksBubbleEnumerationTest.py
+--- a/tests/functional_tests/ksBubbleEnumerationTest.py
++++ b/tests/functional_tests/ksBubbleEnumerationTest.py
 @@ -1,4 +1,4 @@
 -#!/usr/bin/env python3
 +#!/usr/bin/python3
  import re
  import os
  import fnmatch
-Index: kissplice/tests/functional_tests/ksErrorRemovalTest.py
-===================================================================
---- kissplice.orig/tests/functional_tests/ksErrorRemovalTest.py
-+++ kissplice/tests/functional_tests/ksErrorRemovalTest.py
+--- a/tests/functional_tests/ksErrorRemovalTest.py
++++ b/tests/functional_tests/ksErrorRemovalTest.py
 @@ -1,4 +1,4 @@
 -#!/usr/bin/env python3
 +#!/usr/bin/python3
@@ -41,20 +35,16 @@ Index: kissplice/tests/functional_tests/ksErrorRemovalTest.py
 -    print("ks_error_removal: test FAILED")
 \ No newline at end of file
 +    print("ks_error_removal: test FAILED")
-Index: kissplice/tests/functional_tests/ksRunModulesTest.py
-===================================================================
---- kissplice.orig/tests/functional_tests/ksRunModulesTest.py
-+++ kissplice/tests/functional_tests/ksRunModulesTest.py
+--- a/tests/functional_tests/ksRunModulesTest.py
++++ b/tests/functional_tests/ksRunModulesTest.py
 @@ -1,4 +1,4 @@
 -#!/usr/bin/env python3
 +#!/usr/bin/python3
  import re
  import fnmatch
  import os
-Index: kissplice/tests/integration_tests/ProcessLauncher.py
-===================================================================
---- kissplice.orig/tests/integration_tests/ProcessLauncher.py
-+++ kissplice/tests/integration_tests/ProcessLauncher.py
+--- a/tests/integration_tests/ProcessLauncher.py
++++ b/tests/integration_tests/ProcessLauncher.py
 @@ -1,4 +1,4 @@
 -#!/usr/bin/env python3
 +#!/usr/bin/python3
@@ -68,47 +58,24 @@ Index: kissplice/tests/integration_tests/ProcessLauncher.py
 -        return res[0]
 \ No newline at end of file
 +        return res[0]
-Index: kissplice/tests/integration_tests/kisspliceDBGTest.py
-===================================================================
---- kissplice.orig/tests/integration_tests/kisspliceDBGTest.py
-+++ kissplice/tests/integration_tests/kisspliceDBGTest.py
+--- a/tests/integration_tests/kisspliceGraphAndSequenceTest.py
++++ b/tests/integration_tests/kisspliceGraphAndSequenceTest.py
 @@ -1,4 +1,4 @@
 -#!/usr/bin/env python3
 +#!/usr/bin/python3
  import re
  from sys import argv
  from os.path import dirname, abspath
-@@ -51,4 +51,4 @@ else:
-     print("kisspliceDBGTest.py: test FAILED")
-     
- command_line_rm = "rm -r "+TEST_INSTDIR +"/results"
--ProcessLauncher.run(command_line_rm)
-\ No newline at end of file
-+ProcessLauncher.run(command_line_rm)
-Index: kissplice/tests/integration_tests/kisspliceGraphAndSequenceTest.py
-===================================================================
---- kissplice.orig/tests/integration_tests/kisspliceGraphAndSequenceTest.py
-+++ kissplice/tests/integration_tests/kisspliceGraphAndSequenceTest.py
-@@ -1,4 +1,4 @@
--#!/usr/bin/env python3
-+#!/usr/bin/python3
- import re
- from sys import argv
- from os.path import dirname, abspath
-Index: kissplice/tests/integration_tests/kisspliceGraphTest.py
-===================================================================
---- kissplice.orig/tests/integration_tests/kisspliceGraphTest.py
-+++ kissplice/tests/integration_tests/kisspliceGraphTest.py
+--- a/tests/integration_tests/kisspliceGraphTest.py
++++ b/tests/integration_tests/kisspliceGraphTest.py
 @@ -1,4 +1,4 @@
 -#!/usr/bin/env python3
 +#!/usr/bin/python3
  import re
  from sys import argv
  from os.path import dirname, abspath
-Index: kissplice/tests/integration_tests/kisspliceTwoSequencesTest.py
-===================================================================
---- kissplice.orig/tests/integration_tests/kisspliceTwoSequencesTest.py
-+++ kissplice/tests/integration_tests/kisspliceTwoSequencesTest.py
+--- a/tests/integration_tests/kisspliceTwoSequencesTest.py
++++ b/tests/integration_tests/kisspliceTwoSequencesTest.py
 @@ -1,4 +1,4 @@
 -#!/usr/bin/env python3
 +#!/usr/bin/python3


=====================================
doc/CMakeLists.txt
=====================================
@@ -20,12 +20,13 @@ if (USER_GUIDE)
         OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/user_guide.pdf"
         COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/quiet.sh" "${PDFLATEX_COMPILER}" -halt-on-error -interaction=nonstopmode "-output-directory=${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/user_guide.tex"
         COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/quiet.sh" "${PDFLATEX_COMPILER}" -halt-on-error -interaction=nonstopmode "-output-directory=${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/user_guide.tex"
+        COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/rm_pdf_id.sh" "${CMAKE_CURRENT_BINARY_DIR}/user_guide.pdf"
         WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
         MAIN_DEPENDENCY "${CMAKE_CURRENT_BINARY_DIR}/user_guide.tex"
-        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/quiet.sh"
+        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/quiet.sh" "${CMAKE_CURRENT_SOURCE_DIR}/rm_pdf_id.sh"
     )
     add_custom_target(doc ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/user_guide.pdf")
     install(FILES "${CMAKE_CURRENT_BINARY_DIR}/user_guide.pdf" DESTINATION "${CMAKE_INSTALL_DOCDIR}")
 else()
     message(WARNING "user_guide.pdf will not be built, disabled by USER_GUIDE variable")
-endif()
\ No newline at end of file
+endif()


=====================================
doc/quiet.sh
=====================================
@@ -1,6 +1,6 @@
 #!/usr/bin/env bash
 
-# Execute a command while capturing all outputs if VERBOSE / VERBOSE_LATEX is not requested.
+# Execute a latex command while capturing all outputs if VERBOSE / VERBOSE_LATEX is not requested.
 # If the command does not succeed print its outputs for debugging.
 # This is intended to quiet latex verbosity in the nice case.
 # Compiling with `make VERBOSE_LATEX=1` is useful to debug latex compiling only (VERBOSE would print commands for c/c++ too).


=====================================
doc/rm_pdf_id.sh
=====================================
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+
+# Remove PDF ID metadata which breaks reproducible builds.
+# This field is not required.
+# Reference : https://wiki.debian.org/ReproducibleBuilds/TimestampsInPDFGeneratedByLaTeX
+for pdf in "$@"; do
+    grep -av '^/ID \[\(<[0-9A-F]\{32\}>\) \1]$' "${pdf}" > "${pdf}.no_id"
+    mv -f "${pdf}.no_id" "${pdf}"
+done


=====================================
doc/user_guide.in.tex
=====================================
@@ -34,6 +34,7 @@ De-novo calling alternative splicing events\\ from RNA-seq data\\
 User's guide, version @PROJECT_VERSION@
 \vspace{-1cm}
 }
+\date{@CONFIGURE_DATE@}
 
 \author{}
 \begin{document}


=====================================
kissplice.in.py
=====================================
@@ -72,6 +72,665 @@ logFileName = ""
 unfinished_bccs = []
 num_snps = {}
 
+############### NEW GLOBAL VALUES -> REDUNDANCE ###############
+# Maybe switch some of them to parameters?
+ENTROPYMAX=1.8 # If an upper path have less than this entropy, its cycle is deleted
+MAXLENGTHIDIOTIC=200 # Do idiotic strategie for upper paths of the same length and length >= this value
+MAXLENGTHALIGN=1000 # Do not try to align upper paths with a length >= this value (will miss some rare cases redundancy)
+BASEDIFFPATH_UP=2 # Base max difference of length between two upper path for them to be compared (try redundancy removal)
+BASEDIFFPATH_LOW=5 # Base max difference of length between two lower path for them to be compared (try redundancy removal)
+BASEMM_LOW=3 # Base max edit distance (levenshtein) allowed between 2 lower paths
+BASEMM_UP=3 # Base max edit distance (levenshtein) allowed between 2 upper paths
+WILDECARDS=["N","I"]
+# Levenshtein distance parameters
+EXT=1 # Malus for gap extension
+MATCH=0 # Malus for match 
+MMATCH=1 # Malus for mismatch
+GAP=1 # Malus for gap openning
+# Entropy
+WLEN=41 # Window length on sequence to compute entropy
+WSLIDE=41 # Right slide of the window on the sequence
+###############################################################
+
+
+############### NEW FUNCTIONS -> ENTROPY ###############
+# Natural log aproximation using ln(x)=lim(x->inf) n(x**(1/n)-1)
+def ln(x):
+    n = 10000.0 # Increase this number for more accurate estimation
+    return n * ((x ** (1/n)) - 1)
+
+LN2=ln(2) # For conversion between natural and base 2 log
+
+def entropyShannon(s):
+	n=0.0
+	dN={} # d[nucleotide]=occurence
+	for e in s:
+		n+=1
+		if e not in dN.keys():
+			dN[e]=0
+		dN[e]+=1
+
+	entShan=0
+	for base in dN.keys():
+		pN=dN[base]/n
+		entShan+=pN*(ln(pN)/LN2)
+	return entShan*-1
+
+def windowEntropy(s, wLen, wSlide):
+	# Compute Shannon entropy on a sequence s for each window of length wLen with a slide of length wSlide.
+	lE=[] # list of entropy
+	start=0 # start index
+	n=len(s)
+	for start in list(range(n))[::wSlide]:
+		end=start+wLen
+		if end >= n:
+			start=n-wLen
+			end=n-1
+			if start<0:
+				start=0
+		lE.append(entropyShannon(s[start:end]))
+	return lE
+##########################################################
+
+############### NEW FUNCTIONS -> REDUNDANCE ###############
+def idioticConsensus(s1,s2,maxEdit=3):
+		# Make consensus base per base (s1 and s2 have the same length)
+		cons=""
+		nN=0
+		#print(s1)
+		#print(s2)
+		for i in range(len(s1)):
+			if s1[i]==s2[i]:
+				cons+=s1[i]
+			else:
+				cons+="N"
+				if "N" not in [s1[i],s2[i]]:
+					nN+=1
+			i+=1
+			if not i%100 and nN>maxEdit:
+				#print(1)
+				#print(cons)
+				return [None,None]
+		if nN>maxEdit:
+			#print(2)
+			#print(cons)
+			return [None,None]
+		#print("OK")
+		#print(cons)
+		return [nN,cons]
+
+def consensus(s1,s2,mTB, trimN=True):
+	# 2 sequences and a traceback matrix
+	# Return one of the possible consensus, prioritazing match/mmatch, then T then L
+	# Create consensus sequence between 2 seq, adding "n" for indel and "N" for mismatches
+	# Will strip "N" of the consensus of trimN=True
+	nR=len(mTB)-1
+	nC=len(mTB[0])-1
+	cons=""
+	prev="D" # previous type of alignment
+	t=mTB[nR][nC]
+	while t!="E":
+		if prev in t: # If possible, continue the same type of alignment
+			do=prev
+		elif "D" in t: # else, priority to D
+			do="D"
+		elif "T" in t:
+			do="T"
+		else:
+			do="L"
+		if do=="D":
+			if s1[nC-1]==s2[nR-1]:
+				cons+=s1[nC-1]
+			elif s1[nC-1]=="I" or s2[nR-1]=="I":
+				cons+="I"
+			else:
+				cons+="N"
+			nR-=1
+			nC-=1
+		else:
+			cons+="I"
+			if do=="T":
+				nR-=1
+			else:
+				nC-=1
+		prev=do
+		t=mTB[nR][nC]
+	cons=cons[::-1]
+	if trimN:
+		cons=cons.strip("IN")
+	return cons
+
+
+def rc(s):
+    # reverse complement an ATGCN sequence
+	d={"A":"T","T":"A","G":"C","C":"G","N":"N","I":"I"}
+	return "".join([d[e] for e in s[::-1]])
+	
+def levDist(a,b, match=MATCH, mm=MMATCH, gap=GAP, ext=1, semiLoc=False, minRC=True, maxEdit=3, doConsensus=True, trimN=True):
+	# Levenshtein distance between two strings a and b of length n and m. Option for Semi-Local method (smallest sequence must fully aligng anywhere on the largest sequence)
+	# Semi-Local: To do this, no initialisation of the first row (insertion are cost-free before aligning the first base of the smallest sequence) and cost-free insertion on the last row (insertion are cost-free after aligning the last base of the smallest sequence)
+	# a is the largest sequence (switched if needed in the function)
+	# ext : gap extension cost
+	# minRC : also do reverse complement computation and take the minimum distance of the two
+	# maxEdit : stop process if the final score will be greater or equal to this number. -1 to keep everything.
+	# doConsensus : compute consensus sequence
+    # Maybe add a maximum indel length allowed, but this could be complicated...
+    # 'N' is considered as a wildcard
+	
+	cons=None
+	extD=ext
+	extI=ext
+	n=len(a)
+	m=len(b)
+	if n<m:
+		a,b=b,a
+		n,m=m,n
+	
+	# 1) Create an (n+1)x(m+1) matrix
+	mLev=[[None for x in range(n+1)] for y in range(m+1)]
+	mTB=[["O" for x in range(n+1)] for y in range(m+1)]# traceback to know if gap is extended or not. O by default, T for top, L for left (can be TL/LT)
+	
+	# 2) Intiliaze first column
+	mLev[0][0]=0
+	mTB[0][0]="E"
+	for r in range(1,m+1):
+		s=gap+(r-1)*ext
+		if maxEdit==-1 or s<maxEdit:
+			mLev[r][0]=s
+		mTB[r][0]="T"
+		# ... and the first row
+	for c in range(1,n+1):
+		mTB[0][c]="L"
+		if not semiLoc:
+			s=gap+(c-1)*ext
+			if maxEdit==-1 or s<maxEdit:
+				mLev[0][c]=s
+		else:
+			mLev[0][c]=0
+		
+	# 3) Fill the matrix
+	ins=gap
+	dele=gap
+	startC=1 # Starting column to compute score = last None surrounded by Nones + 1, for the first line of None
+	r=1
+	while r <= m:
+	#for r in range(1,m+1):
+		if semiLoc and r==m:
+			ins=0
+			extI=0
+		isFirstNone=True # indicate if this is the first line of None
+		c=startC
+		allNone=True
+		while c <= n:
+			T=mLev[r-1][c] # score if coming from T
+			L=mLev[r][c-1] # score if coming from L
+			D=mLev[r-1][c-1] # score if coming from D
+			if [T,L,D]==[None,None,None]: # Then we can add None and go to next cell or line
+				if isFirstNone: # First line of None : we will start the next line on the last column surrounded by None
+					startC=c
+			else:
+				allNone=False
+				if isFirstNone:
+					isFirstNone=False
+				# N is a wildcard
+				if b[r-1] in WILDECARDS or a[c-1] in WILDECARDS or b[r-1] == a[c-1]:
+					sub=0
+				else:
+					sub=1
+				
+				if T!=None:
+					if "T" in mTB[r-1][c]:
+						T+=extD
+					else:
+						T+=dele
+				
+				if L!=None:
+					if "L" in mTB[r][c-1]:
+						L+=extI
+					else:
+						L+=ins
+				if D!=None:
+					D+=sub
+				minLev=min([x for x in [T,L,D] if x!=None])
+				if maxEdit!=-1 and minLev>=maxEdit:
+					minLev=None
+				add=""
+				if minLev==T:
+					add+="T"
+				if minLev==L:
+					add+="L"
+				if minLev==D:
+					add+="D"
+				if add!="":
+					mTB[r][c]=add
+				mLev[r][c]=minLev
+			c+=1
+		r+=1
+		if allNone: # If the whole line was made of None values
+			# Then the alignment failed
+			r=m+1 # Exit the loop
+
+	lev=mLev[m][n]
+	if doConsensus and lev!=None:
+		cons=consensus(a,b,mTB,trimN=trimN)
+	#print(mLev)
+	#print(mTB)
+	isRC=False
+	if minRC and lev==None:
+		lLevRC=levDist(a,rc(b),match=match, mm=mm, gap=gap, ext=ext, maxEdit=maxEdit, minRC=False,semiLoc=semiLoc,trimN=trimN)
+		levRC=lLevRC[0]
+		if levRC!=None and (lev==None or levRC<lev):
+			lev=levRC
+			isRC=True
+			cons=lLevRC[2]
+	return [lev,isRC,cons]
+	
+def multLev(d, lID, triangular=True, semiLoc=False, lExclude=[], maxID=-1, k=41):
+	# Launch all successive Levenshtein distances between sequence in a dictionnary d[ID]=[[string1,string2],[len1,len2]] if length diff between sequence < threshold
+	# For seq1 seq2 seq3 seq4, true seq1-seq2 compression:
+	# - If compressed: try seq3-seq4 compression
+	# - If not compressed: try seq2-seq3 compression
+	# triangular is not useful
+	# smiLoc=True for semi-local alignment
+	# lExclude is a list of cycle ID tuple indicating cycles allready compared but that could not be compressed. Used to avoid recomputing their edit distance
+	# maxID: maximum number of cycle in a BCC to try compression. -1 for all.
+	# Return a dictionnary d[cycle1][cycle2]=[levDistLow,levDistUp,consensusLow,consensusUpSeq,consensusUpVar] (and the same for d[cycle1][cycle2] if triangular=True)
+	lComp=[]
+	nID=len(lID)
+	if len(lID)<2 or (maxID!=-1 and len(lID)>maxID):
+		return [{},lComp]
+	dLev={}
+	j=0
+	while j+1 < nID:
+		ID1=lID[j]
+		l1l=d[ID1][1][1]
+		l1u=d[ID1][1][0]
+		ID2=lID[(j+1)]
+		l2l=d[ID2][1][1]
+		l2u=d[ID2][1][0]
+		levL=None
+		levU=None
+		addErrorMin=int(l2u/300)
+		addErrorMax=int(l1u/100)
+		#print("Comparing "+ID1+" and "+ID2)
+		if (ID1,ID2) not in lExclude and abs(l1l-l2l)<=BASEDIFFPATH_LOW and abs(l1u-l2u)<=BASEDIFFPATH_UP+addErrorMin: # Length difference of lower path < 6 and length diff of upper path < 3
+			# LOW COMPARISON
+			# We had the variable sequence to the middle of the lower path
+			var1=d[ID1][0][2]
+			var2=d[ID2][0][2]
+			seqLow1=d[ID1][0][1]
+			#seqLow1=seqLow1[:k]+var1+seqLow1[k:]
+			seqLow2=d[ID2][0][1]
+			#seqLow2=seqLow2[:k]+var2+seqLow2[k:]
+			levL,isRC,consL=levDist(seqLow1,seqLow2,maxEdit=BASEMM_LOW,ext=EXT,semiLoc=True) # We allow an high error rate on the lower path
+			if levL!=None:
+				# UP COMPARISON
+				seqUp1=d[ID1][0][0]
+				seqUp2=d[ID2][0][0]
+				if isRC: # if reverse complement was used for lower path, use it for upper path
+					seqUp2=rc(seqUp2)
+					var2=rc(var2)
+				# Variable path comp
+				lLevV=levDist(var1,var2,mm=MMATCH/(addErrorMax+1),ext=EXT,semiLoc=False,minRC=False,trimN=False)
+				levV=lLevV[0]
+				consVar=lLevV[2]
+				if levV!=None: # Variable path align correctly
+					## Test if we are in a big IR cluster
+					idioticOK=False
+					if len(seqUp1)==len(seqUp2) and l1u>=MAXLENGTHIDIOTIC: # Both path of the same length and upper path > 2000nc, probably a big IR
+						levU,consU=idioticConsensus(seqUp1,seqUp2,maxEdit=BASEMM_UP+addErrorMax-levV)
+						if levU!=None:
+							idioticOK=True
+					##
+					if not idioticOK and l1u<MAXLENGTHALIGN:
+						# Do alignment
+						lLevU=levDist(seqUp1,seqUp2,maxEdit=BASEMM_UP-levV,mm=MMATCH/(addErrorMax+1),ext=EXT,semiLoc=False,minRC=False,trimN=False)
+						levU=lLevU[0]
+						consU=lLevU[2]
+			# We know if we compress or not if levU!=None
+			if levU==None: # We will remmember to not compare these two paths because their lower or upper path are too divergent
+				lExclude.append((ID1,ID2))
+			else:
+				#consLseq=consL[:k]+consL[-k:]
+				#consVar=consL[k:-k]
+				j+=1
+				if ID1 not in dLev.keys():
+					dLev[ID1]={}
+				dLev[ID1][ID2]=[levL,levU,consL,consU,consVar]
+				lComp.append((ID1,ID2))
+				if not triangular:
+					if not ID2 in dLev.keys():
+						dLev[ID2]={}
+					dLev[ID2][ID1]=[levL,levU,consL,consU,consVar]
+		j+=1
+	return [dLev,lComp]
+	
+def readFasta4(f, k=41, rmEntropy=True, entropy_threshold=ENTROPYMAX, dBCC2lc={}):
+	# Read 4 lines of a fasta file from KisSplice (1 cycle)
+	# return [ [bcc, cycle, type, length_up, length_low], [seq_up, seq_low, seq_var] ]
+	# return KO if the entropy filter failed (WARNING: do not try to recursively call readFasta4 in this case as the max recursive instance can easily be reached)
+	# seq_var is the sequence from the variable part of the upper path that can be find either at its begining or ending
+	# seq_up is the sequence from the variable part without seq_var
+	head=f.readline().strip()
+	if head=="":
+		return ""
+	lHead=head.split("|")
+	bcc=lHead[0].split("_")[1]
+	c=lHead[1].split("_")[1]
+	t=lHead[2].split("_")[1]
+	lup=int(lHead[3].split("_")[-1])
+	mk=min(lup,2*k)
+	seq=f.readline().strip()
+	seqUp="".join([x for x in seq if x.isupper()])
+	head=f.readline().strip()
+	lHead=head.split("|")
+	llow=int(lHead[3].split("_")[-1])
+	seq=f.readline().strip()
+	seqLow="".join([x for x in seq if x.isupper()])
+	lup=lup-llow
+	if rmEntropy:
+		#ent=entropyShannon(seqUp)
+		lEnt=windowEntropy(seqUp, WLEN, WSLIDE)
+		ent=max(lEnt)
+		if ent<entropy_threshold:
+			#print("\n".join([">bcc_"+bcc+"|Cycle_"+c,seqUp,">lower",seqLow]))
+			if not bcc in dBCC2lc.keys():
+				dBCC2lc[bcc]=[]
+			dBCC2lc[bcc].append([c,str(lEnt)])
+			return "KO"
+	# SeqUp will be the upper sequence without the potential repeated bases at the begining/end of the upperpath, ie :
+	# >bcc_9988|Cycle_0|Type_1|upper_path_length_163
+	# GGCTGCAACCGAGTCTTCATAGAAGAGAATCTGCTGTACCTCGGAATCCTCGCTGAAGTCTTCGGTGACGGTAGAGGAGGAGGCCTGCCGGGGGAGCTTGGCCTCGTATGCCATGACGCTCCACCTGTCCAGCATCTTGGTGCTGGCTCTCTCCAACTTCTCC
+	# >bcc_9988|Cycle_0|Type_1|lower_path_length_78
+	# GGCTGCAACCGAGTCTTCATAGAAGAGAATCTGCTGTACCTGTCCAGCATCTTGGTGCTGGCTCTCTCCAACTTCTCC
+	# In this exemple, the upper sequence can either be :
+	# CGGAATCCTCGCTGAAGTCTTCGGTGACGGTAGAGGAGGAGGCCTGCCGGGGGAGCTTGGCCTCGTATGCCATGACGCTCCACCT
+	# OR
+	# ACCTCGGAATCCTCGCTGAAGTCTTCGGTGACGGTAGAGGAGGAGGCCTGCCGGGGGAGCTTGGCCTCGTATGCCATGACGCTCC
+	# Because of the starting/ending ACCT
+	# We will report seqUp=CGGAATCCTCGCTGAAGTCTTCGGTGACGGTAGAGGAGGAGGCCTGCCGGGGGAGCTTGGCCTCGTATGCCATGACGCTCC and var=ACCT
+	# The size of var is 2*k-lowerPathLength
+	# In can happen that the upper path is < 2*k, in this case we have to use the lvar=lup-llow
+	lvar=mk-llow
+	var=seqUp[k-lvar:k]
+	seqUp=seqUp[k:k+lup-lvar]	
+	# It is possible that seqUp is empty, if the path of the var is the same as the upper path, ie
+	# >bcc_9962|Cycle_0|Type_1|upper_path_length_82
+	# ATAAAGGATATGTTGAATACACCTTTGTGTCCTTCACACAGCAGTTTACATCCAGTGCTGTTACCTTCAGATGTATTTGACC
+	# >bcc_9962|Cycle_0|Type_1|lower_path_length_79
+	# ATAAAGGATATGTTGAATACACCTTTGTGTCCTTCACACAGTTTACATCCAGTGCTGTTACCTTCAGATGTATTTGACC
+	# In this exemple, seqUp='' and var="CAG"
+	# So we simply invert them
+	if seqUp=="":
+		seqUp,var=var,seqUp
+	return [ [bcc, c, t, lup, llow], [seqUp, seqLow, var]]
+
+def compress(dLev, dSeq, lCycleOrder, lExclude):
+	# Remove one of the paths from dSeq and lCycleOrder
+	# Return dictionnary of compressed paths
+	#print(dLev)
+	#print("BEFORE COMPRESS")
+	#print(dSeq)
+	#print(lCycleOrder)
+	for ID1 in dLev.keys():
+		for ID2 in dLev[ID1].keys():
+			dSeq[ID1][0][0]=dLev[ID1][ID2][3]
+			dSeq[ID1][0][2]=dLev[ID1][ID2][4]
+			dSeq[ID1][0][1]=dLev[ID1][ID2][2]
+			dSeq[ID1][1][0]=len(dSeq[ID1][0][0])+len(dSeq[ID1][0][2])
+			dSeq[ID1][1][1]=len(dSeq[ID1][0][1])
+			del dSeq[ID2]
+			del lCycleOrder[lCycleOrder.index(ID2)]
+			# Delete pairs containing either ID1 or ID2 from lExclude
+			lExclude=[lExclude[i] for i in range(len(lExclude)) if ID1 not in lExclude[i] and ID2 not in lExclude[i]]
+	del dLev
+	#print("AFTER COMPRESS")
+	#print(dSeq)
+	#print(lCycleOrder)
+
+def addComp(d,l):
+	# Add compressed paths to the dictionnary of compressed path
+	# d : d[cycle]=[compCycle1, compCycle2, ...]
+	# l : [(cycle1, cycleComp1), (cycle2, cycleComp2), ...] cycleCompX is compressed in cycleX
+	# cycleCompX can be a key of d
+	for cycle,comp in l:
+		if cycle in d.keys():
+			d[cycle].append(comp)
+		else:
+			d[cycle]=[comp]
+		if comp in d.keys():
+			d[cycle].extend(d[comp])
+			del d[comp]
+
+def compressBcc(dBcc, cBcc, dSeq, lCycleOrder, dBcc2cycleComp, dBCC2size, dBccLen2nCompress, k):
+	# t is the type of event
+	# Compressed sequences will be written in fComp, an open writable file
+	# Do the whole compression of a BCC
+	lExclude=[] # list of cycle pairs that can not be compressed
+	# dBccLen2nCompress is not mendatory, contain useful(?) informations. d[BCCsize]=[nBCC, number of compressed path]
+	dBcc2cycleComp[cBcc]={} # not mendatory, contain useful(?) informations. d[bcc][cycle]=[compressed cycles]
+	BCCsize=len(dSeq.keys())
+	dBCC2size[cBcc]=BCCsize # not mendatory, contain useful(?) informations. d[bcc]=size
+	if not BCCsize in dBccLen2nCompress.keys():
+		dBccLen2nCompress[BCCsize]=[0,0]
+	dBccLen2nCompress[BCCsize][0]+=1
+	#print("BCC "+cBcc+" (of "+str(len(dSeq.keys()))+" cycles)")
+	dBcc[cBcc],lComp=multLev(dSeq,lCycleOrder,lExclude=lExclude,k=k)
+	while dBcc[cBcc] != {}: # While we have some compression to do
+		#print(lComp)
+		dBccLen2nCompress[BCCsize][1]+=len(lComp)
+		addComp(dBcc2cycleComp[cBcc],lComp)
+		compress(dBcc[cBcc], dSeq, lCycleOrder, lExclude)
+		dBcc[cBcc],lComp=multLev(dSeq,lCycleOrder,lExclude=lExclude,k=k)
+	#print(dBcc[cBcc])
+	#print(dBccLen2nCompress)
+	#print(dBcc2cycleComp)
+
+def writeCompressedCycles(dSeq, cBcc, t, fComp, kval):
+	# Write info in dSeq to fComp
+	# dSeq: d[cycle]=[ [seqUp, seqLow, var], [lenUp, lenLow] ]
+	# head format: >bcc_[cBcc]|Cycle_[cycle]|Type_[t]|upper/lower_path_length_[length]
+	headBcc=">bcc_"+cBcc
+	headType="Type_"+t
+	for cycle in dSeq.keys():
+		lInfo=dSeq[cycle]
+		seqLow=lInfo[0][1]
+		seqUp=seqLow[:kval]+lInfo[0][0]+lInfo[0][2]+seqLow[kval:]
+		lenUp=str(lInfo[1][0]+lInfo[1][1])
+		lenLow=str(lInfo[1][1])
+		headCycle="Cycle_"+cycle
+		headLenUp="upper_path_length_"+lenUp
+		headLenLow="lower_path_length_"+lenLow
+		head="|".join([headBcc,headCycle,headType])
+		headUp="|".join([head,headLenUp])
+		headLow="|".join([head,headLenLow])
+		fComp.write("\n".join([headUp,seqUp,headLow,seqLow])+"\n")
+
+def splitT1T234(fName, fNameT1, fNameT234):
+	f=open(fName,"r")
+	f1=open(fNameT1,"w")
+	f234=open(fNameT234,"w")
+	retype = re.compile('Type_\d+')
+	line=f.readline()
+	while line:
+		t=retype.search(line).group()
+		if t=="Type_1":
+			oF=f1
+		else:
+			oF=f234
+		oF.write(line)
+		line=f.readline()
+		oF.write(line)
+		line=f.readline()
+		oF.write(line)
+		line=f.readline()
+		oF.write(line)
+		line=f.readline()
+	f.close()
+	f1.close()
+	f234.close()
+
+def redundancyAndLowComplexityRemoval(workdir, mainFileName, keep_rd=False, keep_lc=False, lc_ent=ENTROPYMAX, get_rd_info=True, get_lc_info=True, t1o=False, kval=41):
+	# Main function for redundancy and low-complexity bubbles removal
+	# workdir: str, working directory
+	# mainFileName: str, fasta file name containing all types of bubbles
+	# keep_rd: boolean, do we remove redundancy?
+	# keep_lc: boolean, do we keep low-complexity bubbles?
+	# lc_ent: int, Shannon Entropy threshold to define a bubble as low-complexity (if below this value)
+	# get_rd_info: boolean, do we print useful(?) informations about redundancy removal in some files?
+	# get_lc_info: boolean, do we print useful(?) informations about low-complexity removal in some files?
+	# kval: int, k-mers value
+	# return list of files to copy from the workdir to the resultdir, will replace mainFile by a new file if needed, and will create new files in the workdir
+
+	# Do we need to do anything?
+	if keep_rd and keep_lc and not t1o:
+		return []
+
+	print("\n" + getTimestamp() + "--> Removing low-complexity/redundant bubbles...")
+	printlg("\n" + getTimestamp() + "--> Removing low-complexity/redundant bubbles...")
+	
+	toMove=[] # list of files to move to the result directory (will be returned)
+	toRm=[] # list of files to remove
+
+	# 1) Divide Type_1 bubbles in one file, other bubbles in another file
+	# The Type_1 file will be moved to the result directory
+	t1fileName="all_bcc_type1.fa"
+	t234fileName="all_bcc_type234.fa"
+	if not keep_lc or not keep_rd:
+		toMove.append(t1fileName)
+	toRm.append(t234fileName)
+	splitT1T234("/".join([workdir,mainFileName]), "/".join([workdir,t1fileName]), "/".join([workdir,t234fileName]))
+
+	if not keep_rd or not keep_lc:
+		# 2) Define some dictionnaries...
+		dSeq={} # d[cycle]=[ [seqUp, seqLow, var], [lenUp, lenLow] ]
+		dBcc={} # d[bcc]=dLev with dLev = d[cycle1][cycle2]=[levDistLow,levDistUp,consensusLow,consensusUpSeq,consensusUpVar]
+		dBcc2cycleComp={} # d[bcc][cycle]=[compressed cycles]
+		dBccLen2nCompress={} # d[BCCsize]=[nBCC, number of compressed path]
+		dBCC2size={} # d[bcc]=size
+		dBCC2lc={} # d[bcc]=[ [removed cycle due to low complexity, entropy value], ...]
+		# ... and an output file
+		t1fileNameComp="all_bcc_type1_compressed.fa"
+		fComp=open("/".join([workdir,t1fileNameComp]), "w")
+		if not keep_rd:
+			toMove.append(t1fileNameComp)
+
+		# 3) Open and read first line of Type_1 file
+		f=open("/".join([workdir,t1fileName]),"r")
+		lFasta="KO"
+		while lFasta and lFasta=="KO":
+			lFasta=readFasta4(f, k=kval, rmEntropy=not keep_lc, entropy_threshold=lc_ent, dBCC2lc=dBCC2lc) # [ [bcc, cycle, type, length_up, length_low], [seq_up, seq_low, seq_var] ]
+		# Fasta file not empty and the cycle was not removed by entropy filter (!=KO)
+		if lFasta:
+			# First interesting line of fasta
+			cBcc=lFasta[0][0] # current BCC
+			dSeq[lFasta[0][1]]=[lFasta[1], lFasta[0][3:5]] # we add the sequences informations associated to this cycle to dSeq
+			lCycleOrder=[lFasta[0][1]] # list of cycles ID order as in the fasta file (close cycles have less divergence)
+			# 4) Read the whole fasta file and compress BCC
+			while lFasta:
+				# Read a new cycle
+				lFasta=readFasta4(f, k=kval, rmEntropy=not keep_lc, entropy_threshold=lc_ent, dBCC2lc=dBCC2lc) # [ [bcc, cycle, type, length_up, length_low], [seq_up, seq_low, seq_var] ]
+				if lFasta and lFasta!="KO": # EOF or removed cycle due to low complexity
+					if cBcc!=lFasta[0][0]: # New bcc
+						# Compress the cycles from the previous BCC, if needed
+						if not keep_rd:
+							compressBcc(dBcc, cBcc, dSeq, lCycleOrder, dBcc2cycleComp, dBCC2size, dBccLen2nCompress, kval)
+						writeCompressedCycles(dSeq, cBcc, "1", fComp, kval)
+						cBcc=lFasta[0][0] # New current BCC
+						dSeq={}
+						lCycleOrder=[]
+					dSeq[lFasta[0][1]]=[lFasta[1], lFasta[0][3:5]] # we add the sequences informations associated to this cycle to dSeq
+					lCycleOrder.extend([lFasta[0][1]]) # list of cycles ID order as in the fasta file (close cycles have less divergence)
+			# Compress the cycles of the last BCC
+			if not keep_rd:
+				compressBcc(dBcc, cBcc, dSeq, lCycleOrder, dBcc2cycleComp, dBCC2size, dBccLen2nCompress, kval)
+			writeCompressedCycles(dSeq, cBcc, "1", fComp, kval)
+
+		f.close()
+		fComp.close()
+
+		# 5) Make informations files
+		if get_rd_info:
+			fNameSummary="get_redundancy_info_summary.tsv"
+			fNameRd="get_redundancy_info_compressed_bubbles.tsv"
+			toMove.extend([fNameSummary,fNameRd])
+			makeSummaryRd("/".join([workdir,fNameSummary]), "/".join([workdir,fNameRd]), dBcc2cycleComp, dBccLen2nCompress)
+
+		if get_lc_info:
+			fNameLc="get_low-complexity_info.tsv"
+			toMove.append(fNameLc)
+			makeSummaryLc("/".join([workdir,fNameLc]), dBCC2lc)
+	
+	# 6) Write a new mainFile, combining filtered type 1 and type 234 or type 1 only
+	if t1o:
+		if not keep_rd or not keep_lc:
+			os.system("cat "+"/".join([workdir,t1fileNameComp])+" > "+"/".join([workdir,mainFileName]))
+		else:
+			os.system("cat "+"/".join([workdir,t1fileName])+" > "+"/".join([workdir,mainFileName]))
+	else:
+		os.system("cat "+"/".join([workdir,t1fileNameComp])+" "+"/".join([workdir,t234fileName])+" > "+"/".join([workdir,mainFileName]))
+
+	# 7) Remove some files
+	for fRm in toRm:
+		os.system("rm "+"/".join([workdir,fRm]))
+
+	print(getTimestamp() + "--> Done!")
+	printlg(getTimestamp() + "--> Done!")
+
+	return toMove
+
+def makeSummaryLc(fNameLc, dBCC2lc):
+	# dBCC2lc : d[bcc]=[ [removed cycle due to low complexity, entropy value], ...]
+	fLc=open(fNameLc, "w")
+
+	# low-complexity file
+	# bcc removed_cycle entropy_value
+	head="\t".join(["bcc", "removed_cycle", "shannon_entropy"])
+	fLc.write(head)
+	for bcc in dBCC2lc.keys():
+		for lInfo in dBCC2lc[bcc]:
+			rmCycle=lInfo[0]
+			ent=str(lInfo[1])
+			fLc.write("\n"+"\t".join([bcc, rmCycle, ent]))
+	fLc.close()
+		
+
+
+
+def makeSummaryRd(fNameSummary, fNameRd, dBcc2cycleComp, dBccLen2nCompress):
+	# dBcc2cycleComp : d[bcc][cycle]=[compressed cycles]
+	# dBccLen2nCompress :  d[BCCsize]=[nBCC, number of compressed path]
+	fSum=open(fNameSummary, "w")
+	fRd=open(fNameRd, "w")
+
+	# redundancy file 
+	# bcc	consensus_cycle	compressed_cycles	nCompressed
+	headRd="\t".join(["bcc","consensus_cycle","compressed_cycles","nCompressed"])
+	fRd.write(headRd)
+	for bcc in dBcc2cycleComp.keys():
+		for cycle in dBcc2cycleComp[bcc].keys():
+			lComp=dBcc2cycleComp[bcc][cycle]
+			if lComp!=[]:
+				fRd.write("\n"+"\t".join([bcc, cycle, ",".join(lComp), str(len(lComp))]))
+	fRd.close()
+
+	# summary file 
+	# bcc_size	nBcc	nCycles	nCompressedCycles	%compressed	nRemainingCycles
+	headSum="\t".join(["bcc_size","nBcc","nCycles","nCompressedCycles", "%compressed", "nRemainingCycles"])
+	fSum.write(headSum)
+	for bccSize in sorted(list(dBccLen2nCompress.keys())):
+		lInfo=dBccLen2nCompress[bccSize]
+		nBcc=lInfo[0]
+		nComp=lInfo[1]
+		nCycles=bccSize*nBcc
+		nRemain=nCycles-nComp
+		pComp=round((nComp/nCycles)*100)
+		fSum.write("\n"+"\t".join([str(bccSize), str(nBcc), str(nCycles), str(nComp), str(pComp)+"%", str(nRemain)]))
+	fSum.close()
+	
+
+###########################################################
+
 # print str to the logFile
 def printlg (*args):
     global logFile
@@ -470,15 +1129,13 @@ def concatenate_graph_all_log_bcc_to_all_bcc_type0(workdir, kval, output_snps):
 
 
 def check_read_coverage_and_sort_all_bubbles(internal_bindir, readfiles, workdir, outdir, kval, output_snps, infix_name, 
-  countsMethods, minOverlap, substitutions, getMappingInfo, stranded, strandedAbsoluteThreshold, strandedRelativeThreshold, nbprocs, verbose = False):
-    print("\n" + getTimestamp() + "--> Computing read coherence and coverage...")
-    printlg("\n" + getTimestamp() + "--> Computing read coherence and coverage...")
+  countsMethods, minOverlap, substitutions, substitutionsSNP, getMappingInfo, stranded, strandedAbsoluteThreshold, strandedRelativeThreshold, nbprocs, verbose = False):
 
     # Two KisSreads executions, one for type 0 one for type 1234
     #  Du to the k extension, anchor should be of size k+1 for SNP
     commandLineType0=""
     if output_snps > 0:
-            commandLineType0 = internal_bindir+"/ks_kissreadsSNPS "+workdir+"/all_bcc_type0_"+str(kval)+" "+readfiles+" -i 5 -S 25 -O "+str(kval+minOverlap)+" -o "+ workdir+"/coherentType0.fa -u "+workdir+"/uncoherentType0.fa  -d " + str(substitutions) + " -c 1 -n -t "+str(nbprocs)
+            commandLineType0 = internal_bindir+"/ks_kissreadsSNPS "+workdir+"/all_bcc_type0_"+str(kval)+" "+readfiles+" -i 5 -S 25 -O "+str(kval+minOverlap)+" -o "+ workdir+"/coherentType0.fa -u "+workdir+"/uncoherentType0.fa  -d " + str(substitutionsSNP) + " -c 1 -n -t "+str(nbprocs)
             if stranded:
               commandLineType0+=" -x -a " + str(strandedAbsoluteThreshold) + " -r " + str(strandedRelativeThreshold) + " "
             if getMappingInfo:
@@ -493,6 +1150,9 @@ def check_read_coverage_and_sort_all_bubbles(internal_bindir, readfiles, workdir
             if os.path.exists(workdir+"/uncoherentType0.fa.explanations"):
               shutil.move(workdir+"/uncoherentType0.fa.explanations", outdir+"/uncoherentType0.fa.explanations")
 
+    print("\n" + getTimestamp() + "--> Computing read coherence and coverage...")
+    printlg("\n" + getTimestamp() + "--> Computing read coherence and coverage...")
+
     # no n options anymore
     commandLineType1234 = internal_bindir+"/ks_kissreadsSplice "+workdir+"/all_bcc_type1234_"+str(kval)+" "+readfiles+" -i 5 -k "+str(kval)+" -S 25 -O "+str(kval+minOverlap)+" -o "+workdir+"/coherentType1234.fa -u "+workdir+"/uncoherentType1234.fa  -d " + str(substitutions) + " -c 1 -j " + countsMethods +" -l " + str(minOverlap) +" -t "+str(nbprocs)
     if stranded:
@@ -720,6 +1380,8 @@ def main():
                        help="maximal number of bubbles enumeration in each bcc. If exceeded, no bubble is output for the bcc (default 100M)")
   parser.add_argument('--mismatches',  action = "store", dest = "mism", default = 2, type = int,
                       help="Maximal number of substitutions authorized between a read and a fragment (for quantification only), default 2. If you increase the mismatch and use --counts think of increasing min_overlap too.")
+  parser.add_argument('--mismatchesSNP',  action = "store", dest = "mismSNP", default = 0, type = int,
+                      help="Maximal number of substitutions authorized between a read and a fragment (for quantification only) for SNP, default 0.")
   parser.add_argument('--counts',  action = "store", dest = "countsMethod", default = "2", help="0,1 or 2 . Changes how the counts will be reported. If 0 : total counts, if 1: counts on junctions, if 2 (default): all counts. see User guide for more information ")
   parser.add_argument('--min_overlap',  action = "store", dest = "minOverlap", default = 5, type=int, help="Set how many nt must overlap a junction to be counted by --counts option. Default=5. see User guide for more information ")
   parser.add_argument('--timeout', action='store', dest="timeout", default=TIMEOUT,
@@ -737,7 +1399,12 @@ def main():
   parser.add_argument('--stranded', action="store_true", dest="stranded", default = False, help="Execute kissreads in stranded mode.")
   parser.add_argument('--strandedAbsoluteThreshold',  action = "store", dest = "strandedAbsoluteThreshold", default = 3, type=int, help="Sets the minimum number of reads mapping to a path of a bubble in a read set is needed to call a strand.")
   parser.add_argument('--strandedRelativeThreshold',  action = "store", dest = "strandedRelativeThreshold", default = 0.75, help="If a strand is called for a path of a bubble in a read set, but the proportion of reads calling this strand is less than this threshold, then the strand of the path is set to '?' (any strand - not enough evidence to call a strand).")
-
+  parser.add_argument('--keep-redundancy', action="store_true", dest="keep_rd", default = False, help="Keep the Type_1 redundant cycles in the result file.")
+  parser.add_argument('--keep-low-complexity', action="store_true", dest="keep_lc", default = False, help="Keep the low-complexity Type_1 cycles in the result file.")
+  parser.add_argument('--lc-entropy-threshold',  action = "store", dest = "lc_ent", default = ENTROPYMAX, type=int, help="Cycles with a Shannon entropy value for their upper path below this value will be removed (use --keep-low-complexity to keep them).")
+  parser.add_argument('--get-redundance-info', action="store_true", dest="get_rd_info", default = False, help="Creates files with informations on compressed redundant cycles.")
+  parser.add_argument('--get-low-complexity-info', action="store_true", dest="get_lc_info", default = False, help="Creates a file with informations on removed low-complexity cycles.")
+  parser.add_argument('--type1-only', action="store_true", dest="t1o", default = False, help="Only quantify Type 1 bubbles (alternative splicing events, MAJOR SPEED UP with -b > 10 BUT all other bubbles will not appear in the result file).")
   # ------------------------------------------------------------------------
   #               Parse and interpret command line arguments
   # ------------------------------------------------------------------------
@@ -754,7 +1421,7 @@ def main():
   #               Create the log file
   # ------------------------------------------------------------------------
   global logFile, logFileName
-  logFileName = outdir+"/kissplice_log_summary_"+time.strftime("%H-%M-%S")+"_"+time.strftime("%d-%m-%Y")+"_"+str(randint(0, 1000000))
+  logFileName = outdir+"/kissplice_log_summary_"+time.strftime("%Y-%m-%d")+"_"+time.strftime("%H-%M-%S")+"_"+str(randint(0, 1000000))
   logFile = open(logFileName, 'w')
   
 
@@ -870,8 +1537,8 @@ def main():
   if not nobuild:
     t = time.time()
     build_graph(internal_bindir, workdir, readfiles, kval, graphfile, min_cov, options.nbprocs, options.verbose)
-    print("Elapsed time: ", (time.time() - t), " seconds")
-    printlg("Elapsed time: ", (time.time() - t), " seconds")
+    print("Elapsed time: ", round(time.time() - t,1), " seconds")
+    printlg("Elapsed time: ", round(time.time() - t,1), " seconds")
 
   # ------------------------------------------------------------------------
   #                          Error removal
@@ -882,8 +1549,8 @@ def main():
   else:
      exec_error_removal = False
   
-  print("Elapsed time: ", (time.time() - t), " seconds")
-  printlg("Elapsed time: ", (time.time() - t), " seconds")
+  print("Elapsed time: ", round(time.time() - t,1), " seconds")
+  printlg("Elapsed time: ", round(time.time() - t,1), " seconds")
   
 
   # ------------------------------------------------------------------------
@@ -891,8 +1558,8 @@ def main():
   # ------------------------------------------------------------------------
   t = time.time()
   run_modules(internal_bindir, workdir, graphfile, kval, options.min_relative_cov, options.verbose, options.output_context, exec_error_removal)
-  print("Elapsed time: ", (time.time() - t), " seconds")
-  printlg("Elapsed time: ", (time.time() - t), " seconds")
+  print("Elapsed time: ", round(time.time() - t,1), " seconds")
+  printlg("Elapsed time: ", round(time.time() - t,1), " seconds")
 
 
   # ------------------------------------------------------------------------
@@ -902,20 +1569,30 @@ def main():
   enumerate_all_bubbles(internal_bindir, workdir, outdir, kval, options.bval, output_snps, min_edit_dist, max_cycles, \
                         UL_MAX, LL_MAX, LL_MIN, float(options.timeout), options.nbprocs, options.verbose, \
                         options.output_context, options.output_path, options.output_branch_count, options.experimental, options.max_memory)
-  print("Elapsed time: ", (time.time() - t), " seconds")
-  printlg("Elapsed time: ", (time.time() - t), " seconds")
+  print("Elapsed time: ", round(time.time() - t,1), " seconds")
+  printlg("Elapsed time: ", round(time.time() - t,1), " seconds")
 
 
   # ------------------------------------------------------------------------
-  #            Check read coverage (optionnal) and sort bubbles
+  #            Sort and remove redundancy/low-complexity bubbles (optionnal), only keep type_1 events (optionnal)
   # ------------------------------------------------------------------------
   t = time.time()
   nb = sort_all_bubbles(internal_bindir, readfiles, workdir, outdir, kval, output_snps, infix_name, not only_graph, options.verbose)
+  filesToMove=redundancyAndLowComplexityRemoval(workdir, "all_bcc_type1234_"+str(kval), options.keep_rd, options.keep_lc, options.lc_ent, options.get_rd_info, options.get_lc_info, options.t1o, kval)
+  for fToMove in filesToMove:
+    shutil.move("/".join([workdir,fToMove]), "/".join([outdir,fToMove]))
+  print("Elapsed time: "+str(round(time.time() - t,1))+" seconds")
+  printlg("Elapsed time: "+str(round(time.time() - t,1))+" seconds")
+
+  # ------------------------------------------------------------------------
+  #            Check read coverage (optionnal)
+  # ------------------------------------------------------------------------
+  t = time.time()
   if not only_graph:
-    nb = check_read_coverage_and_sort_all_bubbles(internal_bindir, readfiles, workdir, outdir, kval, output_snps, infix_name, countsMethod, minOverlap, options.mism, options.get_mapping_info, options.stranded, options.strandedAbsoluteThreshold, options.strandedRelativeThreshold, options.nbprocs, options.verbose)
+    nb = check_read_coverage_and_sort_all_bubbles(internal_bindir, readfiles, workdir, outdir, kval, output_snps, infix_name, countsMethod, minOverlap, options.mism, options.mismSNP, options.get_mapping_info, options.stranded, options.strandedAbsoluteThreshold, options.strandedRelativeThreshold, options.nbprocs, options.verbose)
 
-  print("Elapsed time: ", (time.time() - t), " seconds\n")
-  printlg("Elapsed time: ", (time.time() - t), " seconds\n")
+  print("Elapsed time: ", round(time.time() - t,1), " seconds\n")
+  printlg("Elapsed time: ", round(time.time() - t,1), " seconds\n")
 
   if only_graph:
     print("\n\n \t\t ******** We are done, final results are in files "+outdir+"/results_"+infix_name+"_type_*.fa **********")


=====================================
man/CMakeLists.txt
=====================================
@@ -1,5 +1,4 @@
 # Fill manpage template with version and date
 # TODO maybe use tools like help2man to automate manpage, considering this is mostly a --help message ?
-string(TIMESTAMP MANPAGE_DATE "%Y-%m-%d")
 configure_file("${CMAKE_CURRENT_SOURCE_DIR}/kissplice.in.1" "${CMAKE_CURRENT_BINARY_DIR}/kissplice.1" @ONLY)
 install(FILES "${CMAKE_CURRENT_BINARY_DIR}/kissplice.1" DESTINATION "${CMAKE_INSTALL_MANDIR}/man1")
\ No newline at end of file


=====================================
man/kissplice.in.1
=====================================
@@ -1,4 +1,4 @@
-.TH KISSPLICE "1" "@MANPAGE_DATE@" "kissplice @PROJECT_VERSION@" "User Commands"
+.TH KISSPLICE "1" "@CONFIGURE_DATE@" "kissplice @PROJECT_VERSION@" "User Commands"
 .SH NAME
 kisSplice \- local assembly of SNPs, indels and AS events
 .SH SYNOPSIS
@@ -129,3 +129,21 @@ Sets the minimum number of reads mapping to a path of a bubble in a read set is
 .TP
 \fB\-\-strandedRelativeThreshold\fR
 If a strand is called for a path of a bubble in a read set, but the proportion of reads calling this strand is less than this threshold, then the strand of the path is set to '?' (any strand - not enough evidence to call a strand).
+.TP
+\fB\-\-keep-redundancy\fR
+Keep the Type_1 redundant cycles in the result file.
+.TP
+\fB\-\-keep-low-complexity\fR
+Keep the low-complexity Type_1 cycles in the result file.
+.TP
+\fB\-\-lc-entropy-threshold\fR
+Cycles with a Shannon entropy value for their upper path below this value will be removed (use --keep-low-complexity to keep them).
+.TP
+\fB\-\-get-redundance-info\fR
+Creates files with informations on compressed redundant cycles.
+.TP
+\fB\-\-get-low-complexity-info\fR
+Creates a file with informations on removed low-complexity cycles.
+.TP
+\fB\-\-type1-only\fR
+Only quantify Type 1 bubbles (alternative splicing events, MAJOR SPEED UP with -b > 10 BUT all other bubbles will not appear in the result file).
\ No newline at end of file


=====================================
tests/integration_tests/CMakeLists.txt
=====================================
@@ -9,7 +9,6 @@ add_test(KisspliceBasicTest "${BUILD_BINDIR}/kissplice" -h)
 # Comparing the graph and the count file
 # ============================================================================
 add_test(KisspliceDBGTest "${CMAKE_CURRENT_SOURCE_DIR}/kisspliceDBGTest.py" "${BUILD_BINDIR}")
-set_tests_properties(KisspliceDBGTest PROPERTIES PASS_REGULAR_EXPRESSION "test SUCCESSFUL")
 
 # ============================================================================
 # Tests on: /data/HBM75brain_100000.fasta 


=====================================
tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0
=====================================
@@ -12108,4 +12108,4 @@
 97.4
 97.8
 97.8
-99.2
\ No newline at end of file
+99.2


=====================================
tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0
=====================================
@@ -5339,4 +5339,4 @@ RR
 RR
 RR
 RR
-RR
\ No newline at end of file
+RR


=====================================
tests/integration_tests/data/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0
=====================================
@@ -12108,4 +12108,4 @@ TTTTTTTTTTTTTTTTTTTTTTTGCAG
 TTTTTTTTTTTTTTTTTTTTTTTGG
 TTTTTTTTTTTTTTTTTTTTTTTTA
 TTTTTTTTTTTTTTTTTTTTTTTTC
-TTTTTTTTTTTTTTTTTTTTTTTTG
\ No newline at end of file
+TTTTTTTTTTTTTTTTTTTTTTTTG


=====================================
tests/integration_tests/kisspliceDBGTest.py
=====================================
@@ -1,54 +1,34 @@
 #!/usr/bin/env python3
-import re
 from sys import argv
 from os.path import dirname, abspath
-import ProcessLauncher
-TEST_INSTDIR=dirname(abspath(argv[0])) 
-command_line = argv[1]+"/kissplice -k 25 -M 1000 -r "+TEST_INSTDIR+"/data/HBM75brain_100000.fasta -r "+TEST_INSTDIR+"/data/HBM75liver_100000.fasta --keep-counts -o "+ TEST_INSTDIR +"/results"
-result = ProcessLauncher.run(command_line)
+import filecmp
+import shutil
+import shlex
+import subprocess
 
-i=2
-for fn in [TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.edges", TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes", TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance"]:
-    fo=fn+"0"
-    f=open(fn,"r")
-    o=open(fo,"w")
-    toWrite=[]
-    for line in f:
-        line=line.strip()
-        lLine=line.split()
-        toWrite.append(lLine[i])
-    toWrite.sort()
-    o.write("\n".join(toWrite))
-    i-=1
-    f.close()
-    o.close()
+TEST_INSTDIR = dirname(abspath(argv[0]))
+BINDIR = argv[1]
 
-command_line_diff_edges  = "diff " + TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0 " + TEST_INSTDIR + "/data/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0" 
-result_diff_edges =  ProcessLauncher.run(command_line_diff_edges)
+result = subprocess.run(
+    shlex.split(f"{BINDIR}/kissplice -k 25 -M 1000 -r {TEST_INSTDIR}/data/HBM75brain_100000.fasta -r {TEST_INSTDIR}/data/HBM75liver_100000.fasta --keep-counts -o {TEST_INSTDIR}/results"),
+    check = True, capture_output = True
+)
+assert result.stderr == b""
 
-command_line_diff_nodes  = "diff " + TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0 " + TEST_INSTDIR + "/data/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0" 
-result_diff_nodes =  ProcessLauncher.run(command_line_diff_nodes)
- 
-command_line_diff_counts  = "diff " + TEST_INSTDIR + "/results/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0 " + TEST_INSTDIR + "/data/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0" 
-result_diff_counts =  ProcessLauncher.run(command_line_diff_counts)
+# Extract entries at column_index, sort them and store them in file+"0"
+def copy_sorted_entries(column_index: int, file: str):
+    with open(file) as lines:
+        entries = [l.strip().split()[column_index] for l in lines]
+    entries.sort()
+    with open(file + "0", "w") as f:
+        f.writelines(entry + "\n" for entry in entries)
 
-# testing expected results
-successful = True
+copy_sorted_entries(2, f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.edges")
+copy_sorted_entries(1, f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes")
+copy_sorted_entries(0, f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance")
 
-if result_diff_edges.decode('utf8')  != "":
-    successful = False
-
-if result_diff_nodes.decode('utf8')  != "":
-    successful = False
-     
-if result_diff_counts.decode('utf8') != "":
-    successful = False
-
-# summary
-if successful:
-    print("kisspliceDBGTest.py: test SUCCESSFUL")
-else:
-    print("kisspliceDBGTest.py: test FAILED")
+assert filecmp.cmp(f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0", f"{TEST_INSTDIR}/data/graph_HBM75brain_100000_HBM75liver_100000_k25.edges0")
+assert filecmp.cmp(f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0", f"{TEST_INSTDIR}/data/graph_HBM75brain_100000_HBM75liver_100000_k25.nodes0")
+assert filecmp.cmp(f"{TEST_INSTDIR}/results/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0", f"{TEST_INSTDIR}/data/graph_HBM75brain_100000_HBM75liver_100000_k25.abundance0")
     
-command_line_rm = "rm -r "+TEST_INSTDIR +"/results"
-ProcessLauncher.run(command_line_rm)
\ No newline at end of file
+shutil.rmtree(f"{TEST_INSTDIR}/results")
\ No newline at end of file


=====================================
tests/integration_tests/kisspliceGraphAndSequenceTest.py
=====================================
@@ -10,10 +10,10 @@ print(result)
 
 # testing expected results
 successful = True
-if not (re.search('0a: Single SNPs, Inexact Repeats or sequencing substitution errors, 29 found', result.decode())):
+if not (re.search('0a: Single SNPs, Inexact Repeats or sequencing substitution errors, 19 found', result.decode())):
     successful = False
 
-if not (re.search('0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, 8 found', result.decode())):
+if not (re.search('0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, 1 found', result.decode())):
     successful = False
 
 if not (re.search('1: Alternative Splicing Events, 0 found', result.decode())):


=====================================
tests/integration_tests/kisspliceTwoSequencesTest.py
=====================================
@@ -10,10 +10,10 @@ print(result)
 
 # testing expected results
 successful = True
-if not (re.search('0a: Single SNPs, Inexact Repeats or sequencing substitution errors, 81 found', result.decode())):
+if not (re.search('0a: Single SNPs, Inexact Repeats or sequencing substitution errors, 69 found', result.decode())):
     successful = False
 
-if not (re.search('0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, 17 found', result.decode())):
+if not (re.search('0b: Multiple SNPs, Inexact Repeats or sequencing substitution errors, 8 found', result.decode())):
     successful = False
 
 if not (re.search('1: Alternative Splicing Events, 1 found', result.decode())):


=====================================
thirdparty/kissreads/include/coherence_algorithm.h
=====================================
@@ -44,7 +44,7 @@
 #define COHERENCE_ALGORITHM_H_
 
 
-char read_coherent_generic (const int pwi, const char * fragment, const char * read, const int subst_allowed);
+char read_coherent_generic (const int pwi, const char * fragment, const char * read, const int subst_allowed, const int modIndel);
 char read_coherent_SNP(const int pwi, const char * fragment, const char * read, const int subst_allowed);
 
 #endif /* COHERENCE_ALGORITHM_H_ */


=====================================
thirdparty/kissreads/src/coherent_algorithm.cpp
=====================================
@@ -54,7 +54,7 @@
  *  Tests if the overlapping part between read and fragment do not have more than <code>subst_allowed</code> substitions
  *  returns 1 if true between read and fragment, 0 else
  */
-char read_coherent_generic(const int pwi, const char * fragment, const char * read, const int subst_allowed){
+char read_coherent_generic(const int pwi, const char * fragment, const char * read, const int subst_allowed, const int modIndel){
     
 //    printf("read coherent, subst_allowed = %d\n", subst_allowed);
 	int substitution_seen=0; // number of seen substitutions for now
@@ -86,14 +86,15 @@ char read_coherent_generic(const int pwi, const char * fragment, const char * re
 
 	}
 
-
 	// walk the read and the fragment together, detecting substitutions.
 	// stop if the number of substitution is too high
 	while(fragment[pos_on_fragment]!='\0' && read[pos_on_read]!='\0'){
 		//if(fragment[pos_on_fragment]!=read[pos_on_read]) && fragment[pos_on_fragment]!='*') {// one subsitution
-		if(fragment[pos_on_fragment]!=read[pos_on_read] && fragment[pos_on_fragment]!='*' && fragment[pos_on_fragment]!='?' && fragment[pos_on_fragment]!='N'){ // one subsitution
+		if(fragment[pos_on_fragment]!=read[pos_on_read] && fragment[pos_on_fragment]!='*' && fragment[pos_on_fragment]!='?' && fragment[pos_on_fragment]!='N' && fragment[pos_on_fragment]!='I'){ // one subsitution
 			substitution_seen++;
 			if(substitution_seen>subst_allowed) break; // too much subsitutions
+		} else if(modIndel==1 && fragment[pos_on_fragment]!=read[pos_on_read] && fragment[pos_on_fragment]=='I') {
+			pos_on_read--;
 		}
 		pos_on_fragment++;
 		pos_on_read++;


=====================================
thirdparty/kissreads/src/extension_algorithm.cpp
=====================================
@@ -483,10 +483,16 @@ float read_coherence (gzFile reads_file,
                         
      
                         read_coherence=0;
-                        if(no_subsutitution_on_central_position)
+                        if(no_subsutitution_on_central_position){
                             read_coherence = read_coherent_SNP(pwi, starter, read, subst_allowed);
-                        else
-                            read_coherence = read_coherent_generic(pwi, starter, read, subst_allowed);
+                        }
+                        else {
+                            read_coherence = read_coherent_generic(pwi, starter, read, subst_allowed, 0); // Try alignment with 'I' as deletion on fragment
+                            if(read_coherence != 1) {
+                                read_coherence = read_coherent_generic(pwi, starter, read, subst_allowed, 1); // Try alignment with 'I' as insertion on fragment
+                            }
+                        }
+                            
                         
                         
                         if(read_coherence == 1){ // tuple read starter position is read coherent


=====================================
utils/cmake/cmake_uninstall.cmake.in deleted
=====================================
@@ -1,22 +0,0 @@
-if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
-    message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
-endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
-
-file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
-string(REGEX REPLACE "\n" ";" files "${files}")
-list(REVERSE files)
-foreach (file ${files})
-    message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"")
-    if (EXISTS "$ENV{DESTDIR}${file}")
-        execute_process(
-            COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}"
-            OUTPUT_VARIABLE rm_out
-            RESULT_VARIABLE rm_retval
-        )
-        if(NOT ${rm_retval} EQUAL 0)
-            message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"")
-        endif (NOT ${rm_retval} EQUAL 0)
-    else (EXISTS "$ENV{DESTDIR}${file}")
-        message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.")
-    endif (EXISTS "$ENV{DESTDIR}${file}")
-endforeach(file)



View it on GitLab: https://salsa.debian.org/med-team/kissplice/-/compare/6ba8b768222490f02a6b81560e6e7d3ec7cc9833...9a0a1a18d2b320f068af0ff2175e9736a2f90a7d

-- 
View it on GitLab: https://salsa.debian.org/med-team/kissplice/-/compare/6ba8b768222490f02a6b81560e6e7d3ec7cc9833...9a0a1a18d2b320f068af0ff2175e9736a2f90a7d
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20220630/5fa82d3a/attachment-0001.htm>


More information about the debian-med-commit mailing list