2025-11-03 15:37:45 +00:00
# Poindexter
2025-11-03 18:19:12 +00:00
[](https://pkg.go.dev/github.com/Snider/Poindexter)
[](https://github.com/Snider/Poindexter/actions)
[](https://goreportcard.com/report/github.com/Snider/Poindexter)
[](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck)
2025-11-03 18:26:40 +00:00
[](https://codecov.io/gh/Snider/Poindexter)
2025-11-03 18:58:44 +00:00
[](https://github.com/Snider/Poindexter/releases)
2025-11-03 18:19:12 +00:00
2025-11-03 15:37:45 +00:00
A Go library package providing utility functions including sorting algorithms with custom comparators.
## Features
- 🔢 **Sorting Utilities** : Sort integers, strings, and floats in ascending or descending order
- 🎯 **Custom Sorting** : Sort any type with custom comparison functions or key extractors
- 🔍 **Binary Search** : Fast search on sorted data
2025-11-03 18:51:23 +00:00
- 🧭 **KDTree (NN Search)** : Build a KDTree over points with generic payloads; nearest, k-NN, and radius queries with Euclidean, Manhattan, Chebyshev, and Cosine metrics
2025-11-03 15:37:45 +00:00
- 📦 **Generic Functions** : Type-safe operations using Go generics
- ✅ **Well-Tested** : Comprehensive test coverage
- 📖 **Documentation** : Full documentation available at GitHub Pages
## Installation
```bash
go get github.com/Snider/Poindexter
```
## Quick Start
```go
package main
import (
"fmt"
2025-11-03 16:49:31 +00:00
poindexter "github.com/Snider/Poindexter"
2025-11-03 15:37:45 +00:00
)
func main() {
// Basic sorting
numbers := []int{3, 1, 4, 1, 5, 9}
poindexter.SortInts(numbers)
fmt.Println(numbers) // [1 1 3 4 5 9]
// Custom sorting with key function
type Product struct {
Name string
Price float64
}
2025-11-03 16:49:31 +00:00
products := []Product{{"Apple", 1.50}, {"Banana", 0.75}, {"Cherry", 3.00}}
poindexter.SortByKey(products, func(p Product) float64 { return p.Price })
// KDTree quick demo
pts := []poindexter.KDPoint[string]{
{ID: "A", Coords: []float64{0, 0}, Value: "alpha"},
{ID: "B", Coords: []float64{1, 0}, Value: "bravo"},
{ID: "C", Coords: []float64{0, 1}, Value: "charlie"},
2025-11-03 15:37:45 +00:00
}
2025-11-03 16:49:31 +00:00
tree, _ := poindexter.NewKDTree(pts, poindexter.WithMetric(poindexter.EuclideanDistance{}))
nearest, dist, _ := tree.Nearest([]float64{0.9, 0.1})
fmt.Println(nearest.ID, nearest.Value, dist) // B bravo ~0.141...
2025-11-03 15:37:45 +00:00
}
```
## Documentation
Full documentation is available at [https://snider.github.io/Poindexter/ ](https://snider.github.io/Poindexter/ )
2025-11-03 18:19:12 +00:00
Explore runnable examples in the repository:
- examples/dht_ping_1d
- examples/kdtree_2d_ping_hop
- examples/kdtree_3d_ping_hop_geo
- examples/kdtree_4d_ping_hop_geo_score
2025-11-04 02:15:04 +00:00
- examples/dht_helpers (convenience wrappers for common DHT schemas)
2025-11-04 01:44:16 +00:00
- examples/wasm-browser (browser demo using the ESM loader)
2025-11-04 02:15:04 +00:00
- examples/wasm-browser-ts (TypeScript + Vite local demo)
2025-11-03 18:19:12 +00:00
### KDTree performance and notes
2025-11-04 01:44:16 +00:00
- Dual backend support: Linear (always available) and an optimized KD backend enabled when building with `-tags=gonum` . Linear is the default; with the `gonum` tag, the optimized backend becomes the default.
- Complexity: Linear backend is O(n) per query. Optimized KD backend is typically sub-linear on prunable datasets and dims ≤ ~8, especially as N grows (≥10k– 100k).
2025-11-03 18:19:12 +00:00
- Insert is O(1) amortized; delete by ID is O(1) via swap-delete; order is not preserved.
- Concurrency: the KDTree type is not safe for concurrent mutation. Protect with a mutex or share immutable snapshots for read-mostly workloads.
- See multi-dimensional examples (ping/hops/geo/score) in docs and `examples/` .
2025-11-03 18:58:44 +00:00
- Performance guide: see docs/Performance for benchmark guidance and tips: [docs/perf.md ](docs/perf.md ) • Hosted: https://snider.github.io/Poindexter/perf/
2025-11-04 01:44:16 +00:00
### Backend selection
- Default backend is Linear. If you build with `-tags=gonum` , the default becomes the optimized KD backend.
- You can override per tree at construction:
```go
// Force Linear (always available)
kdt1, _ := poindexter.NewKDTree(pts, poindexter.WithBackend(poindexter.BackendLinear))
// Force Gonum (requires build tag)
kdt2, _ := poindexter.NewKDTree(pts, poindexter.WithBackend(poindexter.BackendGonum))
```
- Supported metrics in the optimized backend: Euclidean (L2), Manhattan (L1), Chebyshev (L∞).
- Cosine and Weighted-Cosine currently run on the Linear backend.
- See the Performance guide for measured comparisons and when to choose which backend.
2025-11-03 18:58:44 +00:00
#### Choosing a metric (quick tips)
- Euclidean (L2): smooth trade-offs across axes; solid default for blended preferences.
- Manhattan (L1): emphasizes per-axis absolute differences; good when each unit of ping/hop matters equally.
- Chebyshev (L∞): dominated by the worst axis; useful for strict thresholds (e.g., reject high hop count regardless of ping).
- Cosine: angle-based for vector similarity; pair it with normalized/weighted features when direction matters more than magnitude.
See the multi-dimensional KDTree docs for end-to-end examples and weighting/normalization helpers: [Multi-Dimensional KDTree (DHT) ](docs/kdtree-multidimensional.md ).
2025-11-03 18:19:12 +00:00
2025-11-03 19:33:28 +00:00
## Maintainer Makefile
The repository includes a maintainer-friendly `Makefile` that mirrors CI tasks and speeds up local workflows.
- help — list available targets
- tidy / tidy-check — run `go mod tidy` , optionally verify no diffs
- fmt — format code (`go fmt ./...` )
- vet — `go vet ./...`
- build — `go build ./...`
- examples — build all programs under `examples/` (if present)
- test — run unit tests
- race — run tests with the race detector
- cover — run tests with race + coverage (writes `coverage.out` and prints summary)
- coverhtml — render HTML coverage report to `coverage.html`
2025-11-03 19:46:38 +00:00
- coverfunc — print per-function coverage (from `coverage.out` )
- cover-kdtree — print coverage details filtered to `kdtree.go`
2025-11-03 19:33:28 +00:00
- fuzz — run Go fuzzing for a configurable time (default 10s) matching CI
- bench — run benchmarks with `-benchmem` (writes `bench.txt` )
- lint — run `golangci-lint` (if installed)
- vuln — run `govulncheck` (if installed)
- ci — CI-parity aggregate: tidy-check, build, vet, cover, examples, bench, lint, vuln
- release — run GoReleaser with the canonical `.goreleaser.yaml` (for tagged releases)
- snapshot — GoReleaser snapshot (no publish)
- docs-serve — serve MkDocs locally on 127.0.0.1:8000
- docs-build — build MkDocs site into `site/`
Quick usage:
- See all targets:
```bash
make help
```
- Fast local cycle:
```bash
make fmt
make vet
make test
```
- CI-parity run (what GitHub Actions does, locally):
```bash
make ci
```
- Coverage summary:
```bash
make cover
```
- Generate HTML coverage report (writes coverage.html):
```bash
make coverhtml
```
- Fuzz for 10 seconds (default):
```bash
make fuzz
```
- Fuzz with a custom time (e.g., 30s):
```bash
make fuzz FUZZTIME=30s
```
- Run benchmarks (writes bench.txt):
```bash
make bench
```
- Build examples (if any under ./examples):
```bash
make examples
```
- Serve docs locally (requires mkdocs-material):
```bash
make docs-serve
```
Configurable variables:
- `FUZZTIME` (default `10s` ) — e.g. `make fuzz FUZZTIME=30s`
- `BENCHOUT` (default `bench.txt` ), `COVEROUT` (default `coverage.out` ), `COVERHTML` (default `coverage.html` )
- Tool commands are overridable via env: `GO` , `GOLANGCI_LINT` , `GORELEASER` , `MKDOCS`
Requirements for optional targets:
- `golangci-lint` for `make lint`
- `golang.org/x/vuln/cmd/govulncheck` for `make vuln`
- `goreleaser` for `make release` / `make snapshot`
- `mkdocs` + `mkdocs-material` for `make docs-serve` / `make docs-build`
See the full Makefile at the repo root for authoritative target definitions.
2025-11-03 15:37:45 +00:00
## License
This project is licensed under the European Union Public Licence v1.2 (EUPL-1.2). See [LICENSE ](LICENSE ) for details.
## Contributing
2025-11-04 02:15:04 +00:00
Contributions are welcome! Please feel free to submit a Pull Request.
## Coverage
- CI produces coverage summaries as artifacts on every push/PR:
- Default job: `coverage-summary.md` (from `coverage.out` )
- Gonum-tag job: `coverage-summary-gonum.md` (from `coverage-gonum.out` )
- Locally, you can generate and inspect coverage with the Makefile:
```bash
make cover # runs tests with race + coverage and prints the total
make coverfunc # prints per-function coverage
make cover-kdtree # filters coverage to kdtree.go
make coverhtml # writes coverage.html for visual inspection
```
Note: CI also uploads raw coverage profiles as artifacts (`coverage.out` , `coverage-gonum.out` ).