2026-11-03 06:09:06 +11:00
|
|
|
//go:build goexperiment.simd
|
|
|
|
|
|
2026-11-03 09:22:32 +11:00
|
|
|
package obisimilarity
|
2026-11-03 06:09:06 +11:00
|
|
|
|
|
|
|
|
import "simd"
|
|
|
|
|
|
2026-11-03 09:22:32 +11:00
|
|
|
// commonWords is the upper bound used for the number of 4-mers shared by two
|
|
|
|
|
// sequences: the sum of the minima of the counters, plus the minimum of the
|
|
|
|
|
// two overflows.
|
2026-11-03 06:09:06 +11:00
|
|
|
//
|
|
|
|
|
// The minima of the 256 counters are computed by vectors of bytes, and summed
|
|
|
|
|
// in 16-bit lanes (at most 256*255 spread over the lanes, the lanes being at
|
|
|
|
|
// least 8 in number, so that none of them overflows).
|
2026-11-03 09:22:32 +11:00
|
|
|
func commonWords(t1, t2 *Table4) int {
|
2026-11-03 06:09:06 +11:00
|
|
|
var probe simd.Uint8s
|
|
|
|
|
vl := probe.Len()
|
|
|
|
|
|
|
|
|
|
acc := simd.BroadcastUint16s(0)
|
|
|
|
|
low := simd.BroadcastUint16s(0x00FF)
|
2026-11-03 09:22:32 +11:00
|
|
|
for i := 0; i < len(t1.Count); i += vl {
|
|
|
|
|
m := simd.LoadUint8s(t1.Count[i:]).Min(simd.LoadUint8s(t2.Count[i:])).ReshapeToUint16s()
|
2026-11-03 06:09:06 +11:00
|
|
|
acc = acc.Add(m.And(low)).Add(m.ShiftAllRight(8))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// sum the pairs of 16-bit lanes into 32-bit lanes, then add those up
|
|
|
|
|
wide := acc.ReshapeToUint32s()
|
|
|
|
|
sums := wide.And(simd.BroadcastUint32s(0xFFFF)).Add(wide.ShiftAllRight(16))
|
|
|
|
|
var lanes [32]uint32
|
|
|
|
|
sums.Store(lanes[:])
|
|
|
|
|
total := 0
|
|
|
|
|
for _, v := range lanes[:sums.Len()] {
|
|
|
|
|
total += int(v)
|
|
|
|
|
}
|
2026-11-03 09:22:32 +11:00
|
|
|
return total + min(t1.Overflow, t2.Overflow)
|
2026-11-03 06:09:06 +11:00
|
|
|
}
|