{"id":46857779,"url":"https://github.com/aalpar/deheap","last_synced_at":"2026-03-10T17:29:12.121Z","repository":{"id":57516660,"uuid":"182847422","full_name":"aalpar/deheap","owner":"aalpar","description":"Generic double-ended heap (min-max heap) for Go — O(1) min and max, O(log n) push/pop","archived":false,"fork":false,"pushed_at":"2026-03-07T03:39:12.000Z","size":67,"stargazers_count":15,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-07T12:27:41.020Z","etag":null,"topics":["data-structure","deque","double-ended-queue","generics","go","golang","heap","heaps","min-max-heap","priority-queue"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aalpar.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2019-04-22T18:44:48.000Z","updated_at":"2026-03-07T03:39:16.000Z","dependencies_parsed_at":"2022-09-26T18:41:10.073Z","dependency_job_id":null,"html_url":"https://github.com/aalpar/deheap","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/aalpar/deheap","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aalpar%2Fdeheap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aalpar%2Fdeheap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aalpar%2Fdeheap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aalpar%2Fdeheap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aalpar","download_url":"https://codeload.github.com/aalpar/deheap/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aalpar%2Fdeheap/sbom","scorecard":{"id":158832,"data":{"date":"2025-08-11","repo":{"name":"github.com/aalpar/deheap","commit":"0cc84d79dec3e8beffe8761720771a27691bf32b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Code-Review","score":1,"reason":"Found 1/7 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.txt:0","Info: FSF or OSI recognized license: MIT License: LICENSE.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 2 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T12:45:13.123Z","repository_id":57516660,"created_at":"2025-08-16T12:45:13.123Z","updated_at":"2025-08-16T12:45:13.123Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30344426,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T15:55:29.454Z","status":"ssl_error","status_checked_at":"2026-03-10T15:54:58.440Z","response_time":106,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-structure","deque","double-ended-queue","generics","go","golang","heap","heaps","min-max-heap","priority-queue"],"created_at":"2026-03-10T17:29:11.527Z","updated_at":"2026-03-10T17:29:12.111Z","avatar_url":"https://github.com/aalpar.png","language":"Go","readme":"# deheap\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/aalpar/deheap.svg)](https://pkg.go.dev/github.com/aalpar/deheap)\n[![Go Report Card](https://goreportcard.com/badge/github.com/aalpar/deheap)](https://goreportcard.com/report/github.com/aalpar/deheap)\n[![codecov](https://codecov.io/gh/aalpar/deheap/graph/badge.svg)](https://codecov.io/gh/aalpar/deheap)\n\nA doubly-ended heap (min-max heap) for Go. Provides O(log n) access to both\nthe minimum and maximum elements of a collection through a single data\nstructure, with zero external dependencies.\n\n```\ngo get github.com/aalpar/deheap\n```\n\n## Why a doubly-ended heap?\n\nA standard binary heap gives you efficient access to one extremum — the\nsmallest or the largest element — but not both. Many practical problems need\nboth ends simultaneously:\n\n**Scheduling and resource allocation.** Operating system schedulers and job\nqueues routinely need the highest-priority task (to run next) and the\nlowest-priority task (to evict or age). A doubly-ended priority queue avoids\nmaintaining two separate heaps and the bookkeeping to keep them synchronized.\n\n**Bounded-size caches and buffers.** When a priority queue has a capacity\nlimit, insertions must discard the least valuable element. With a min-max\nheap, both the insertion (against the max) and the eviction (from the min, or\nvice versa) are logarithmic — no linear scan required.\n\n**Median maintenance and order statistics.** Streaming median algorithms\ntypically partition data into a max-heap of the lower half and a min-heap of\nthe upper half. A single min-max heap can serve double duty, simplifying the\nimplementation.\n\n**Network packet scheduling.** Rate-controlled and deadline-aware packet\nschedulers (e.g., in QoS systems) need to dequeue by earliest deadline and\ndrop by lowest priority, both efficiently.\n\n**Search algorithms.** Algorithms like SMA\\* (Simplified Memory-Bounded A\\*)\nmaintain an open set where the node with the lowest f-cost is expanded next\nand the node with the highest f-cost is pruned when memory is exhausted.\n\n## API\n\n`deheap` provides two API surfaces.\n\n### Generic API (Go 1.21+)\n\nFor `cmp.Ordered` types — `int`, `float64`, `string`, and friends — use the\ntype-safe generic API directly:\n\n```go\nimport \"github.com/aalpar/deheap\"\n\n// Construct from existing elements.\nh := deheap.From(5, 3, 8, 1, 9)\n\n// Or build incrementally.\nh := deheap.New[int]()\nh.Push(5)\nh.Push(3)\n\n// O(1) access to both extrema.\nfmt.Println(h.Peek())    // smallest\nfmt.Println(h.PeekMax()) // largest\n\n// O(log n) removal from either end.\nmin := h.Pop()\nmax := h.PopMax()\n\n// Remove by index.\nval := h.Remove(2)\n\n// Update an element and restore heap order.\nh.Push(10)\nh.Push(20)\n// ... mutate the element at index 0 ...\nh.Fix(0)\n\n// Check if the heap is valid.\nfmt.Println(h.Verify()) // true\n```\n\n### Interface API\n\nFor custom types, implement `heap.Interface` and use the package-level\nfunctions. This is the original v1 API and remains stable.\n\n```go\nimport \"github.com/aalpar/deheap\"\n\ntype IntHeap []int\n\nfunc (h IntHeap) Len() int           { return len(h) }\nfunc (h IntHeap) Less(i, j int) bool { return h[i] \u003c h[j] }\nfunc (h IntHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }\n\nfunc (h *IntHeap) Push(x interface{}) { *h = append(*h, x.(int)) }\n\nfunc (h *IntHeap) Pop() interface{} {\n    old := *h\n    n := len(old)\n    x := old[n-1]\n    *h = old[:n-1]\n    return x\n}\n\nh := \u0026IntHeap{2, 1, 5, 6}\ndeheap.Init(h)\ndeheap.Push(h, 3)\nmin := deheap.Pop(h)\nmax := deheap.PopMax(h)\n\n// Update an element and restore heap order.\n(*h)[0] = 42\ndeheap.Fix(h, 0)\n\n// Check if the heap is valid.\nfmt.Println(deheap.Verify(h)) // true\n```\n\n## Implementation\n\nThe underlying data structure is a **min-max heap** stored in a flat slice\nwith no pointers and no additional bookkeeping. Nodes on even levels\n(0, 2, 4, ...) satisfy the min-heap property with respect to their\ndescendants, and nodes on odd levels (1, 3, 5, ...) satisfy the max-heap\nproperty. The root is always the minimum; the maximum is one of its two\nchildren.\n\nLevel parity is determined by bit-length of the 1-based index, computed via\n`math/bits.Len` — a single CPU instruction on most architectures. Insertions\nbubble up through grandparent links; deletions bubble down through\ngrandchild links, with a secondary swap against the binary-tree parent when\nthe element crosses a level boundary.\n\nThe generic API implements the same algorithms directly on `[]T` with\nnative `\u003c` comparisons, eliminating interface dispatch, adapter allocation,\nand boxing overhead.\n\n### Complexity\n\n| Operation | Time     | Space |\n|-----------|----------|-------|\n| `Push`    | O(log n) | O(1) amortized |\n| `Pop`     | O(log n) | O(1) |\n| `PopMax`  | O(log n) | O(1) |\n| `Remove`  | O(log n) | O(1) |\n| `Fix`     | O(log n) | O(1) |\n| `Peek`    | O(1)     | O(1) |\n| `PeekMax` | O(1)     | O(1) |\n| `Init`    | O(n)     | O(1) |\n| `Verify`  | O(n)     | O(1) |\n\nStorage is a single contiguous slice — one element per slot, no child\npointers, no color bits, no auxiliary arrays. Memory overhead beyond the\nelements themselves is the slice header (24 bytes on 64-bit systems).\n\n### Benchmarks\n\nMeasured on Apple M4 Max (arm64), Go 1.23:\n\n**Generic API** (`Deheap[T]` — direct `\u003c` comparisons, no interface dispatch):\n\n| Operation   | ns/op  | B/op | allocs/op |\n|-------------|--------|------|-----------|\n| Push        | 12     | 45   | 0         |\n| Pop         | 225    | 0    | 0         |\n| PopMax      | 225    | 0    | 0         |\n\n**Interface API** (v1 — `heap.Interface`):\n\n| Operation   | ns/op  | B/op | allocs/op |\n|-------------|--------|------|-----------|\n| Push        | 21     | 54   | 0         |\n| Pop         | 288    | 7    | 0         |\n| PopMax      | 282    | 7    | 0         |\n\n**Standard library** (`container/heap`) for comparison:\n\n| Operation   | ns/op  | B/op | allocs/op |\n|-------------|--------|------|-----------|\n| Push        | 22     | 55   | 0         |\n| Pop         | 208    | 7    | 0         |\n\nThe generic API is faster than `container/heap` on Push. Pop is ~8% slower\ndue to examining up to four grandchildren per node (vs two children in a\nbinary heap), but provides O(log n) access to both extrema. The v1 interface\nAPI pays the same `heap.Interface` dispatch cost as `container/heap`. All\noperations are zero-allocation in steady state.\n\n#### Benchmark descriptions\n\nThe benchmarks compare deheap's two API surfaces against each other and\nagainst `container/heap`. Baseline measurements isolate the cost of slice\noperations from heap logic. All benchmarks use a heap of 10,000 `int`\nelements to ensure the working set fits in L1 cache while exercising the\nfull tree depth.\n\nSample run (Apple M4 Max, Go 1.23):\n\n```\n$ go test -bench . -benchmem\ngoos: darwin\ngoarch: arm64\npkg: github.com/aalpar/deheap\ncpu: Apple M4 Max\nBenchmarkMin4-16              \t261428533\t         4.784 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkBaselinePush-16      \t1000000000\t         6.411 ns/op\t      42 B/op\t       0 allocs/op\nBenchmarkPush-16              \t44383689\t        23.16 ns/op\t      50 B/op\t       0 allocs/op\nBenchmarkPop-16               \t 5666707\t       326.3 ns/op\t       7 B/op\t       0 allocs/op\nBenchmarkPopMax-16            \t 5644575\t       319.7 ns/op\t       7 B/op\t       0 allocs/op\nBenchmarkPushPop-16           \t 5098502\t       297.2 ns/op\t      65 B/op\t       1 allocs/op\nBenchmarkHeapPushPop-16       \t 6203408\t       256.5 ns/op\t      56 B/op\t       1 allocs/op\nBenchmarkHeapPop-16           \t 8475319\t       234.7 ns/op\t       7 B/op\t       0 allocs/op\nBenchmarkHeapPush-16          \t46837188\t        21.57 ns/op\t      48 B/op\t       0 allocs/op\nBenchmarkOrderedPush-16       \t100000000\t        12.38 ns/op\t      45 B/op\t       0 allocs/op\nBenchmarkOrderedPop-16        \t 8680756\t       249.4 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkOrderedPopMax-16     \t10103798\t       237.3 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkOrderedPushPop-16    \t 8926204\t       233.2 ns/op\t      44 B/op\t       0 allocs/op\nPASS\n```\n\n| Benchmark | What it measures |\n|-----------|-----------------|\n| `OrderedPush` | Generic `Deheap[int].Push`: append + bubble-up with direct `\u003c` comparisons. |\n| `OrderedPop` | Generic `Deheap[int].Pop`: remove minimum and bubble-down with direct `\u003c`. |\n| `OrderedPopMax` | Generic `Deheap[int].PopMax`: remove maximum and bubble-down with direct `\u003c`. |\n| `OrderedPushPop` | Generic push-then-drain throughput. |\n| `Min4` | Cost of `min4`, the internal function that finds the extremum among up to 4 grandchildren during bubble-down. |\n| `BaselinePush` | Raw slice append with no heap ordering — establishes the floor cost of memory allocation and copying. |\n| `Push` | `deheap.Push` (v1): append + bubble-up via `heap.Interface`. |\n| `Pop` | `deheap.Pop` (v1): remove the minimum element and bubble-down via `heap.Interface`. |\n| `PopMax` | `deheap.PopMax` (v1): remove the maximum element and bubble-down via `heap.Interface`. |\n| `PushPop` | v1 push-then-drain throughput. |\n| `HeapPushPop` | Same push-then-drain pattern using `container/heap` for direct comparison. |\n| `HeapPop` | `container/heap.Pop` in isolation, comparable to `Pop` above. |\n| `HeapPush` | `container/heap.Push` in isolation, comparable to `Push` above. |\n\n## Testing\n\nThe test suite includes 54 test functions covering internal helpers,\nalgorithmic correctness, edge cases (empty, single-element, two-element\nheaps), and large-scale randomized validation. Six native Go fuzz targets\n(`testing.F`) exercise both API surfaces under arbitrary input. Tests are run\nagainst Go 1.21, 1.22, and 1.23 in CI.\n\n```bash\ngo test ./...          # run all tests\ngo test -bench . ./... # run benchmarks\ngo test -fuzz .        # run fuzz tests\n```\n\n## Requirements\n\n- Go 1.21 or later (generic API)\n- Go 1.13 or later (interface API only)\n- Zero external dependencies\n\n## References\n\n1. M.D. Atkinson, J.-R. Sack, N. Santoro, and T. Strothotte. \"Min-Max\n   Heaps and Generalized Priority Queues.\" *Communications of the ACM*,\n   29(10):996–1000, October 1986.\n   https://doi.org/10.1145/6617.6621\n\n2. J. van Leeuwen and D. Wood. \"Interval Heaps.\" *The Computer Journal*,\n   36(3):209–216, 1993.\n\n3. P. Brass. *Advanced Data Structures*. Cambridge University Press, 2008.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","funding_links":[],"categories":["Data Structures and Algorithms","Data Integration Frameworks"],"sub_categories":["Queues"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faalpar%2Fdeheap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faalpar%2Fdeheap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faalpar%2Fdeheap/lists"}