This commit introduces a new file, AUDIT-COMPLEXITY.md, which contains a detailed audit of the codebase. The audit covers: - Cyclomatic and cognitive complexity - Code duplication - Maintainability issues such as God classes, long methods, and parameter lists - Other code smells The report provides specific recommendations for refactoring and improving the code's structure and maintainability, including illustrative code examples. The findings are prioritized by their impact. This audit serves as a guide for future refactoring efforts to improve the overall quality of the codebase. Co-authored-by: Snider <631881+Snider@users.noreply.github.com> |
||
|---|---|---|
| .github/workflows | ||
| docs | ||
| examples | ||
| npm/poindexter-wasm | ||
| wasm | ||
| .gitignore | ||
| .golangci.yml | ||
| .goreleaser.yaml | ||
| .goreleaser.yml | ||
| AUDIT-COMPLEXITY.md | ||
| bench_kdtree_dual_100k_test.go | ||
| bench_kdtree_dual_test.go | ||
| bench_kdtree_test.go | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| CODE_OF_CONDUCT.md | ||
| CONTRIBUTING.md | ||
| dns_tools.go | ||
| dns_tools_test.go | ||
| doc.go | ||
| examples_test.go | ||
| fuzz_kdtree_test.go | ||
| go.mod | ||
| kdtree.go | ||
| kdtree_analytics.go | ||
| kdtree_analytics_test.go | ||
| kdtree_backend_parity_test.go | ||
| kdtree_branches_test.go | ||
| kdtree_extra_test.go | ||
| kdtree_gonum.go | ||
| kdtree_gonum_stub.go | ||
| kdtree_gonum_test.go | ||
| kdtree_helpers.go | ||
| kdtree_helpers_test.go | ||
| kdtree_morecov_test.go | ||
| kdtree_nd_noerr_test.go | ||
| kdtree_nd_test.go | ||
| kdtree_test.go | ||
| LICENSE | ||
| Makefile | ||
| mkdocs.yml | ||
| poindexter.go | ||
| poindexter_test.go | ||
| README.md | ||
| SECURITY.md | ||
| sort.go | ||
| sort_test.go | ||
Poindexter
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
- 🧭 KDTree (NN Search): Build a KDTree over points with generic payloads; nearest, k-NN, and radius queries with Euclidean, Manhattan, Chebyshev, and Cosine metrics
- 📦 Generic Functions: Type-safe operations using Go generics
- ✅ Well-Tested: Comprehensive test coverage
- 📖 Documentation: Full documentation available at GitHub Pages
Installation
go get github.com/Snider/Poindexter
Quick Start
package main
import (
"fmt"
poindexter "github.com/Snider/Poindexter"
)
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
}
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"},
}
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...
}
Documentation
Full documentation is available at https://snider.github.io/Poindexter/
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
- examples/dht_helpers (convenience wrappers for common DHT schemas)
- examples/wasm-browser (browser demo using the ESM loader)
- examples/wasm-browser-ts (TypeScript + Vite local demo)
KDTree performance and notes
- Dual backend support: Linear (always available) and an optimized KD backend enabled when building with
-tags=gonum. Linear is the default; with thegonumtag, 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).
- 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/. - Performance guide: see docs/Performance for benchmark guidance and tips: docs/perf.md • Hosted: https://snider.github.io/Poindexter/perf/
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:
// 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.
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).
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.outand prints summary) - coverhtml — render HTML coverage report to
coverage.html - coverfunc — print per-function coverage (from
coverage.out) - cover-kdtree — print coverage details filtered to
kdtree.go - fuzz — run Go fuzzing for a configurable time (default 10s) matching CI
- bench — run benchmarks with
-benchmem(writesbench.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:
make help
- Fast local cycle:
make fmt
make vet
make test
- CI-parity run (what GitHub Actions does, locally):
make ci
- Coverage summary:
make cover
- Generate HTML coverage report (writes coverage.html):
make coverhtml
- Fuzz for 10 seconds (default):
make fuzz
- Fuzz with a custom time (e.g., 30s):
make fuzz FUZZTIME=30s
- Run benchmarks (writes bench.txt):
make bench
- Build examples (if any under ./examples):
make examples
- Serve docs locally (requires mkdocs-material):
make docs-serve
Configurable variables:
FUZZTIME(default10s) — e.g.make fuzz FUZZTIME=30sBENCHOUT(defaultbench.txt),COVEROUT(defaultcoverage.out),COVERHTML(defaultcoverage.html)- Tool commands are overridable via env:
GO,GOLANGCI_LINT,GORELEASER,MKDOCS
Requirements for optional targets:
golangci-lintformake lintgolang.org/x/vuln/cmd/govulncheckformake vulngoreleaserformake release/make snapshotmkdocs+mkdocs-materialformake docs-serve/make docs-build
See the full Makefile at the repo root for authoritative target definitions.
License
This project is licensed under the European Union Public Licence v1.2 (EUPL-1.2). See LICENSE for details.
Contributing
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(fromcoverage.out) - Gonum-tag job:
coverage-summary-gonum.md(fromcoverage-gonum.out)
- Default job:
- Locally, you can generate and inspect coverage with the Makefile:
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).