Compare commits

...

9 Commits

Author SHA1 Message Date
Eric Coissac e298daeef9 [v4.5] Bugfix for 3-base sequence handling and utility refactoring
- **Bug fix**: Corrected logic in 4-mer calculation to properly handle sequences of length exactly three. Previously, such cases could produce invalid or unexpected results due to an incomplete guard condition (`length < 0`) which failed for ` length == 3` (where computed step size was zero). The fix ensures all sequences shorter than four bases are safely excluded.

- **Refactor**: Introduced a new internal utility function (`inverser_chaine`) to centralize string reversal logic, improving code maintainability and test coverage without affecting user-facing behavior.
2026-04-13 17:18:53 +02:00
Eric Coissac d9e6f67a6e chore: bump version to 4.4.36
Update package and file versions from v4.4.35 to 4.4.36.
2026-04-13 17:18:48 +02:00
Eric Coissac f036c7fa96 ⬆️ version bump to v4.5
- Update `version.txt` from "v3" to v4.5
- Bump Go constant `_Version = 'Release 4.x.y'` accordingly
2026-04-13 17:18:34 +02:00
Eric Coissac e33665e716 Refactor: Extract utility function for string reversal
- Introduce `inverser_chaine()` helper to centralize logic
 - Update tests and documentation accordingly
2026-04-13 17:18:34 +02:00
Eric Coissac c955a614ca chore: bump version to 4.4.35
Update obioptions/version.go and version.txt to reflect release 4.4.35.
2026-04-13 17:18:34 +02:00
Eric Coissac f19065261e We kept 2026-04-13 17:18:34 +02:00
coissac 3e349e92e1 Merge pull request #104 from theo-krueger/master
Bugfix: result of 0 4mers not caught if sequence length == 3
2026-04-13 16:39:08 +02:00
coissac a4ce24a418 Merge pull request #108 from metabarcoding/push-qlxnulxwokxo
Push qlxnulxwokxo
2026-04-13 16:27:43 +02:00
theo-krueger c7816973a6 Bugfix: result of 0 4mers not caught if sequence length == 3
In the 4mer calculation:
length := slength - 3

- for sequences with <4 bases, length is <=0

The check to stop did only catch <0, so sequences lengths 2 or less, leaving sequence lengths of 3 unguarded
if length < 0 {
		return nil
	}
2026-04-10 14:05:30 +02:00
4 changed files with 90 additions and 7 deletions
+1 -1
View File
@@ -47,7 +47,7 @@ func Encode4mer(seq *obiseq.BioSequence, buffer *[]byte) []byte {
length := slength - 3
rawseq := seq.Sequence()
if length < 0 {
if length <= 0 {
return nil
}
+87 -4
View File
@@ -141,6 +141,69 @@ func LuaWorker(proto *lua.FunctionProto) obiseq.SeqWorker {
return nil
}
// LuaSliceWorker creates a SeqSliceWorker that calls the Lua function
// named "slice_worker". Unlike LuaWorker, the entire batch (BioSequenceSlice)
// is passed to the Lua function at once, enabling batch-level processing
// (e.g. a single HTTP request per batch instead of one per sequence).
//
// The Lua function signature:
//
// function slice_worker(slice) -- receives a BioSequenceSlice
// -- process the batch
// return slice -- returns a BioSequenceSlice (or nil)
// end
func LuaSliceWorker(proto *lua.FunctionProto) obiseq.SeqSliceWorker {
interpreter := NewInterpreter()
lfunc := interpreter.NewFunctionFromProto(proto)
interpreter.Push(lfunc)
err := interpreter.PCall(0, lua.MultRet, nil)
if err != nil {
log.Fatalf("Error in executing the lua script: %v", err)
}
result := interpreter.GetGlobal("slice_worker")
if lua_worker, ok := result.(*lua.LFunction); ok {
f := func(slice obiseq.BioSequenceSlice) (obiseq.BioSequenceSlice, error) {
if err := interpreter.CallByParam(lua.P{
Fn: lua_worker,
NRet: 1,
Protect: true,
}, obiseqslice2Lua(interpreter, &slice)); err != nil {
log.Fatal(err)
}
lreponse := interpreter.Get(-1)
defer interpreter.Pop(1)
if reponse, ok := lreponse.(*lua.LUserData); ok {
s := reponse.Value
switch val := s.(type) {
case *obiseq.BioSequenceSlice:
return *val, nil
case *obiseq.BioSequence:
return obiseq.BioSequenceSlice{val}, nil
default:
r := reflect.TypeOf(val)
return nil, fmt.Errorf("slice_worker function doesn't return the correct type %s", r)
}
}
if _, ok = lreponse.(*lua.LNilType); ok {
return nil, nil
}
return nil, fmt.Errorf("slice_worker function doesn't return the correct type %T", lreponse)
}
return f
}
log.Fatalf("The slice_worker object is not a function")
return nil
}
// LuaProcessor processes a Lua script on a sequence iterator and returns a new iterator.
//
// Parameters:
@@ -216,11 +279,27 @@ func LuaProcessor(iterator obiiter.IBioSequence, name, program string, breakOnEr
}()
ff := func(iterator obiiter.IBioSequence) {
w := LuaWorker(proto)
sw := obiseq.SeqToSliceWorker(w, false)
// Detect whether the script defines slice_worker (batch-level) or worker (per-sequence).
hasSliceWorker := func() bool {
interpreter := NewInterpreter()
lfunc := interpreter.NewFunctionFromProto(proto)
interpreter.Push(lfunc)
if err := interpreter.PCall(0, lua.MultRet, nil); err != nil {
return false
}
result := interpreter.GetGlobal("slice_worker")
interpreter.Close()
_, ok := result.(*lua.LFunction)
return ok
}()
// iterator = iterator.SortBatches()
ff := func(iterator obiiter.IBioSequence) {
var sw obiseq.SeqSliceWorker
if hasSliceWorker {
sw = LuaSliceWorker(proto)
} else {
sw = obiseq.SeqToSliceWorker(LuaWorker(proto), false)
}
for iterator.Next() {
seqs := iterator.Get()
@@ -235,6 +314,10 @@ func LuaProcessor(iterator obiiter.IBioSequence, name, program string, breakOnEr
}
}
if ns == nil {
ns = obiseq.BioSequenceSlice{}
}
newIter.Push(obiiter.MakeBioSequenceBatch(seqs.Source(), seqs.Order(), ns))
}
+1 -1
View File
@@ -3,7 +3,7 @@ package obioptions
// Version is automatically updated by the Makefile from version.txt
// The patch number (third digit) is incremented on each push to the repository
var _Version = "Release 4.4.34"
var _Version = "Release 4.4.37"
// Version returns the version of the obitools package.
//
+1 -1
View File
@@ -1 +1 @@
4.4.34
4.4.37