From 0c8bbf79258b4a82020d439e51245d3084f82ba1 Mon Sep 17 00:00:00 2001 From: IvanTsers Date: Thu, 25 Jun 2026 10:47:56 +0200 Subject: [PATCH 1/5] added r8.txt to makeRes.sh --- fur/makeRes.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/fur/makeRes.sh b/fur/makeRes.sh index 839a1e5..1ad1cf3 100644 --- a/fur/makeRes.sh +++ b/fur/makeRes.sh @@ -5,3 +5,4 @@ ./fur -d test.db -M &> r5.txt ./fur -d masked.db &> r6.txt ./fur -d masked.db -M &> r7.txt +./fur -d testPartial.db -f 0.8 &> r8.txt From b65523ee62f39a66815b2abca8f870af5115f3f5 Mon Sep 17 00:00:00 2001 From: IvanTsers Date: Thu, 25 Jun 2026 14:00:51 +0200 Subject: [PATCH 2/5] added word size to Subtraction 2 --- fur/fur.go | 61 ++++++++++++++++------- fur/fur.org | 129 ++++++++++++++++++++++++++++++++---------------- fur/fur_test.go | 5 ++ fur/makeRes.sh | 2 + fur/r10.txt | 22 +++++++++ fur/r9.txt | 21 ++++++++ 6 files changed, 178 insertions(+), 62 deletions(-) create mode 100644 fur/r10.txt create mode 100644 fur/r9.txt diff --git a/fur/fur.go b/fur/fur.go index d473db5..b801307 100644 --- a/fur/fur.go +++ b/fur/fur.go @@ -61,6 +61,7 @@ func main() { optMM := flag.Bool("M", false, "activate masking (recommended for mammalian genomes)") optN := flag.Int("n", 100, "number of nucleotides in region") + optWW := flag.Int("W", 0, "word length for Blast") flag.Parse() if *optV { util.PrintInfo("fur") @@ -104,6 +105,18 @@ func main() { log.Fatalf("can't use %f as a sensitivity threshold\n" + "please use a positive value not exceeding 1", *optF) } + wProvided := false + flag.Visit(func(f *flag.Flag) { + if f.Name == "W" { + wProvided = true + } + }) + + if wProvided && *optWW < 4 { + m := "couldn't set the Blast word size to %d: " + + "please use a word size of >= 4" + log.Fatalf(m, *optWW) + } regions := make([]*fasta.Sequence, 0) rw := tabwriter.NewWriter(os.Stderr, 0, 0, 2, ' ', tabwriter.AlignRight) @@ -387,10 +400,11 @@ func main() { if len(regions) > 0 { cmds := make([]*exec.Cmd, 0) da := *optD + "/n" - th := *optT - ev := *optE + th := strconv.Itoa(*optT) + ev := fmt.Sprintf("%g", *optE) ta := "megablast" ma := "" + ws := "" if *optMM { cmd := exec.Command("blastdbcmd", "-info", "-db", *optD + "/n") out, err := cmd.CombinedOutput() @@ -409,32 +423,41 @@ func main() { fmt.Fprintf(os.Stderr, m) } } + if wProvided { + ws = strconv.Itoa(*optWW) + } of := "6 qaccver qstart qend" - tm := "blastn -db %s -num_threads %d " - tm += "-evalue %g -task %s " - if *optMM && ma != "" { - tm += "-db_soft_mask %s " + args := []string{ + "-db", da, + "-num_threads", th, + "-evalue", ev, + "-task", ta, } - tm += "-outfmt " - as := fmt.Sprintf(tm, da, th, ev, ta) if *optMM && ma != "" { - as = fmt.Sprintf(tm, da, th, ev, ta, ma) + args = append(args, "-db_soft_mask", ma) } - args := strings.Fields(as) - args = append(args, of) - cmd := exec.Command("blastn") - cmd.Args = args + if wProvided { + args = append(args, "-word_size", ws) + } + args = append(args, "-outfmt", of) + cmd := exec.Command("blastn", args...) cmds = append(cmds, cmd) if !*optM { ta = "blastn" - as := fmt.Sprintf(tm, da, th, ev, ta) + args := []string{ + "-db", da, + "-num_threads", th, + "-evalue", ev, + "-task", ta, + } if *optMM && ma != "" { - as = fmt.Sprintf(tm, da, th, ev, ta, ma) + args = append(args, "-db_soft_mask", ma) + } + if wProvided { + args = append(args, "-word_size", ws) } - args = strings.Fields(as) - args = append(args, of) - cmd = exec.Command("blastn") - cmd.Args = args + args = append(args, "-outfmt", of) + cmd := exec.Command("blastn", args...) cmds = append(cmds, cmd) } for _, cmd := range cmds { diff --git a/fur/fur.org b/fur/fur.org index 944568b..85cb7ac 100644 --- a/fur/fur.org +++ b/fur/fur.org @@ -120,9 +120,10 @@ The user can also print the result of Step~(\ref{eq:fur2}) and exit. #+begin_export latex Step~(\ref{eq:fur3}) is implemented in \ty{blastn}, where we expose the E-value, the number of threads, a switch to megablast mode instead -of the default blastn mode, and a switch for running with masking. We -also set the minimum length of the final regions at this point. The -number of threads is initialized to the number of CPUs. +of the default blastn mode, a switch for running with masking, and the +word length. We also set the minimum length of the final regions at +this point. The number of threads is initialized to the number of +CPUs. #+end_export #+begin_src go <>= optE := flag.Float64("e", 1e-5, "E-value for Blast") @@ -134,6 +135,7 @@ number of threads is initialized to the number of CPUs. optMM := flag.Bool("M", false, "activate masking (recommended for mammalian genomes)") optN := flag.Int("n", 100, "number of nucleotides in region") + optWW := flag.Int("W", 0, "word length for Blast") #+end_src #+begin_export latex We import \ty{runtime}. @@ -144,8 +146,8 @@ We import \ty{runtime}. #+begin_export latex We parse the options and first respond to the version request, \ty{-v}, as this might stop the program. We also respond to the -database name, \ty{-d}, the number of threads, \ty{-t}, and the -sensitivity threshold, \ty{-f}. +database name, \ty{-d}, the number of threads, \ty{-t}, the +sensitivity threshold, \ty{-f}, and the Blast word length, \ty{-W}. #+end_export #+begin_src go <>= flag.Parse() @@ -153,6 +155,7 @@ sensitivity threshold, \ty{-f}. //<> //<> //<> + //<> #+end_src #+begin_export latex We import \ty{fmt}. @@ -255,6 +258,27 @@ sets $f$ outside the range, we notify the user and exit. } #+end_src #+begin_export latex +The user can override task-specific Blast word sizes by explicitly +setting it with the \ty{-W} flag. We visit all parsed flags and check +if \ty{-W} is provided. Nucleotide Blast allows the word size of 4 and +greater, so we check the provided value. If it is invalid, we politely +ask the user to set a valid one. +#+end_export +#+begin_src go <>= + wProvided := false + flag.Visit(func(f *flag.Flag) { + if f.Name == "W" { + wProvided = true + } + }) + + if wProvided && *optWW < 4 { + m := "couldn't set the Blast word size to %d: " + + "please use a word size of >= 4" + log.Fatalf(m, *optWW) + } +#+end_src +#+begin_export latex We get the program version from the utility function \ty{Version} and also store it as a floating point number. #+end_export @@ -1129,36 +1153,41 @@ We import \ty{exec}. "os/exec" #+end_src #+begin_export latex -We construct the Blast options, construct a template for the Blast -commands, and construct the megablast command. Then we construct the -blastn command, unless the user opted for megablast only. +We construct the Blast options and construct the megablast +command. Then we construct the blastn command, unless the user opted +for megablast only. #+end_export #+begin_src go <>= //<> - //<> //<> if !*optM { //<> } #+end_src #+begin_export latex -We set six options in Blast, the values of which we first need to +We set seven options in Blast, the values of which we first need to construct. The options are the database path, the number of threads, -the E-value, the task, the masking algorithm, and the output -format. The masking algorithm is initialized as an empty string which -we fill if the user requested masking. As output format we set the -query accession, start, and end---the coordinates for homology masking -later on. +the E-value, the task, the masking algorithm, the word size, and the +output format. The masking algorithm is initialized as an empty +string, which we fill if the user requested masking. The word size is +initialized as an empty string, which we set if the user has provided +the word length. As output format we set the query accession, start, +and end---the coordinates for homology masking later on. We convert +all numerical options values to strings. #+end_export #+begin_src go <>= da := *optD + "/n" - th := *optT - ev := *optE + th := strconv.Itoa(*optT) + ev := fmt.Sprintf("%g", *optE) ta := "megablast" ma := "" + ws := "" if *optMM { //<> } + if wProvided { + ws = strconv.Itoa(*optWW) + } of := "6 qaccver qstart qend" #+end_src #+begin_export latex @@ -1210,31 +1239,24 @@ database. In that case we should warn the user. } #+end_src #+begin_export latex -The Blast template has space for five Blast options, six with -masking. The output format is a composite string, so we append it -later to the arguments slice. -#+end_export -#+begin_src go <>= - tm := "blastn -db %s -num_threads %d " - tm += "-evalue %g -task %s " - if *optMM && ma != "" { - tm += "-db_soft_mask %s " - } - tm += "-outfmt " -#+end_src -#+begin_export latex We generate the arguments for the Blast command, append the output format, set the arguments, and store the command. #+end_export #+begin_src go <>= - as := fmt.Sprintf(tm, da, th, ev, ta) + args := []string{ + "-db", da, + "-num_threads", th, + "-evalue", ev, + "-task", ta, + } if *optMM && ma != "" { - as = fmt.Sprintf(tm, da, th, ev, ta, ma) + args = append(args, "-db_soft_mask", ma) } - args := strings.Fields(as) - args = append(args, of) - cmd := exec.Command("blastn") - cmd.Args = args + if wProvided { + args = append(args, "-word_size", ws) + } + args = append(args, "-outfmt", of) + cmd := exec.Command("blastn", args...) cmds = append(cmds, cmd) #+end_src #+begin_export latex @@ -1242,14 +1264,20 @@ We repeat this for the blastn command, only with a different task. #+end_export #+begin_src go <>= ta = "blastn" - as := fmt.Sprintf(tm, da, th, ev, ta) + args := []string{ + "-db", da, + "-num_threads", th, + "-evalue", ev, + "-task", ta, + } if *optMM && ma != "" { - as = fmt.Sprintf(tm, da, th, ev, ta, ma) + args = append(args, "-db_soft_mask", ma) } - args = strings.Fields(as) - args = append(args, of) - cmd = exec.Command("blastn") - cmd.Args = args + if wProvided { + args = append(args, "-word_size", ws) + } + args = append(args, "-outfmt", of) + cmd := exec.Command("blastn", args...) cmds = append(cmds, cmd) #+end_src #+begin_export latex @@ -1531,12 +1559,13 @@ We import \ty{exec}. #+end_src #+begin_export latex We construct a set of tests without repeat masking, one with repeat -masking, and one with partial intersection. +masking, one with partial intersection, and two tests with word size. #+end_export #+begin_src go <>= //<> //<> //<> + //<> #+end_src #+begin_export latex We construct four tests without repeat masking. The first runs with @@ -1593,6 +1622,20 @@ five targets lacks the marker. We set the sensitivity threshold to tests = append(tests, test) #+end_src #+begin_export latex +We run \ty{fur} in megablast and blastn modes setting the word size to +4 and 36, respectively. In the first case, we adjust megablast to be +actually more stringent than blastn, and in the second case, we adjust +both megablast and blastn to be less stringent than the default +megablast. +#+end_export +#+begin_src go <>= + d = "test.db" + test = exec.Command("./fur", "-d", d, "-m", "-W", "4") + tests = append(tests, test) + test = exec.Command("./fur", "-d", d, "-W", "36") + tests = append(tests, test) +#+end_src +#+begin_export latex For each test we compare the result we get with the result we want, which is contained in files \ty{r1.txt}, \ty{r2.txt}, and so on. #+end_export diff --git a/fur/fur_test.go b/fur/fur_test.go index ab1b4ac..fb7af46 100644 --- a/fur/fur_test.go +++ b/fur/fur_test.go @@ -32,6 +32,11 @@ func TestFur(t *testing.T) { d = "testPartial.db" test = exec.Command("./fur", "-d", d, "-f", "0.8") tests = append(tests, test) + d = "test.db" + test = exec.Command("./fur", "-d", d, "-m", "-W", "4") + tests = append(tests, test) + test = exec.Command("./fur", "-d", d, "-W", "36") + tests = append(tests, test) for i, test := range tests { get, err := test.CombinedOutput() if err != nil { diff --git a/fur/makeRes.sh b/fur/makeRes.sh index 1ad1cf3..02723bd 100644 --- a/fur/makeRes.sh +++ b/fur/makeRes.sh @@ -6,3 +6,5 @@ ./fur -d masked.db &> r6.txt ./fur -d masked.db -M &> r7.txt ./fur -d testPartial.db -f 0.8 &> r8.txt +./fur -d test.db -m -W 4 &> r9.txt +./fur -d test.db -W 36 &> r10.txt diff --git a/fur/r10.txt b/fur/r10.txt new file mode 100644 index 0000000..a3857be --- /dev/null +++ b/fur/r10.txt @@ -0,0 +1,22 @@ + Step Sequences Length Ns + ------------- --------- ------ -- + Subtraction_1 1 1052 0 + Intersection 1 1051 5 + Subtraction_2 1 1051 5 +>t1_1 +GTTGATCGCAATCCTGCATCGACTGTGGCTCTGGGAGGTCCGACCAAGTGCTACTGCGGCATTAGGGCGC +AGCGATCCCCTCATGCGGCAATGAGGTCATATAATACTACGTTATATTTTGAAGATGAATAANGACTTCC +GTCAAATCAGCAGTCAGTAAATATTGAACCANTTTAAACCGGCGCCGCGCGGTAACCTAGGACTCTCCAT +GCTAAACCCCAGCGCCGTAGAGTGATCCCTAAGCTAATCAAACCGTTCGTTTGTGCCCTATGTCAGGAAG +CACGACGTCGGTAATCGAGTCACCTGTGCCCCGTTTATCCCAGATGTTAGGATAACATTCGTACGACGTA +AAGTTAGACATGGCGCAGGNCACGCCTGCAAACGTTGTTCCCCTTCATGTTTGCACTACTCTTAGTCGCC +CTCCCTTTTCCGCGTCTCCAGGGCGCGCCCCTCAGGACTGTCTTTGCGGTGGCTCGCTCCAGCTGGCTGA +GCATTATAAGAGGATACTATTAATTAGTCTTGTTAACTCGCGGTTTGCGAAAGCGTATGTAGTCTGTTGT +TTGCCGGGGTATTTGAGATAATCACACCTGGGCTCTAGTGGCTCTCAGCAAAATCGGGATTGGCCGNGAA +AAACCTAACTAACTTCTGTTTGCCGCACGTCTATTAATCAACCGCATATCGGGGACCCAGGATTCTCCTC +AGACAGCAATAACGCGGACGGAGAGATCTAATGGCAGTGCCTCGGGCTCTTGGAAAGTGAGTCAACTTNT +TCCCGACAAATTAAACAGGTGGCCACCGTAACTGCGGCATCTACGGAACATGACTCGACACTAGTGACAG +ATGGACTGCACGACTTCGCGGCGGTATCGGGCTATTTTTTACGTACGTTAAACGGAAAGGGACTCTAGAG +CCGTGTCAGGCTGACTGACTTCCCGAGCCAACGCGCTTAACCGAACGCCACAGACAAACTGTTAAGGCTT +GCATGGAGTATACATGGACAAGGATCCCAACTGAATCTGAAAGCAAGGTAATTCTAGGACGTAGAGCCGT +G diff --git a/fur/r9.txt b/fur/r9.txt new file mode 100644 index 0000000..e7db604 --- /dev/null +++ b/fur/r9.txt @@ -0,0 +1,21 @@ + Step Sequences Length Ns + ------------- --------- ------ -- + Subtraction_1 1 1052 0 + Intersection 1 1051 5 + Subtraction_2 1 1000 5 +>t1_1 +TGTGGCTCTGGGAGGTCCGACCAAGTGCTACTGCGGCATTAGGGCGCAGCGATCCCCTCATGCGGCAATG +AGGTCATATAATACTACGTTATATTTTGAAGATGAATAANGACTTCCGTCAAATCAGCAGTCAGTAAATA +TTGAACCANTTTAAACCGGCGCCGCGCGGTAACCTAGGACTCTCCATGCTAAACCCCAGCGCCGTAGAGT +GATCCCTAAGCTAATCAAACCGTTCGTTTGTGCCCTATGTCAGGAAGCACGACGTCGGTAATCGAGTCAC +CTGTGCCCCGTTTATCCCAGATGTTAGGATAACATTCGTACGACGTAAAGTTAGACATGGCGCAGGNCAC +GCCTGCAAACGTTGTTCCCCTTCATGTTTGCACTACTCTTAGTCGCCCTCCCTTTTCCGCGTCTCCAGGG +CGCGCCCCTCAGGACTGTCTTTGCGGTGGCTCGCTCCAGCTGGCTGAGCATTATAAGAGGATACTATTAA +TTAGTCTTGTTAACTCGCGGTTTGCGAAAGCGTATGTAGTCTGTTGTTTGCCGGGGTATTTGAGATAATC +ACACCTGGGCTCTAGTGGCTCTCAGCAAAATCGGGATTGGCCGNGAAAAACCTAACTAACTTCTGTTTGC +CGCACGTCTATTAATCAACCGCATATCGGGGACCCAGGATTCTCCTCAGACAGCAATAACGCGGACGGAG +AGATCTAATGGCAGTGCCTCGGGCTCTTGGAAAGTGAGTCAACTTNTTCCCGACAAATTAAACAGGTGGC +CACCGTAACTGCGGCATCTACGGAACATGACTCGACACTAGTGACAGATGGACTGCACGACTTCGCGGCG +GTATCGGGCTATTTTTTACGTACGTTAAACGGAAAGGGACTCTAGAGCCGTGTCAGGCTGACTGACTTCC +CGAGCCAACGCGCTTAACCGAACGCCACAGACAAACTGTTAAGGCTTGCATGGAGTATACATGGACAAGG +ATCCCAACTGAATCTGAAAG From 530e22c428eed680d3e3aad67688605f30f7c481 Mon Sep 17 00:00:00 2001 From: IvanTsers Date: Thu, 25 Jun 2026 14:14:31 +0200 Subject: [PATCH 3/5] -W affects only megablast mode --- fur/fur.go | 8 +++----- fur/fur.org | 34 ++++++++++++++-------------------- fur/fur_test.go | 2 -- fur/makeRes.sh | 1 - fur/r10.txt | 22 ---------------------- 5 files changed, 17 insertions(+), 50 deletions(-) delete mode 100644 fur/r10.txt diff --git a/fur/fur.go b/fur/fur.go index b801307..4f9b348 100644 --- a/fur/fur.go +++ b/fur/fur.go @@ -61,7 +61,8 @@ func main() { optMM := flag.Bool("M", false, "activate masking (recommended for mammalian genomes)") optN := flag.Int("n", 100, "number of nucleotides in region") - optWW := flag.Int("W", 0, "word length for Blast") + optWW := flag.Int("W", 0, "word size for megablast mode " + + "(use with -m)") flag.Parse() if *optV { util.PrintInfo("fur") @@ -112,7 +113,7 @@ func main() { } }) - if wProvided && *optWW < 4 { + if wProvided && *optM && *optWW < 4 { m := "couldn't set the Blast word size to %d: " + "please use a word size of >= 4" log.Fatalf(m, *optWW) @@ -453,9 +454,6 @@ func main() { if *optMM && ma != "" { args = append(args, "-db_soft_mask", ma) } - if wProvided { - args = append(args, "-word_size", ws) - } args = append(args, "-outfmt", of) cmd := exec.Command("blastn", args...) cmds = append(cmds, cmd) diff --git a/fur/fur.org b/fur/fur.org index 85cb7ac..fd3602f 100644 --- a/fur/fur.org +++ b/fur/fur.org @@ -121,9 +121,9 @@ The user can also print the result of Step~(\ref{eq:fur2}) and exit. Step~(\ref{eq:fur3}) is implemented in \ty{blastn}, where we expose the E-value, the number of threads, a switch to megablast mode instead of the default blastn mode, a switch for running with masking, and the -word length. We also set the minimum length of the final regions at -this point. The number of threads is initialized to the number of -CPUs. +word size for megablast. We also set the minimum length of the final +regions at this point. The number of threads is initialized to the +number of CPUs. #+end_export #+begin_src go <>= optE := flag.Float64("e", 1e-5, "E-value for Blast") @@ -135,7 +135,8 @@ CPUs. optMM := flag.Bool("M", false, "activate masking (recommended for mammalian genomes)") optN := flag.Int("n", 100, "number of nucleotides in region") - optWW := flag.Int("W", 0, "word length for Blast") + optWW := flag.Int("W", 0, "word size for megablast mode " + + "(use with -m)") #+end_src #+begin_export latex We import \ty{runtime}. @@ -258,11 +259,11 @@ sets $f$ outside the range, we notify the user and exit. } #+end_src #+begin_export latex -The user can override task-specific Blast word sizes by explicitly +The user can override megablast-specific Blast word size by explicitly setting it with the \ty{-W} flag. We visit all parsed flags and check -if \ty{-W} is provided. Nucleotide Blast allows the word size of 4 and -greater, so we check the provided value. If it is invalid, we politely -ask the user to set a valid one. +if \ty{-W} is provided and megablast mode is activated. Nucleotide +Blast allows the word size of 4 and greater, so we check the provided +value. If it is invalid, we politely ask the user to set a valid one. #+end_export #+begin_src go <>= wProvided := false @@ -272,7 +273,7 @@ ask the user to set a valid one. } }) - if wProvided && *optWW < 4 { + if wProvided && *optM && *optWW < 4 { m := "couldn't set the Blast word size to %d: " + "please use a word size of >= 4" log.Fatalf(m, *optWW) @@ -1260,7 +1261,8 @@ format, set the arguments, and store the command. cmds = append(cmds, cmd) #+end_src #+begin_export latex -We repeat this for the blastn command, only with a different task. +We repeat this for the blastn command, only with a different task and +without word size. #+end_export #+begin_src go <>= ta = "blastn" @@ -1273,9 +1275,6 @@ We repeat this for the blastn command, only with a different task. if *optMM && ma != "" { args = append(args, "-db_soft_mask", ma) } - if wProvided { - args = append(args, "-word_size", ws) - } args = append(args, "-outfmt", of) cmd := exec.Command("blastn", args...) cmds = append(cmds, cmd) @@ -1622,18 +1621,13 @@ five targets lacks the marker. We set the sensitivity threshold to tests = append(tests, test) #+end_src #+begin_export latex -We run \ty{fur} in megablast and blastn modes setting the word size to -4 and 36, respectively. In the first case, we adjust megablast to be -actually more stringent than blastn, and in the second case, we adjust -both megablast and blastn to be less stringent than the default -megablast. +We run \ty{fur} in megablast mode setting the word size to 4. In this +way, we adjust megablast to be actually more stringent than blastn. #+end_export #+begin_src go <>= d = "test.db" test = exec.Command("./fur", "-d", d, "-m", "-W", "4") tests = append(tests, test) - test = exec.Command("./fur", "-d", d, "-W", "36") - tests = append(tests, test) #+end_src #+begin_export latex For each test we compare the result we get with the result we want, diff --git a/fur/fur_test.go b/fur/fur_test.go index fb7af46..dfa15e1 100644 --- a/fur/fur_test.go +++ b/fur/fur_test.go @@ -35,8 +35,6 @@ func TestFur(t *testing.T) { d = "test.db" test = exec.Command("./fur", "-d", d, "-m", "-W", "4") tests = append(tests, test) - test = exec.Command("./fur", "-d", d, "-W", "36") - tests = append(tests, test) for i, test := range tests { get, err := test.CombinedOutput() if err != nil { diff --git a/fur/makeRes.sh b/fur/makeRes.sh index 02723bd..6576383 100644 --- a/fur/makeRes.sh +++ b/fur/makeRes.sh @@ -7,4 +7,3 @@ ./fur -d masked.db -M &> r7.txt ./fur -d testPartial.db -f 0.8 &> r8.txt ./fur -d test.db -m -W 4 &> r9.txt -./fur -d test.db -W 36 &> r10.txt diff --git a/fur/r10.txt b/fur/r10.txt deleted file mode 100644 index a3857be..0000000 --- a/fur/r10.txt +++ /dev/null @@ -1,22 +0,0 @@ - Step Sequences Length Ns - ------------- --------- ------ -- - Subtraction_1 1 1052 0 - Intersection 1 1051 5 - Subtraction_2 1 1051 5 ->t1_1 -GTTGATCGCAATCCTGCATCGACTGTGGCTCTGGGAGGTCCGACCAAGTGCTACTGCGGCATTAGGGCGC -AGCGATCCCCTCATGCGGCAATGAGGTCATATAATACTACGTTATATTTTGAAGATGAATAANGACTTCC -GTCAAATCAGCAGTCAGTAAATATTGAACCANTTTAAACCGGCGCCGCGCGGTAACCTAGGACTCTCCAT -GCTAAACCCCAGCGCCGTAGAGTGATCCCTAAGCTAATCAAACCGTTCGTTTGTGCCCTATGTCAGGAAG -CACGACGTCGGTAATCGAGTCACCTGTGCCCCGTTTATCCCAGATGTTAGGATAACATTCGTACGACGTA -AAGTTAGACATGGCGCAGGNCACGCCTGCAAACGTTGTTCCCCTTCATGTTTGCACTACTCTTAGTCGCC -CTCCCTTTTCCGCGTCTCCAGGGCGCGCCCCTCAGGACTGTCTTTGCGGTGGCTCGCTCCAGCTGGCTGA -GCATTATAAGAGGATACTATTAATTAGTCTTGTTAACTCGCGGTTTGCGAAAGCGTATGTAGTCTGTTGT -TTGCCGGGGTATTTGAGATAATCACACCTGGGCTCTAGTGGCTCTCAGCAAAATCGGGATTGGCCGNGAA -AAACCTAACTAACTTCTGTTTGCCGCACGTCTATTAATCAACCGCATATCGGGGACCCAGGATTCTCCTC -AGACAGCAATAACGCGGACGGAGAGATCTAATGGCAGTGCCTCGGGCTCTTGGAAAGTGAGTCAACTTNT -TCCCGACAAATTAAACAGGTGGCCACCGTAACTGCGGCATCTACGGAACATGACTCGACACTAGTGACAG -ATGGACTGCACGACTTCGCGGCGGTATCGGGCTATTTTTTACGTACGTTAAACGGAAAGGGACTCTAGAG -CCGTGTCAGGCTGACTGACTTCCCGAGCCAACGCGCTTAACCGAACGCCACAGACAAACTGTTAAGGCTT -GCATGGAGTATACATGGACAAGGATCCCAACTGAATCTGAAAGCAAGGTAATTCTAGGACGTAGAGCCGT -G From 5c9e6d5443a12276112ff3dc08a7a8e52de7f338 Mon Sep 17 00:00:00 2001 From: IvanTsers Date: Thu, 25 Jun 2026 14:35:39 +0200 Subject: [PATCH 4/5] ensure that -W is used with -m only --- fur/fur.go | 4 ++-- fur/fur.org | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fur/fur.go b/fur/fur.go index 4f9b348..bea62c5 100644 --- a/fur/fur.go +++ b/fur/fur.go @@ -424,7 +424,7 @@ func main() { fmt.Fprintf(os.Stderr, m) } } - if wProvided { + if wProvided && *optM { ws = strconv.Itoa(*optWW) } of := "6 qaccver qstart qend" @@ -437,7 +437,7 @@ func main() { if *optMM && ma != "" { args = append(args, "-db_soft_mask", ma) } - if wProvided { + if wProvided && *optM { args = append(args, "-word_size", ws) } args = append(args, "-outfmt", of) diff --git a/fur/fur.org b/fur/fur.org index fd3602f..51ed3c9 100644 --- a/fur/fur.org +++ b/fur/fur.org @@ -1186,7 +1186,7 @@ all numerical options values to strings. if *optMM { //<> } - if wProvided { + if wProvided && *optM { ws = strconv.Itoa(*optWW) } of := "6 qaccver qstart qend" @@ -1253,7 +1253,7 @@ format, set the arguments, and store the command. if *optMM && ma != "" { args = append(args, "-db_soft_mask", ma) } - if wProvided { + if wProvided && *optM { args = append(args, "-word_size", ws) } args = append(args, "-outfmt", of) From 016bfad2041db8c4537c31e3f5ffc86031e77f22 Mon Sep 17 00:00:00 2001 From: Bernhard Haubold Date: Thu, 25 Jun 2026 17:44:28 +0200 Subject: [PATCH 5/5] fur -W now implies -m Previously, fur -W could be run with or without -m, producing different results. --- fur/fur.go | 28 +++++++++++++--------------- fur/fur.org | 44 ++++++++++++++++++++++---------------------- fur/fur_test.go | 2 +- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/fur/fur.go b/fur/fur.go index bea62c5..e418817 100644 --- a/fur/fur.go +++ b/fur/fur.go @@ -62,7 +62,7 @@ func main() { "activate masking (recommended for mammalian genomes)") optN := flag.Int("n", 100, "number of nucleotides in region") optWW := flag.Int("W", 0, "word size for megablast mode " + - "(use with -m)") + "(implies -m)") flag.Parse() if *optV { util.PrintInfo("fur") @@ -103,20 +103,18 @@ func main() { (*optT) = ncpu } if *optF > 1 || *optF <= 0 { - log.Fatalf("can't use %f as a sensitivity threshold\n" + - "please use a positive value not exceeding 1", *optF) + m := "can't use %f as a sensitivity threshold\n" + + "please use a positive value not exceeding 1" + log.Fatalf(m, *optF) } - wProvided := false - flag.Visit(func(f *flag.Flag) { - if f.Name == "W" { - wProvided = true + if *optWW > 0 { + if *optWW < 4 { + m := "couldn't set the Blast word size " + + "to %d; " + + "please use a word size of >= 4" + log.Fatalf(m, *optWW) } - }) - - if wProvided && *optM && *optWW < 4 { - m := "couldn't set the Blast word size to %d: " + - "please use a word size of >= 4" - log.Fatalf(m, *optWW) + (*optM) = true } regions := make([]*fasta.Sequence, 0) rw := tabwriter.NewWriter(os.Stderr, 0, 0, 2, ' ', @@ -424,7 +422,7 @@ func main() { fmt.Fprintf(os.Stderr, m) } } - if wProvided && *optM { + if *optWW > 0 { ws = strconv.Itoa(*optWW) } of := "6 qaccver qstart qend" @@ -437,7 +435,7 @@ func main() { if *optMM && ma != "" { args = append(args, "-db_soft_mask", ma) } - if wProvided && *optM { + if *optWW > 0 { args = append(args, "-word_size", ws) } args = append(args, "-outfmt", of) diff --git a/fur/fur.org b/fur/fur.org index 51ed3c9..21c3d60 100644 --- a/fur/fur.org +++ b/fur/fur.org @@ -136,7 +136,7 @@ number of CPUs. "activate masking (recommended for mammalian genomes)") optN := flag.Int("n", 100, "number of nucleotides in region") optWW := flag.Int("W", 0, "word size for megablast mode " + - "(use with -m)") + "(implies -m)") #+end_src #+begin_export latex We import \ty{runtime}. @@ -254,29 +254,28 @@ sets $f$ outside the range, we notify the user and exit. #+end_export #+begin_src go <>= if *optF > 1 || *optF <= 0 { - log.Fatalf("can't use %f as a sensitivity threshold\n" + - "please use a positive value not exceeding 1", *optF) + m := "can't use %f as a sensitivity threshold\n" + + "please use a positive value not exceeding 1" + log.Fatalf(m, *optF) } #+end_src #+begin_export latex The user can override megablast-specific Blast word size by explicitly -setting it with the \ty{-W} flag. We visit all parsed flags and check -if \ty{-W} is provided and megablast mode is activated. Nucleotide -Blast allows the word size of 4 and greater, so we check the provided -value. If it is invalid, we politely ask the user to set a valid one. +setting it with the \ty{-W} flag. Nucleotide Blast allows the word +size of 4 and greater, so we check the provided value. If it is +invalid, we politely ask the user to set a valid one. Setting the word +size only makes sense in megablast mode, so if \ty{-W} is correctly +set, we also set \ty{-m}. #+end_export #+begin_src go <>= - wProvided := false - flag.Visit(func(f *flag.Flag) { - if f.Name == "W" { - wProvided = true + if *optWW > 0 { + if *optWW < 4 { + m := "couldn't set the Blast word size " + + "to %d; " + + "please use a word size of >= 4" + log.Fatalf(m, *optWW) } - }) - - if wProvided && *optM && *optWW < 4 { - m := "couldn't set the Blast word size to %d: " + - "please use a word size of >= 4" - log.Fatalf(m, *optWW) + (*optM) = true } #+end_src #+begin_export latex @@ -1186,7 +1185,7 @@ all numerical options values to strings. if *optMM { //<> } - if wProvided && *optM { + if *optWW > 0 { ws = strconv.Itoa(*optWW) } of := "6 qaccver qstart qend" @@ -1253,7 +1252,7 @@ format, set the arguments, and store the command. if *optMM && ma != "" { args = append(args, "-db_soft_mask", ma) } - if wProvided && *optM { + if *optWW > 0 { args = append(args, "-word_size", ws) } args = append(args, "-outfmt", of) @@ -1621,12 +1620,13 @@ five targets lacks the marker. We set the sensitivity threshold to tests = append(tests, test) #+end_src #+begin_export latex -We run \ty{fur} in megablast mode setting the word size to 4. In this -way, we adjust megablast to be actually more stringent than blastn. +We run \ty{fur} in implied megablast mode by setting the word size to +4. In this way, we adjust megablast to be actually more stringent than +blastn. #+end_export #+begin_src go <>= d = "test.db" - test = exec.Command("./fur", "-d", d, "-m", "-W", "4") + test = exec.Command("./fur", "-d", d, "-W", "4") tests = append(tests, test) #+end_src #+begin_export latex diff --git a/fur/fur_test.go b/fur/fur_test.go index dfa15e1..c854fbe 100644 --- a/fur/fur_test.go +++ b/fur/fur_test.go @@ -33,7 +33,7 @@ func TestFur(t *testing.T) { test = exec.Command("./fur", "-d", d, "-f", "0.8") tests = append(tests, test) d = "test.db" - test = exec.Command("./fur", "-d", d, "-m", "-W", "4") + test = exec.Command("./fur", "-d", d, "-W", "4") tests = append(tests, test) for i, test := range tests { get, err := test.CombinedOutput()