Files
sqlite3/ext/stats/mode.go

122 lines
2.1 KiB
Go
Raw Permalink Normal View History

2025-01-19 01:28:53 +00:00
package stats
import (
"unsafe"
"github.com/ncruces/go-sqlite3"
)
func newMode() sqlite3.AggregateFunction {
return &mode{}
}
type mode struct {
ints counter[int64]
reals counter[float64]
texts counter[string]
blobs counter[string]
}
func (m mode) Value(ctx sqlite3.Context) {
var (
typ = sqlite3.NULL
2025-07-17 00:50:39 +01:00
max uint
2025-01-19 01:28:53 +00:00
i64 int64
f64 float64
str string
)
for k, v := range m.ints {
if v > max || v == max && k < i64 {
typ = sqlite3.INTEGER
max = v
i64 = k
}
}
for k, v := range m.reals {
if v > max || v == max && k < f64 {
typ = sqlite3.FLOAT
max = v
f64 = k
}
}
for k, v := range m.texts {
if v > max || v == max && typ == sqlite3.TEXT && k < str {
typ = sqlite3.TEXT
max = v
str = k
}
}
for k, v := range m.blobs {
if v > max || v == max && typ == sqlite3.BLOB && k < str {
typ = sqlite3.BLOB
max = v
str = k
}
}
switch typ {
case sqlite3.INTEGER:
ctx.ResultInt64(i64)
case sqlite3.FLOAT:
ctx.ResultFloat(f64)
case sqlite3.TEXT:
ctx.ResultText(str)
case sqlite3.BLOB:
ctx.ResultBlob(unsafe.Slice(unsafe.StringData(str), len(str)))
}
}
2025-07-17 00:50:39 +01:00
func (m *mode) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
2025-01-19 01:28:53 +00:00
switch arg[0].Type() {
case sqlite3.INTEGER:
2025-07-17 00:50:39 +01:00
if m.reals == nil {
m.ints.add(arg[0].Int64())
break
}
fallthrough
2025-01-19 01:28:53 +00:00
case sqlite3.FLOAT:
2025-07-17 00:50:39 +01:00
m.reals.add(arg[0].Float())
for k, v := range m.ints {
m.reals[float64(k)] += v
}
m.ints = nil
2025-01-19 01:28:53 +00:00
case sqlite3.TEXT:
2025-07-17 00:50:39 +01:00
m.texts.add(arg[0].Text())
2025-01-19 01:28:53 +00:00
case sqlite3.BLOB:
2025-07-17 00:50:39 +01:00
m.blobs.add(string(arg[0].RawBlob()))
2025-01-19 01:28:53 +00:00
}
}
2025-07-17 00:50:39 +01:00
func (m *mode) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
2025-01-19 01:28:53 +00:00
switch arg[0].Type() {
case sqlite3.INTEGER:
2025-07-17 00:50:39 +01:00
if m.reals == nil {
m.ints.del(arg[0].Int64())
break
}
fallthrough
2025-01-19 01:28:53 +00:00
case sqlite3.FLOAT:
2025-07-17 00:50:39 +01:00
m.reals.del(arg[0].Float())
2025-01-19 01:28:53 +00:00
case sqlite3.TEXT:
2025-07-17 00:50:39 +01:00
m.texts.del(arg[0].Text())
2025-01-19 01:28:53 +00:00
case sqlite3.BLOB:
2025-07-17 00:50:39 +01:00
m.blobs.del(string(arg[0].RawBlob()))
2025-01-19 01:28:53 +00:00
}
}
2025-07-17 00:50:39 +01:00
type counter[T comparable] map[T]uint
2025-01-19 01:28:53 +00:00
func (c *counter[T]) add(k T) {
if (*c) == nil {
(*c) = make(counter[T])
}
(*c)[k]++
}
func (c counter[T]) del(k T) {
2025-07-17 00:50:39 +01:00
if n := c[k]; n == 1 {
2025-01-19 01:28:53 +00:00
delete(c, k)
2025-07-17 00:50:39 +01:00
} else {
c[k] = n - 1
2025-01-19 01:28:53 +00:00
}
}