[med-svn] [Git][med-team/milib][upstream] New upstream version 1.13

Steffen Möller gitlab at salsa.debian.org
Sat Jun 13 20:42:40 BST 2020



Steffen Möller pushed to branch upstream at Debian Med / milib


Commits:
03ca7e47 by Steffen Moeller at 2020-06-13T21:31:55+02:00
New upstream version 1.13
- - - - -


26 changed files:

- .gitignore
- CHANGELOG
- init.sh
- pom.xml
- src/main/java/com/milaboratory/cli/ACommandWithSmartOverwriteWithSingleInput.java
- src/main/java/com/milaboratory/core/Range.java
- src/main/java/com/milaboratory/core/alignment/Alignment.java
- + src/main/java/com/milaboratory/core/alignment/AlignmentElement.java
- + src/main/java/com/milaboratory/core/alignment/AlignmentElementType.java
- src/main/java/com/milaboratory/core/alignment/kaligner1/KAlignerParameters.java
- src/main/java/com/milaboratory/core/sequence/AminoAcidSequence.java
- src/main/java/com/milaboratory/core/sequence/GeneticCode.java
- src/main/java/com/milaboratory/core/sequence/NucleotideSequence.java
- src/main/java/com/milaboratory/core/sequence/SequencesUtils.java
- src/main/java/com/milaboratory/core/sequence/Wildcard.java
- src/main/java/com/milaboratory/core/sequence/quality/QualityTrimmer.java
- src/main/java/com/milaboratory/core/sequence/quality/ReadTrimmerReport.java
- src/main/java/com/milaboratory/util/VersionInfo.java
- src/main/resources/parameters/kaligner_parameters.json → src/main/resources/parameters_resource/kaligner_parameters.json
- src/test/java/com/milaboratory/core/alignment/AlignmentTest.java
- src/test/java/com/milaboratory/core/motif/MotifUtilsTest.java
- + src/test/java/com/milaboratory/core/sequence/GeneticCodeTest.java
- src/test/java/com/milaboratory/core/sequence/NucleotideSequencesTest.java
- src/test/java/com/milaboratory/core/sequence/SequencesUtilsTest.java
- + src/test/java/com/milaboratory/core/tree/PrimerGenerator.java
- src/test/java/com/milaboratory/util/VersionInfoTest.java


Changes:

=====================================
.gitignore
=====================================
@@ -22,3 +22,4 @@ release.properties
 out
 /blast
 src/test/resources/big
+benchmark-data


=====================================
CHANGELOG
=====================================
@@ -1,4 +1,13 @@
 
+MiLib 1.13 (15 Apr 2020)
+========================
+
+-- Fix quality trimming report json
+-- Added CIGAR string for alignments
+-- Implementation of barcodes generator in `SequencesUtils`
+-- Upgrade to picocli 3
+
+
 MiLib 1.12 (19 Nov 2019)
 ========================
 


=====================================
init.sh
=====================================
@@ -61,6 +61,6 @@ set -e
 if [[ ! -f src/test/resources/big/16SMicrobial.nsq ]]
 then
     mkdir -p ${scriptDir}/src/test/resources/big
-    (cd ${scriptDir}/src/test/resources/big; curl https://ftp.ncbi.nlm.nih.gov/blast/db/16SMicrobial.tar.gz 2> /dev/null | tar -xzv)
+    (cd ${scriptDir}/src/test/resources/big; curl https://ftp.ncbi.nlm.nih.gov/blast/db/v4/16SMicrobial_v4.tar.gz  2> /dev/null | tar -xzv)
 fi
 


=====================================
pom.xml
=====================================
@@ -19,7 +19,7 @@
 
     <groupId>com.milaboratory</groupId>
     <artifactId>milib</artifactId>
-    <version>1.12</version>
+    <version>1.13</version>
     <packaging>jar</packaging>
     <name>MiLib</name>
     <url>https://milaboratory.com/</url>
@@ -52,7 +52,7 @@
         <dependency>
             <groupId>com.fasterxml.jackson.core</groupId>
             <artifactId>jackson-databind</artifactId>
-            <version>2.9.10.1</version>
+            <version>2.9.10.3</version>
         </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
@@ -78,7 +78,7 @@
         <dependency>
             <groupId>info.picocli</groupId>
             <artifactId>picocli</artifactId>
-            <version>3.9.6</version>
+            <version>4.1.2</version>
             <optional>true</optional>
         </dependency>
         <dependency>
@@ -89,13 +89,13 @@
         <dependency>
             <groupId>junit</groupId>
             <artifactId>junit</artifactId>
-            <version>4.10</version>
+            <version>4.12</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-all</artifactId>
-            <version>1.9.5</version>
+            <version>1.10.19</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
@@ -168,7 +168,7 @@
                     <plugin>
                         <groupId>org.apache.maven.plugins</groupId>
                         <artifactId>maven-source-plugin</artifactId>
-                        <version>3.1.0</version>
+                        <version>3.2.0</version>
                         <executions>
                             <execution>
                                 <id>attach-sources</id>
@@ -220,26 +220,18 @@
     <build>
         <plugins>
             <plugin>
-                <!-- Workaround maven not being able to set a property conditionally -->
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-antrun-plugin</artifactId>
-                <version>1.8</version>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>build-helper-maven-plugin</artifactId>
+                <version>3.1.0</version>
                 <executions>
                     <execution>
+                        <id>init-build-properties</id>
                         <phase>validate</phase>
                         <goals>
-                            <goal>run</goal>
+                            <goal>hostname</goal>
                         </goals>
                         <configuration>
-                            <exportAntProperties>true</exportAntProperties>
-                            <target>
-                                <exec executable="hostname" failonerror="false" outputproperty="hostname"/>
-                                <condition property="hostname" value="unknown">
-                                    <not>
-                                        <isset property="hostname"/>
-                                    </not>
-                                </condition>
-                            </target>
+                            <hostnameProperty>hostname</hostnameProperty>
                         </configuration>
                     </execution>
                 </executions>
@@ -284,7 +276,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-jar-plugin</artifactId>
-                <version>3.1.1</version>
+                <version>3.2.0</version>
                 <executions>
                     <execution>
                         <goals>
@@ -297,7 +289,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-surefire-plugin</artifactId>
-                <version>2.22.1</version>
+                <version>2.22.2</version>
                 <configuration>
                     <!-- Travis build workaround -->
                     <argLine>-Xms1024m -Xmx2048m</argLine>
@@ -307,7 +299,7 @@
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.8.0</version>
+                <version>3.8.1</version>
                 <configuration>
                     <source>1.8</source>
                     <target>1.8</target>


=====================================
src/main/java/com/milaboratory/cli/ACommandWithSmartOverwriteWithSingleInput.java
=====================================
@@ -26,11 +26,18 @@ public abstract class ACommandWithSmartOverwriteWithSingleInput extends ACommand
         super(appName, binaryFileInfoExtractor, pipelineConfigurationReader);
     }
 
-    @Parameters(index = "0", description = "input file")
     public String in;
+    public String out;
+
+    @Parameters(index = "0", description = "input file")
+    public void setIn(String in) {
+        this.in = in;
+    }
 
     @Parameters(index = "1", description = "output file")
-    public String out;
+    public void setOut(String out) {
+        this.out = out;
+    }
 
     @Override
     public final List<String> getOutputFiles() {


=====================================
src/main/java/com/milaboratory/core/Range.java
=====================================
@@ -108,6 +108,26 @@ public final class Range implements java.io.Serializable, Comparable<Range> {
         return reversed ? -1 : +1;
     }
 
+    /**
+     * Returns new range object with updated upper value
+     *
+     * @param newUpper new upper value
+     * @return new range object with updated upper value
+     */
+    public Range setUpper(int newUpper) {
+        return new Range(lower, newUpper, reversed);
+    }
+
+    /**
+     * Returns new range object with updated lower value
+     *
+     * @param newLower new lower value
+     * @return new range object with updated lower value
+     */
+    public Range setLower(int newLower) {
+        return new Range(newLower, upper, reversed);
+    }
+
     /**
      * Return reversed range.
      *


=====================================
src/main/java/com/milaboratory/core/alignment/Alignment.java
=====================================
@@ -15,6 +15,8 @@
  */
 package com.milaboratory.core.alignment;
 
+import cc.redberry.pipe.CUtils;
+import cc.redberry.pipe.OutputPort;
 import com.milaboratory.core.Range;
 import com.milaboratory.core.io.binary.AlignmentSerializer;
 import com.milaboratory.core.mutations.Mutation;
@@ -84,7 +86,8 @@ public final class Alignment<S extends Sequence<S>> implements java.io.Serializa
             if (!mutations.isCompatibleWith(sequence1)) {
                 MutationsUtil.assertCompatibleWithSequence(sequence1, mutations.getRAWMutations());
                 throw new IllegalArgumentException("Not compatible mutations: muts: " + mutations + " range1: " + sequence1Range + " seq1: " + sequence1.getRange(sequence1Range));
-            } if (!sequence1Range.contains(mutations.getMutatedRange()))
+            }
+            if (!sequence1Range.contains(mutations.getMutatedRange()))
                 throw new IllegalArgumentException("Not compatible mutations range: muts: " + mutations + " range1: " + sequence1Range);
             if (sequence1Range.length() + mutations.getLengthDelta() != sequence2Range.length())
                 throw new IllegalArgumentException("Not compatible range2: muts: " + mutations + "muts delta:" + mutations.getLengthDelta() + " range1: " + sequence1Range + " range2: " + sequence2Range);
@@ -364,6 +367,29 @@ public final class Alignment<S extends Sequence<S>> implements java.io.Serializa
         return new Alignment<>(sequence1, mutations, sequence1Range, sequence2Range.move(offset), score);
     }
 
+    /**
+     * Returns iterator over alignments elements in terms of CIGAR string.
+     *
+     * @return iterator over alignments elements in terms of CIGAR string
+     */
+    public OutputPort<AlignmentElement> getAlignmentElements() {
+        return new AlignmentElementOP();
+    }
+
+    /**
+     * Returns CIGAR string representation of this alignment.
+     *
+     * @return CIGAR string representation of this alignment
+     */
+    public String getCigarString() {
+        StringBuilder sb = new StringBuilder();
+        for (AlignmentElement ae : CUtils.it(getAlignmentElements())) {
+            sb.append(ae.getCigarLength());
+            sb.append(ae.getType().cigarLetterUpper);
+        }
+        return sb.toString();
+    }
+
     @Override
     public String toString() {
         return getAlignmentHelper().toCompactString();
@@ -407,4 +433,81 @@ public final class Alignment<S extends Sequence<S>> implements java.io.Serializa
             return -2 - position;
         return position;
     }
+
+    private final class AlignmentElementOP implements OutputPort<AlignmentElement> {
+        int currentMutationIndex = -1;
+        int lastSeq1Position = sequence1Range.getFrom();
+        int lastSeq2Position = sequence2Range.getFrom();
+
+        AlignmentElementOP() {
+            if (sequence1Range.isReverse() || sequence2Range.isReverse())
+                throw new IllegalArgumentException();
+        }
+
+        @Override
+        public AlignmentElement take() {
+            int mutation;
+            boolean isMatch = true;
+            while (true) {
+                ++currentMutationIndex;
+
+                if (currentMutationIndex > mutations.size())
+                    return null;
+
+                if (currentMutationIndex == mutations.size()) {
+                    if (lastSeq1Position == sequence1Range.getTo()) {
+                        assert lastSeq2Position == sequence2Range.getTo();
+                        return null;
+                    }
+                    return new AlignmentElement(
+                            new Range(lastSeq1Position, sequence1Range.getTo()),
+                            new Range(lastSeq2Position, sequence2Range.getTo()),
+                            isMatch);
+                }
+
+                mutation = mutations.getMutation(currentMutationIndex);
+
+                if (isSubstitution(mutation))
+                    isMatch = false;
+
+                if (isInDel(mutation)) {
+                    int newSeq1Position = getPosition(mutation);
+                    int newSeq2Position;
+
+                    if (newSeq1Position != lastSeq1Position) {
+                        --currentMutationIndex; // This mutation will be inspected again on the next round
+                        newSeq2Position = lastSeq2Position + (newSeq1Position - lastSeq1Position);
+                    } else {
+                        // newSeq1Position == lastSeq1Position == getPosition(mutation)
+                        newSeq2Position = lastSeq2Position;
+                        if (isInsertion(mutation)) {
+                            ++newSeq2Position;
+                            while (currentMutationIndex + 1 < mutations.size() &&
+                                    isInsertion(mutation = mutations.getMutation(currentMutationIndex + 1)) &&
+                                    getPosition(mutation) == newSeq1Position) {
+                                ++currentMutationIndex;
+                                ++newSeq2Position;
+                            }
+                        } else {
+                            ++newSeq1Position;
+                            while (currentMutationIndex + 1 < mutations.size() &&
+                                    isDeletion(mutation = mutations.getMutation(currentMutationIndex + 1)) &&
+                                    getPosition(mutation) == newSeq1Position) {
+                                ++currentMutationIndex;
+                                ++newSeq1Position;
+                            }
+                        }
+                    }
+
+                    AlignmentElement ret = new AlignmentElement(
+                            new Range(lastSeq1Position, newSeq1Position),
+                            new Range(lastSeq2Position, newSeq2Position),
+                            isMatch);
+                    lastSeq1Position = newSeq1Position;
+                    lastSeq2Position = newSeq2Position;
+                    return ret;
+                }
+            }
+        }
+    }
 }


=====================================
src/main/java/com/milaboratory/core/alignment/AlignmentElement.java
=====================================
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2020 MiLaboratory, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.milaboratory.core.alignment;
+
+import com.milaboratory.core.Range;
+
+import java.util.Objects;
+
+/**
+ * Represents CIGAR string element, along with its positions in original alignment coordinates, and matching status
+ */
+public final class AlignmentElement {
+    private final Range referenceRange, queryRange;
+    private final boolean isMatch;
+
+    public AlignmentElement(Range referenceRange, Range queryRange, boolean isMatch) {
+        Objects.requireNonNull(referenceRange);
+        Objects.requireNonNull(queryRange);
+        if (referenceRange.length() > 0 && queryRange.length() > 0 && queryRange.length() != referenceRange.length())
+            throw new IllegalArgumentException();
+        this.referenceRange = referenceRange;
+        this.queryRange = queryRange;
+        this.isMatch = isMatch;
+    }
+
+    public AlignmentElementType getType() {
+        if (referenceRange.isEmpty())
+            return AlignmentElementType.Insertion;
+        if (queryRange.isEmpty())
+            return AlignmentElementType.Deletion;
+        return isMatch ? AlignmentElementType.Match : AlignmentElementType.Mismatch;
+    }
+
+    public int getCigarLength() {
+        return Math.max(referenceRange.length(), queryRange.length());
+    }
+
+    /**
+     * Returns CIGAR string for the element
+     *
+     * @return CIGAR string for the element
+     */
+    public String getCigarString() {
+        return "" + getCigarLength() + getType().cigarLetterUpper;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AlignmentElement)) return false;
+        AlignmentElement that = (AlignmentElement) o;
+        return isMatch == that.isMatch &&
+                referenceRange.equals(that.referenceRange) &&
+                queryRange.equals(that.queryRange);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(referenceRange, queryRange, isMatch);
+    }
+
+    @Override
+    public String toString() {
+        return "" + referenceRange + " " + getCigarLength() + getType().cigarLetter + " " + queryRange;
+    }
+}


=====================================
src/main/java/com/milaboratory/core/alignment/AlignmentElementType.java
=====================================
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2020 MiLaboratory, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.milaboratory.core.alignment;
+
+public enum AlignmentElementType {
+    Match('M'),
+    Mismatch('m'),
+    Insertion('I'),
+    Deletion('D');
+
+    public final char cigarLetter, cigarLetterUpper;
+
+    AlignmentElementType(char cigarLetter) {
+        this.cigarLetter = cigarLetter;
+        this.cigarLetterUpper = Character.toUpperCase(cigarLetter);
+    }
+}


=====================================
src/main/java/com/milaboratory/core/alignment/kaligner1/KAlignerParameters.java
=====================================
@@ -37,7 +37,7 @@ public final class KAlignerParameters implements Cloneable, java.io.Serializable
     static {
         Map<String, KAlignerParameters> map = null;
         try {
-            InputStream is = KAlignerParameters.class.getClassLoader().getResourceAsStream("parameters/kaligner_parameters.json");
+            InputStream is = KAlignerParameters.class.getClassLoader().getResourceAsStream("parameters_resource/kaligner_parameters.json");
             TypeReference<HashMap<String, KAlignerParameters>> typeRef
                     = new TypeReference<
                     HashMap<String, KAlignerParameters>


=====================================
src/main/java/com/milaboratory/core/sequence/AminoAcidSequence.java
=====================================
@@ -98,32 +98,6 @@ public final class AminoAcidSequence extends AbstractArraySequence<AminoAcidSequ
         return count;
     }
 
-    /**
-     * Extracts {@literal int} representation of triplet starting from specified position (see implementation for
-     * details).
-     *
-     * @param nSequence    nucleotide sequence
-     * @param tripletStart position of first nucleotide of triplet
-     * @return {@literal int} representation of triplet
-     */
-    public static int getTriplet(NucleotideSequence nSequence, int tripletStart) {
-        int triplet = (nSequence.codeAt(tripletStart) << 4) |
-                (nSequence.codeAt(tripletStart + 1) << 2) |
-                nSequence.codeAt(tripletStart + 2);
-        return triplet;
-    }
-
-    /**
-     * Returns amino acid encoded by triplet starting from specified position (in terms of standard genetic code)
-     *
-     * @param nSequence    nucleotide sequence
-     * @param tripletStart position of first nucleotide of triplet
-     * @return byte-code of encoded amino acid
-     */
-    public static byte getAminoAcid(NucleotideSequence nSequence, int tripletStart) {
-        return GeneticCode.getAminoAcid(getTriplet(nSequence, tripletStart));
-    }
-
     /**
      * Translate sequence in one of frames (-1, -2, -3 frames are not implemented, use {@link
      * NucleotideSequence#getReverseComplement()}) discarding all incomplete codons on both boundaries.


=====================================
src/main/java/com/milaboratory/core/sequence/GeneticCode.java
=====================================
@@ -15,18 +15,21 @@
  */
 package com.milaboratory.core.sequence;
 
+import java.util.Arrays;
+
 /**
  * Defines standard genetic code.
  */
 public final class GeneticCode {
-    private static byte[] code = null;
+    private static byte[] basicCode = null;
+    private static byte[] wildcardCode = null;
 
     static {
         char[] Base1 = "ttttttttttttttttccccccccccccccccaaaaaaaaaaaaaaaagggggggggggggggg".toCharArray();
         char[] Base2 = "ttttccccaaaaggggttttccccaaaaggggttttccccaaaaggggttttccccaaaagggg".toCharArray();
         char[] Base3 = "tcagtcagtcagtcagtcagtcagtcagtcagtcagtcagtcagtcagtcagtcagtcagtcag".toCharArray();
         char[] Amino = "FFLLSSSSYY**CC*WLLLLPPPPHHQQRRRRIIIMTTTTNNKKSSRRVVVVAAAADDEEGGGG".toCharArray();
-        code = new byte[Base1.length];
+        basicCode = new byte[Base1.length];
         int triplet;
         byte b0, b1, b2;
         for (int i = 0; i < Base1.length; ++i) {
@@ -34,15 +37,57 @@ public final class GeneticCode {
             b1 = NucleotideAlphabet.INSTANCE.symbolToCode(Base2[i]);
             b2 = NucleotideAlphabet.INSTANCE.symbolToCode(Base3[i]);
             triplet = (b0 << 4) | (b1 << 2) | b2;
-            code[triplet] = AminoAcidAlphabet.INSTANCE.symbolToCode(Amino[i]);
+            basicCode[triplet] = AminoAcidAlphabet.INSTANCE.symbolToCode(Amino[i]);
+        }
+
+        Base1 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTYYYMMM".toCharArray();
+        Base2 = "AAAAAAGGGGGGCCCCCCCCCCCCCCCTTTTTTTTAAAAAAGGGGGGGGGGGGGGGCCCCCCCCCCCCCCCTTTTTTTTTTTTTTTAAAAAAGGGGGGGGGGGGGGGCCCCCCCCCCCCCCCTTTTTTTTTTTTTTTAAAAAAGGGGGCCCCCCCCCCCCCCCTTTTTTRTTTGGG".toCharArray();
+        Base3 = "AGCTRYAGCTRYAGCTNRYSWKMBDHVAGCTYWMHAGCTRYAGCTNRYSWKMBDHVAGCTNRYSWKMBDHVAGCTNRYSWKMBDHVAGCTRYAGCTNRYSWKMBDHVAGCTNRYSWKMBDHVAGCTNRYSWKMBDHVAGCTRYAGCTYAGCTNRYSWKMBDHVAGCTRYAAGRAGR".toCharArray();
+        Amino = "KKNNKNRRSSRSTTTTTTTTTTTTTTTIMIIIIIIEEDDEDGGGGGGGGGGGGGGGAAAAAAAAAAAAAAAVVVVVVVVVVVVVVVQQHHQHRRRRRRRRRRRRRRRPPPPPPPPPPPPPPPLLLLLLLLLLLLLLL**YY*Y*WCCCSSSSSSSSSSSSSSSLLFFLF*LLLRRR".toCharArray();
+        wildcardCode = new byte[1 << 12];
+        Arrays.fill(wildcardCode, AminoAcidAlphabet.X);
+        for (int i = 0; i < Base1.length; ++i) {
+            b0 = NucleotideAlphabet.INSTANCE.symbolToCode(Base1[i]);
+            b1 = NucleotideAlphabet.INSTANCE.symbolToCode(Base2[i]);
+            b2 = NucleotideAlphabet.INSTANCE.symbolToCode(Base3[i]);
+            triplet = (b0 << 8) | (b1 << 4) | b2;
+            wildcardCode[triplet] = AminoAcidAlphabet.INSTANCE.symbolToCode(Amino[i]);
         }
     }
 
-    public static byte getAminoAcid(int triplet) {
-        return code[triplet];
+    public static byte getAminoAcid(byte n0, byte n1, byte n2) {
+        return getAminoAcid(n0 << 8 | n1 << 4 | n2);
+    }
+
+    static byte getAminoAcid(int triplet) {
+        return wildcardCode[triplet];
     }
 
     public static void translate(byte[] dest, int offsetInDest, NucleotideSequence sequence, int offsetInSeq, int seqLength) {
+        if (seqLength % 3 != 0)
+            throw new IllegalArgumentException("Only nucleotide sequences with size multiple " +
+                    "of three are supported (in-frame).");
+
+        int size = seqLength / 3;
+        int triplet;
+        for (int i = 0; i < size; i++) {
+            triplet = (sequence.codeAt(offsetInSeq + i * 3) << 8) |
+                    (sequence.codeAt(offsetInSeq + i * 3 + 1) << 4) |
+                    sequence.codeAt(offsetInSeq + i * 3 + 2);
+            dest[i + offsetInDest] = wildcardCode[triplet];
+        }
+    }
+
+
+    public static byte getBasicAminoAcid(byte n0, byte n1, byte n2) {
+        return getBasicAminoAcid(n0 << 4 | n1 << 2 | n2);
+    }
+
+    static byte getBasicAminoAcid(int triplet) {
+        return basicCode[triplet];
+    }
+
+    public static void basicTranslate(byte[] dest, int offsetInDest, NucleotideSequence sequence, int offsetInSeq, int seqLength) {
         if (seqLength % 3 != 0)
             throw new IllegalArgumentException("Only nucleotide sequences with size multiple " +
                     "of three are supported (in-frame).");
@@ -55,7 +100,7 @@ public final class GeneticCode {
             triplet = (sequence.codeAt(offsetInSeq + i * 3) << 4) |
                     (sequence.codeAt(offsetInSeq + i * 3 + 1) << 2) |
                     sequence.codeAt(offsetInSeq + i * 3 + 2);
-            dest[i + offsetInDest] = code[triplet];
+            dest[i + offsetInDest] = basicCode[triplet];
         }
     }
 }


=====================================
src/main/java/com/milaboratory/core/sequence/NucleotideSequence.java
=====================================
@@ -43,6 +43,87 @@ public final class NucleotideSequence extends AbstractArraySequence<NucleotideSe
      */
     public static final NucleotideSequence EMPTY = new NucleotideSequence("");
 
+    /**
+     * Single letter sequence A
+     */
+    public static final NucleotideSequence A = new NucleotideSequence(new byte[]{NucleotideAlphabet.A});
+
+    /**
+     * Single letter sequence T
+     */
+    public static final NucleotideSequence T = new NucleotideSequence(new byte[]{NucleotideAlphabet.T});
+
+    /**
+     * Single letter sequence G
+     */
+    public static final NucleotideSequence G = new NucleotideSequence(new byte[]{NucleotideAlphabet.G});
+
+    /**
+     * Single letter sequence C
+     */
+    public static final NucleotideSequence C = new NucleotideSequence(new byte[]{NucleotideAlphabet.C});
+
+    /**
+     * Single letter sequence R
+     */
+    public static final NucleotideSequence R = new NucleotideSequence(new byte[]{NucleotideAlphabet.R});
+
+    /**
+     * Single letter sequence Y
+     */
+    public static final NucleotideSequence Y = new NucleotideSequence(new byte[]{NucleotideAlphabet.Y});
+
+    /**
+     * Single letter sequence S
+     */
+    public static final NucleotideSequence S = new NucleotideSequence(new byte[]{NucleotideAlphabet.S});
+
+    /**
+     * Single letter sequence W
+     */
+    public static final NucleotideSequence W = new NucleotideSequence(new byte[]{NucleotideAlphabet.W});
+
+    /**
+     * Single letter sequence K
+     */
+    public static final NucleotideSequence K = new NucleotideSequence(new byte[]{NucleotideAlphabet.K});
+
+    /**
+     * Single letter sequence M
+     */
+    public static final NucleotideSequence M = new NucleotideSequence(new byte[]{NucleotideAlphabet.M});
+
+    /**
+     * Single letter sequence B
+     */
+    public static final NucleotideSequence B = new NucleotideSequence(new byte[]{NucleotideAlphabet.B});
+
+    /**
+     * Single letter sequence D
+     */
+    public static final NucleotideSequence D = new NucleotideSequence(new byte[]{NucleotideAlphabet.D});
+
+    /**
+     * Single letter sequence H
+     */
+    public static final NucleotideSequence H = new NucleotideSequence(new byte[]{NucleotideAlphabet.H});
+
+    /**
+     * Single letter sequence V
+     */
+    public static final NucleotideSequence V = new NucleotideSequence(new byte[]{NucleotideAlphabet.V});
+
+    /**
+     * Single letter sequence N
+     */
+    public static final NucleotideSequence N = new NucleotideSequence(new byte[]{NucleotideAlphabet.N});
+
+    static final NucleotideSequence[] ONE_LETTER_SEQUENCES = {A, G, C, T, N, R, Y, S, W, K, M, B, D, H, V};
+
+    static NucleotideSequence getOneLetterSequence(byte letter) {
+        return ONE_LETTER_SEQUENCES[letter];
+    }
+
     /**
      * Creates nucleotide sequence from its string representation (e.g. "ATCGG" or "atcgg").
      *
@@ -86,6 +167,11 @@ public final class NucleotideSequence extends AbstractArraySequence<NucleotideSe
         if (range.length() == 0)
             return EMPTY;
 
+        if (range.length() == 1)
+            return range.isReverse()
+                    ? getOneLetterSequence(NucleotideAlphabet.complementCode(data[range.getFrom()]))
+                    : getOneLetterSequence(data[range.getFrom()]);
+
         if (range.isReverse())
             return new NucleotideSequence(
                     transformToRC(data, range.getLower(), range.getUpper()), true);


=====================================
src/main/java/com/milaboratory/core/sequence/SequencesUtils.java
=====================================
@@ -15,6 +15,7 @@
  */
 package com.milaboratory.core.sequence;
 
+import com.milaboratory.core.Range;
 import com.milaboratory.util.Bit2Array;
 import com.milaboratory.util.HashFunctions;
 
@@ -161,4 +162,24 @@ public final class SequencesUtils {
             seq.append((byte) bar.get(i));
         return seq.createAndDestroy();
     }
+
+    /**
+     * Searches for the longest homopolymer
+     */
+    public static Range findLongestHomopolymer(Sequence<?> seq) {
+        Range longest = null;
+        int code = -1, hpLength = 0, maxLength = 0;
+        for (int j = 0; j < seq.size(); j++) {
+            if (code != seq.codeAt(j)) {
+                code = seq.codeAt(j);
+                hpLength = 0;
+            }
+            ++hpLength;
+            if (hpLength > maxLength) {
+                maxLength = hpLength;
+                longest = new Range(j - hpLength + 1, j + 1);
+            }
+        }
+        return longest;
+    }
 }
\ No newline at end of file


=====================================
src/main/java/com/milaboratory/core/sequence/Wildcard.java
=====================================
@@ -39,6 +39,10 @@ public final class Wildcard {
      * Set of codes in wildcard
      */
     final byte[] matchingCodes;
+    /**
+     * Set of basic codes matching current wildcard
+     */
+    final byte[] basicMatchingCodes;
     /**
      * Code representing this wildcard (e.g. code == codes[0] for pure letters)
      */
@@ -81,6 +85,7 @@ public final class Wildcard {
         this.code = code;
         this.matchingCodes = matchingCodes.clone();
         this.basicSize = (byte) numberOfBasicCodes;
+        this.basicMatchingCodes = Arrays.copyOf(matchingCodes, numberOfBasicCodes);
 
         // Sorting for binary search
         Arrays.sort(this.matchingCodes);


=====================================
src/main/java/com/milaboratory/core/sequence/quality/QualityTrimmer.java
=====================================
@@ -239,10 +239,17 @@ public final class QualityTrimmer {
                     direction, false, parameters));
 
             if (Math.abs(islandEnd - islandStart) >= parameters.getWindowSize())
-                if (direction == +1)
-                    ranges.add(new Range(islandStart + 1, islandEnd + 1, isReversed));
-                else
-                    ranges.add(0, new Range(islandEnd, islandStart, isReversed));
+                if (direction == +1) {
+                    if (!ranges.isEmpty() && ranges.get(ranges.size() - 1).getUpper() == islandStart + 1)
+                        ranges.set(ranges.size() - 1, ranges.get(ranges.size() - 1).setUpper(islandEnd + 1));
+                    else
+                        ranges.add(new Range(islandStart + 1, islandEnd + 1, isReversed));
+                } else {
+                    if (!ranges.isEmpty() && ranges.get(ranges.size() - 1).getLower() == islandStart)
+                        ranges.set(ranges.size() - 1, ranges.get(ranges.size() - 1).setLower(islandEnd));
+                    else
+                        ranges.add(0, new Range(islandEnd, islandStart, isReversed));
+                }
 
             from = islandEnd + direction;
         }


=====================================
src/main/java/com/milaboratory/core/sequence/quality/ReadTrimmerReport.java
=====================================
@@ -36,41 +36,73 @@ public class ReadTrimmerReport implements ReadTrimmerListener {
         return bySideEvents.get(0);
     }
 
-    @JsonProperty("r1LeftTrimmedEvents")
+    public void setR1LeftTrimmedEvents(long v) {
+        bySideEvents.set(0, v);
+    }
+
+    @JsonProperty("r1RightTrimmedEvents")
     public long getR1RightTrimmedEvents() {
         return bySideEvents.get(1);
     }
 
-    @JsonProperty("r2RightTrimmedEvents")
+    public void setR1RightTrimmedEvents(long v) {
+        bySideEvents.set(1, v);
+    }
+
+    @JsonProperty("r2LeftTrimmedEvents")
     public long getR2LeftTrimmedEvents() {
         return bySideEvents.get(2);
     }
 
+    public void setR2LeftTrimmedEvents(long v) {
+        bySideEvents.set(2, v);
+    }
+
     @JsonProperty("r2RightTrimmedEvents")
     public long getR2RightTrimmedEvents() {
         return bySideEvents.get(3);
     }
 
+    public void setR2RightTrimmedEvents(long v) {
+        bySideEvents.set(3, v);
+    }
+
     @JsonProperty("r1LeftTrimmedNucleotides")
     public long getR1LeftTrimmedNucleotides() {
         return bySideNucleotides.get(0);
     }
 
+    public void setR1LeftTrimmedNucleotides(long v) {
+        bySideNucleotides.set(0, v);
+    }
+
     @JsonProperty("r1RightTrimmedNucleotides")
     public long getR1RightTrimmedNucleotides() {
         return bySideNucleotides.get(1);
     }
 
+    public void setR1RightTrimmedNucleotides(long v) {
+        bySideNucleotides.set(1, v);
+    }
+
     @JsonProperty("r2LeftTrimmedNucleotides")
     public long getR2LeftTrimmedNucleotides() {
         return bySideNucleotides.get(2);
     }
 
+    public void setR2LeftTrimmedNucleotides(long v) {
+        bySideNucleotides.set(2, v);
+    }
+
     @JsonProperty("r2RightTrimmedNucleotides")
     public long getR2RightTrimmedNucleotides() {
         return bySideNucleotides.get(3);
     }
 
+    public void setR2RightTrimmedNucleotides(long v) {
+        bySideNucleotides.set(3, v);
+    }
+
     @Override
     public void onSequence(SequenceRead originalRead, int readIndex, Range range, boolean trimmed) {
         if (readIndex == 0)


=====================================
src/main/java/com/milaboratory/util/VersionInfo.java
=====================================
@@ -102,7 +102,7 @@ public class VersionInfo {
     }
 
     public static VersionInfo getVersionInfoForArtifact(String artifactId) {
-        return getVersionInfo("/" + artifactId + "-build.properties");
+        return getVersionInfo(className(artifactId), "/" + artifactId + "-build.properties");
     }
 
     static String longest(String s1, String s2) {
@@ -116,9 +116,22 @@ public class VersionInfo {
             return s2;
     }
 
-    static VersionInfo getVersionInfo(String resourceName) {
+    static String className(String resourceName) {
+        switch (resourceName) {
+            case "milib":
+                return "com.milaboratory.util.VersionInfo";
+            case "mixcr":
+                return "com.milaboratory.mixcr.util.MiXCRVersionInfo";
+            case "repseqio":
+                return "io.repseq.util.RepseqIOVersionInfo";
+            default:
+                return resourceName;
+        }
+    }
+
+    static VersionInfo getVersionInfo(String className, String resourceName) {
         Properties properties = new Properties();
-        try (InputStream is = VersionInfo.class.getResourceAsStream(resourceName)) {
+        try (InputStream is = Class.forName(className).getResourceAsStream(resourceName)) {
             properties.load(is);
         } catch (Exception ex) {
             return null;


=====================================
src/main/resources/parameters/kaligner_parameters.json → src/main/resources/parameters_resource/kaligner_parameters.json
=====================================


=====================================
src/test/java/com/milaboratory/core/alignment/AlignmentTest.java
=====================================
@@ -16,6 +16,7 @@
 package com.milaboratory.core.alignment;
 
 
+import cc.redberry.pipe.CUtils;
 import com.milaboratory.core.Range;
 import com.milaboratory.core.mutations.Mutations;
 import com.milaboratory.core.mutations.generator.GenericNucleotideMutationModel;
@@ -80,6 +81,62 @@ public class AlignmentTest {
                 helper.getSequence2PositionAt(helper.size() - 1));
     }
 
+    @Test
+    public void testAlignmentElements1() {
+        NucleotideSequence sequence1 = new NucleotideSequence("TACCGCCATGACCA");
+
+        // 2 ccGc-cat 8
+        // 0 cc-cTcat 6
+
+        Alignment<NucleotideSequence> alignment =
+                new Alignment<>(
+                        sequence1,
+                        Mutations.decode("DG4:I6T", NucleotideSequence.ALPHABET),
+                        new Range(2, 9), new Range(0, 7),
+                        0);
+        Assert.assertEquals("2M1D1M1I3M", alignment.getCigarString());
+
+        alignment = new Alignment<>(
+                sequence1,
+                Mutations.decode("DG4:DC5:I6T", NucleotideSequence.ALPHABET),
+                new Range(2, 9), new Range(0, 6),
+                0);
+        Assert.assertEquals("2M2D1I3M", alignment.getCigarString());
+
+        alignment = new Alignment<>(
+                sequence1,
+                Mutations.decode("DG4:SC5G:I6T", NucleotideSequence.ALPHABET),
+                new Range(2, 9), new Range(0, 7),
+                0);
+        Assert.assertEquals("2M1D1M1I3M", alignment.getCigarString());
+
+        alignment = new Alignment<>(
+                sequence1,
+                Mutations.decode("I2A:I2T:DG4:SC5G:I6T", NucleotideSequence.ALPHABET),
+                new Range(2, 9), new Range(0, 9),
+                0);
+        Assert.assertEquals("2I2M1D1M1I3M", alignment.getCigarString());
+
+        alignment = new Alignment<>(
+                sequence1,
+                Mutations.decode("I3A:I3T:DG4:SC5G:I6T", NucleotideSequence.ALPHABET),
+                new Range(2, 9), new Range(0, 9),
+                0);
+        Assert.assertEquals("1M2I1M1D1M1I3M", alignment.getCigarString());
+
+        alignment = new Alignment<>(
+                sequence1,
+                Mutations.decode("DG4:SC5G:I6T:I9A:I9T", NucleotideSequence.ALPHABET),
+                new Range(2, 9), new Range(0, 9),
+                0);
+        Assert.assertEquals("2M1D1M1I3M2I", alignment.getCigarString());
+
+        // System.out.println(alignment);
+        // for (AlignmentElement ae : CUtils.it(alignment.getAlignmentElements())) {
+        //     System.out.println(ae);
+        // }
+    }
+
     @Test
     public void testConvertPosition() throws Exception {
         Well19937c rand = new Well19937c();
@@ -163,9 +220,8 @@ public class AlignmentTest {
     @Test
     public void testSerialization1() throws Exception {
         NucleotideSequence sequence1 = new NucleotideSequence("TACCGCCATGACCA");
-        NucleotideSequence sequence2 = new NucleotideSequence("CCTCATCTCTT");
-        Alignment<NucleotideSequence> alignment = Aligner.alignLocal(LinearGapAlignmentScoring.getNucleotideBLASTScoring(),
-                sequence1, sequence2);
+        Mutations<NucleotideSequence> mutations = Mutations.decode("DG4SC5T", NucleotideSequence.ALPHABET);
+        Alignment<NucleotideSequence> alignment = new Alignment<>(sequence1, mutations, 0);
 
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         PrimitivO po = new PrimitivO(bos);


=====================================
src/test/java/com/milaboratory/core/motif/MotifUtilsTest.java
=====================================
@@ -16,7 +16,7 @@
 package com.milaboratory.core.motif;
 
 import com.milaboratory.core.sequence.NucleotideSequence;
-import junit.framework.Assert;
+import org.junit.Assert;
 import org.apache.commons.math3.random.RandomGenerator;
 import org.apache.commons.math3.random.Well19937c;
 import org.junit.Test;


=====================================
src/test/java/com/milaboratory/core/sequence/GeneticCodeTest.java
=====================================
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019 MiLaboratory, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.milaboratory.core.sequence;
+
+import gnu.trove.set.hash.TByteHashSet;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class GeneticCodeTest {
+    @Test
+    @Ignore
+    public void generateExtendedGeneticCode() {
+        StringBuilder
+                n1 = new StringBuilder(),
+                n2 = new StringBuilder(),
+                n3 = new StringBuilder(),
+                aa = new StringBuilder();
+
+        for (Wildcard iw : NucleotideSequence.ALPHABET.getAllWildcards())
+            for (Wildcard jw : NucleotideSequence.ALPHABET.getAllWildcards())
+                for (Wildcard kw : NucleotideSequence.ALPHABET.getAllWildcards()) {
+                    TByteHashSet aminoAcids = new TByteHashSet();
+                    for (byte i : iw.basicMatchingCodes)
+                        for (byte j : jw.basicMatchingCodes)
+                            for (byte k : kw.basicMatchingCodes)
+                                aminoAcids.add(GeneticCode.getBasicAminoAcid(i, j, k));
+                    if (aminoAcids.size() == 1) {
+                        System.out.println("" + iw.getSymbol() + jw.getSymbol() + kw.getSymbol() + " -> " + AminoAcidSequence.ALPHABET.codeToSymbol(aminoAcids.iterator().next()));
+                        n1.append(iw.getSymbol());
+                        n2.append(jw.getSymbol());
+                        n3.append(kw.getSymbol());
+                        aa.append(AminoAcidSequence.ALPHABET.codeToSymbol(aminoAcids.iterator().next()));
+                    }
+                }
+
+        System.out.println(n1);
+        System.out.println(n2);
+        System.out.println(n3);
+        System.out.println(aa);
+    }
+
+    @Test
+    public void test1() {
+        for (byte i = 0; i < 4; i++)
+            for (byte j = 0; j < 4; j++)
+                for (byte k = 0; k < 4; k++)
+                    Assert.assertEquals(GeneticCode.getAminoAcid(i, j, k), GeneticCode.getBasicAminoAcid(i, j, k));
+    }
+}
\ No newline at end of file


=====================================
src/test/java/com/milaboratory/core/sequence/NucleotideSequencesTest.java
=====================================
@@ -15,6 +15,7 @@
  */
 package com.milaboratory.core.sequence;
 
+import org.junit.Assert;
 import org.junit.Test;
 
 import static org.hamcrest.CoreMatchers.not;
@@ -101,4 +102,10 @@ public class NucleotideSequencesTest {
     public void testUnknownSymbol2() throws Exception {
         new NucleotideSequence(new char[]{'a', 'n', 'q'});
     }
+
+    @Test
+    public void testOneLetter() {
+        for (int i = 0; i < NucleotideSequence.ONE_LETTER_SEQUENCES.length; i++)
+            Assert.assertEquals(i, NucleotideSequence.ONE_LETTER_SEQUENCES[i].codeAt(0));
+    }
 }


=====================================
src/test/java/com/milaboratory/core/sequence/SequencesUtilsTest.java
=====================================
@@ -15,9 +15,9 @@
  */
 package com.milaboratory.core.sequence;
 
+import com.milaboratory.core.Range;
 import com.milaboratory.core.motif.Motif;
 import com.milaboratory.test.TestUtil;
-import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.Set;
@@ -77,4 +77,11 @@ public class SequencesUtilsTest {
         assertTrue(set.contains(AminoAcidSequence.ALPHABET));
         assertFalse(set.contains(NucleotideSequence.ALPHABET));
     }
+
+    @Test
+    public void testLongestHomopolymer1() {
+        assertEquals(new Range(0, 3), findLongestHomopolymer(new NucleotideSequence("AAAGACA")));
+        assertEquals(new Range(6 , 11), findLongestHomopolymer(new NucleotideSequence("AAAGACAAAAA")));
+        assertEquals(new Range(4, 10), findLongestHomopolymer(new NucleotideSequence("AAAGAAAAAACA")));
+    }
 }


=====================================
src/test/java/com/milaboratory/core/tree/PrimerGenerator.java
=====================================
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019 MiLaboratory, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.milaboratory.core.tree;
+
+import com.milaboratory.core.sequence.NucleotideSequence;
+import com.milaboratory.core.sequence.SequencesUtils;
+import com.milaboratory.test.TestUtil;
+import org.apache.commons.math3.random.RandomGenerator;
+import org.apache.commons.math3.random.Well19937c;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+import java.util.HashSet;
+import java.util.Set;
+
+public class PrimerGenerator {
+
+    @Test
+    @Ignore
+    public void generate() throws FileNotFoundException {
+        Well19937c rg = new Well19937c();
+
+        for (int length = 6; length <= 7; length++) {
+            for (int minDistance = 2; minDistance <= 4; minDistance++) {
+                for (int maxHomopolymer = 2; maxHomopolymer <= 5; maxHomopolymer++) {
+                    Set<NucleotideSequence> best = null;
+                    for (int i = 0; i < 100; i++) {
+                        TreeSearchParameters searchParameters;
+                        switch (minDistance) {
+                            case 2:
+                                searchParameters = TreeSearchParameters.TWO_MISMATCHES_OR_INDELS;
+                                break;
+                            case 3:
+                                searchParameters = TreeSearchParameters.THREE_MISMATCHES_OR_INDELS;
+                                break;
+                            case 4:
+                                searchParameters = TreeSearchParameters.FOUR_MISMATCHES_OR_INDELS;
+                                break;
+                            default:
+                                throw new IllegalArgumentException();
+                        }
+                        Set<NucleotideSequence> gen = generateSequences(rg,
+                                new HashSet<>(),
+                                searchParameters,
+                                length, maxHomopolymer, 1_000_00,
+                                false);
+                        if (best == null || gen.size() > best.size())
+                            best = gen;
+                    }
+                    System.out.println("Best ford length=" + length +
+                            " minDistance=" + minDistance +
+                            " maxHomo=" + maxHomopolymer +
+                            ": " + best.size());
+                    try (PrintStream ps = new PrintStream(
+                            "length" + length +
+                                    "_minDistance" + minDistance +
+                                    "_maxHomo" + maxHomopolymer + ".txt")) {
+                        for (NucleotideSequence seq : best)
+                            ps.println(seq);
+                    }
+                }
+            }
+        }
+
+
+        // System.out.println("4");
+        //
+        // for (NucleotideSequence seq : bestGen4)
+        //     System.out.println(seq);
+
+        // System.out.println("3");
+        //
+        // for (NucleotideSequence seq : best)
+        //     System.out.println(seq);
+    }
+
+    public Set<NucleotideSequence> generateSequences(RandomGenerator rg,
+                                                     Set<NucleotideSequence> exclude,
+                                                     TreeSearchParameters treeSearchParameters,
+                                                     int length,
+                                                     int maxHomopolymerLength,
+                                                     long attempts,
+                                                     boolean log) {
+        SequenceTreeMap<NucleotideSequence, Boolean> tree = new SequenceTreeMap<>(NucleotideSequence.ALPHABET);
+        for (NucleotideSequence seq : exclude)
+            tree.put(seq, true);
+
+        Set<NucleotideSequence> generated = new HashSet<>();
+        for (int i = 0; i < attempts; i++) {
+            NucleotideSequence newSeq = TestUtil.randomSequence(NucleotideSequence.ALPHABET, rg, length, length);
+            if (SequencesUtils.findLongestHomopolymer(newSeq).length() > maxHomopolymerLength)
+                continue;
+            Boolean searchResult = tree.getNeighborhoodIterator(newSeq, treeSearchParameters).next();
+            if (searchResult == null) {
+                generated.add(newSeq);
+                tree.put(newSeq, false);
+                if (log)
+                    System.out.println("Attempt = " + i + " generated = " + generated.size());
+            }
+        }
+
+        return generated;
+    }
+}


=====================================
src/test/java/com/milaboratory/util/VersionInfoTest.java
=====================================
@@ -21,14 +21,14 @@ import org.junit.Test;
 public class VersionInfoTest {
     @Test
     public void test1() throws Exception {
-        VersionInfo versionInfo = VersionInfo.getVersionInfo("/util/milib-build.properties");
+        VersionInfo versionInfo = VersionInfo.getVersionInfo("com.milaboratory.util.VersionInfo","/util/milib-build.properties");
         Assert.assertEquals("1.1.3-SNAPSHOT", versionInfo.version);
         Assert.assertEquals("6a235a64bf84b829f28b0ef3cfb03bd41d2e74eb", versionInfo.revision);
     }
 
     @Test
     public void test2() throws Exception {
-        VersionInfo versionInfo = VersionInfo.getVersionInfo("/util/asdafas");
+        VersionInfo versionInfo = VersionInfo.getVersionInfo("","/util/asdafas");
         Assert.assertNull(versionInfo);
     }
 



View it on GitLab: https://salsa.debian.org/med-team/milib/-/commit/03ca7e47bdefe055d227cde5e123bc3d5293bc66

-- 
View it on GitLab: https://salsa.debian.org/med-team/milib/-/commit/03ca7e47bdefe055d227cde5e123bc3d5293bc66
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/20200613/91496131/attachment-0001.html>


More information about the debian-med-commit mailing list