caio.co/de/go-tdigest

Breaking: add Clone method to RNG interface

Id
a11b2e8dc6420325b0b7432146592531e0ffd166
Author
Caio
Commit time
2025-11-29T14:45:00+01:00

Modified options.go

@@ -32,10 +32,9
// This allows changing which random number source is used when using
// the TDigest structure (rngs are used when deciding which candidate
// centroid to merge with and when compressing or merging with
-// another digest for it increases accuracy). This functionality is
-// particularly useful for testing or when you want to disconnect
-// your sample collection from the (default) shared random source
-// to minimize lock contention.
+// another digest for it increases accuracy).
+//
+// By default the digest is initialized with a local RNG seeded with 1.
func RandomNumberGenerator(rng RNG) tdigestOption { // nolint
return func(t *TDigest) error {
t.rng = rng
@@ -44,7 +43,7
}

// LocalRandomNumberGenerator makes the TDigest use the default
-// `math/random` functions but with an unshared source that is
+// `math/rand` functions but with an unshared source that is
// seeded with the given `seed` parameter.
func LocalRandomNumberGenerator(seed int64) tdigestOption { // nolint
return RandomNumberGenerator(newLocalRNG(seed))

Modified rng.go

@@ -9,16 +9,7
type RNG interface {
Float32() float32
Intn(int) int
-}
-
-type globalRNG struct{}
-
-func (r globalRNG) Float32() float32 {
- return rand.Float32()
-}
-
-func (r globalRNG) Intn(i int) int {
- return rand.Intn(i)
+ Clone() RNG
}

type localRNG struct {
@@ -37,4 +28,8

func (r *localRNG) Intn(i int) int {
return r.localRand.Intn(i)
+}
+
+func (r *localRNG) Clone() RNG {
+ return newLocalRNG(r.localRand.Int63())
}

Modified tdigest.go

@@ -339,12 +339,16
}

// Clone returns a deep copy of a TDigest.
+//
+// Notice that the provided RNG (via options.LocalRandomNumberGenerator)
+// does NOT clone its state - a brand new source is created. Users
+// must implement their own RNG if they want something different.
func (t *TDigest) Clone() *TDigest {
return &TDigest{
summary: t.summary.Clone(),
compression: t.compression,
count: t.count,
- rng: t.rng,
+ rng: t.rng.Clone(),
}
}

@@ -353,7 +357,6
t.summary.Reset()
t.count = 0
}
-

func interpolate(x, x0, x1 float64) float64 {
return (x - x0) / (x1 - x0)

Modified tdigest_test.go

@@ -521,7 +521,7
},
compression: 5,
count: 1250,
- rng: globalRNG{},
+ rng: newLocalRNG(1),
}

if cdf := td.CDF(7.144560976650238e+06); cdf > 1 {