From 76edfae0e0a1da7435f8f0e30a53ae4a70c74ba7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 4 Feb 2022 12:08:20 +0000 Subject: [PATCH] `core:container/topological_sort` --- .../topological_sort/topological_sort.odin | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 core/container/topological_sort/topological_sort.odin diff --git a/core/container/topological_sort/topological_sort.odin b/core/container/topological_sort/topological_sort.odin new file mode 100644 index 000000000..4b69930d5 --- /dev/null +++ b/core/container/topological_sort/topological_sort.odin @@ -0,0 +1,98 @@ +// The following is a generic O(V+E) topological sorter implementation. +// This is the fastest known method for topological sorting and Odin's +// map type is being used to accelerate lookups. +package container_topological_sort + +import "core:intrinsics" +import "core:runtime" +_ :: intrinsics +_ :: runtime + + +Relations :: struct($K: typeid) where intrinsics.type_is_valid_map_key(K) { + dependents: map[K]bool, + dependencies: int, +} + +Sorter :: struct(K: typeid) where intrinsics.type_is_valid_map_key(K) { + relations: map[K]Relations(K), + dependents_allocator: runtime.Allocator, +} + +@(private="file") +make_relations :: proc(sorter: ^$S/Sorter($K)) -> (r: Relations(K)) { + r.dependents.allocator = sorter.dependents_allocator + return +} + + +init :: proc(sorter: ^$S/Sorter($K)) { + sorter.relations = make(map[K]Relations(K)) + sorter.dependents_allocator = context.allocator +} + +destroy :: proc(sorter: ^$S/Sorter($K)) { + for _, v in &sorter.relations { + delete(v.dependents) + } + delete(sorter.relations) +} + +add_key :: proc(sorter: ^$S/Sorter($K), key: K) -> bool { + if key in sorter.relations { + return false + } + sorter.relations[key] = make_relations(sorter) + return true +} + +add_dependency :: proc(sorter: ^$S/Sorter($K), key, dependency: K) -> bool { + if key == dependency { + return false + } + + find := &sorter.relations[dependency] + if find == nil { + find = map_insert(&sorter.relations, dependency, make_relations(sorter)) + } + + if find.dependents[key] { + return true + } + find.dependents[key] = true + + find = &sorter.relations[key] + if find == nil { + find = map_insert(&sorter.relations, key, make_relations(sorter)) + } + + find.dependencies += 1 + + return true +} + +sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) { + relations := &sorter.relations + + for k, v in relations { + if v.dependencies == 0 { + append(&sorted, k) + } + } + + for root in &sorted do for k, _ in relations[root].dependents { + relation := &relations[k] + relation.dependencies -= 1 + if relation.dependencies == 0 { + append(&sorted, k) + } + } + + for k, v in relations { + if v.dependencies != 0 { + append(&cycled, k) + } + } + + return +} \ No newline at end of file