Skip to content

Commit de61ff7

Browse files
authored
Make the namings consitent (#6)
1 parent 9b00244 commit de61ff7

22 files changed

+634
-432
lines changed

README.md

Lines changed: 119 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,51 @@
1616
[![Release](https://img.shields.io/github/release/CogitatorTech/ordered.svg?label=release&style=flat&labelColor=282c34&logo=github)](https://github.com/CogitatorTech/ordered/releases/latest)
1717
[![License](https://img.shields.io/badge/license-MIT-007ec6?label=license&style=flat&labelColor=282c34&logo=open-source-initiative)](https://github.com/CogitatorTech/ordered/blob/main/LICENSE)
1818

19-
Pure Zig implementations of high-performance, memory-safe ordered data structures
19+
A sorted collection library for Zig
2020

2121
</div>
2222

2323
---
2424

25-
Ordered is a Zig library that provides efficient implementations of various popular data structures including
26-
B-tree, skip list, trie, and red-black tree for Zig programming language.
25+
Ordered is a Zig library that provides fast and efficient implementations of various data structures that keep elements
26+
sorted (AKA sorted collections).
27+
It is written in pure Zig and has no external dependencies.
28+
Ordered is inspired by [Java collections](https://en.wikipedia.org/wiki/Java_collections_framework) and sorted
29+
containers in the [C++ standard library](https://en.cppreference.com/w/cpp/container), and aims to provide a similar
30+
experience in Zig.
2731

2832
### Features
2933

30-
To be added.
34+
- Simple and uniform API for all data structures
35+
- Pure Zig implementations with no external dependencies
36+
- Fast and memory-efficient implementations (see [benches](benches))
3137

3238
### Data Structures
3339

34-
| Data Structure | Build Complexity | Memory Complexity | Search Complexity |
35-
|------------------------------------------------------------------------|------------------|-------------------|-------------------|
36-
| [B-tree](https://en.wikipedia.org/wiki/B-tree) | $O(\log n)$ | $O(n)$ | $O(\log n)$ |
37-
| [Cartesian tree](https://en.wikipedia.org/wiki/Cartesian_tree) | $O(\log n)$\* | $O(n)$ | $O(\log n)$\* |
38-
| [Red-black tree](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree) | $O(\log n)$ | $O(n)$ | $O(\log n)$ |
39-
| [Skip list](https://en.wikipedia.org/wiki/Skip_list) | $O(\log n)$\* | $O(n)$ | $O(\log n)$\* |
40-
| Sorted set | $O(n)$ | $O(n)$ | $O(\log n)$ |
41-
| [Trie](https://en.wikipedia.org/wiki/Trie) | $O(m)$ | $O(n \cdot m)$ | $O(m)$ |
40+
Ordered provides two main interfaces for working with sorted collections: sorted maps and sorted sets.
41+
At the moment, Ordered supports the following implementations of these interfaces:
4242

43-
- $n$: number of stored elements
44-
- $m$: maximum length of a key
45-
- \*: average case complexity
43+
#### Maps (Key-value)
44+
45+
| Type | Data Structure | Insert | Search | Delete | Space |
46+
|--------------------|------------------------------------------------------|--------------|--------------|--------------|----------------|
47+
| `BTreeMap` | [B-tree](https://en.wikipedia.org/wiki/B-tree) | $O(\log n)$ | $O(\log n)$ | $O(\log n)$ | $O(n)$ |
48+
| `SkipListMap` | [Skip list](https://en.wikipedia.org/wiki/Skip_list) | $O(\log n)$† | $O(\log n)$† | $O(\log n)$† | $O(n)$ |
49+
| `TrieMap` | [Trie](https://en.wikipedia.org/wiki/Trie) | $O(m)$ | $O(m)$ | $O(m)$ | $O(n \cdot m)$ |
50+
| `CartesianTreeMap` | [Treap](https://en.wikipedia.org/wiki/Treap) | $O(\log n)$† | $O(\log n)$† | $O(\log n)$† | $O(n)$ |
51+
52+
#### Sets (Value-only)
53+
54+
| Type | Data Structure | Insert | Search | Delete | Space |
55+
|-------------------|----------------------------------------------------------------|-------------|-------------|-------------|--------|
56+
| `SortedSet` | [Sorted array](https://en.wikipedia.org/wiki/Sorted_array) | $O(n)$ | $O(\log n)$ | $O(n)$ | $O(n)$ |
57+
| `RedBlackTreeSet` | [Red-black tree](https://en.wikipedia.org/wiki/Red-black_tree) | $O(\log n)$ | $O(\log n)$ | $O(\log n)$ | $O(n)$ |
58+
59+
- $n$ = number of elements stored
60+
- $m$ = length of the key (for string-based keys)
61+
- † = average case complexity (the worst case is $O(n)$)
62+
63+
See the [ROADMAP.md](ROADMAP.md) for the list of implemented and planned features.
4664

4765
> [!IMPORTANT]
4866
> Ordered is in early development, so bugs and breaking API changes are expected.
@@ -52,7 +70,91 @@ To be added.
5270

5371
### Getting Started
5472

55-
To be added.
73+
You can add Ordered to your project and start using it by following the steps below.
74+
75+
#### Installation
76+
77+
Run the following command in the root directory of your project to download Ordered:
78+
79+
```sh
80+
zig fetch --save=ordered "https://github.com/CogitatorTech/ordered/archive/<branch_or_tag>.tar.gz"
81+
```
82+
83+
Replace `<branch_or_tag>` with the desired branch or release tag, like `main` (for the development version) or `v0.1.0`.
84+
This command will download Ordered and add it to Zig's global cache and update your project's `build.zig.zon` file.
85+
86+
#### Adding to Build Script
87+
88+
Next, modify your `build.zig` file to make Ordered available to your build target as a module.
89+
90+
```zig
91+
const std = @import("std");
92+
93+
pub fn build(b: *std.Build) void {
94+
const target = b.standardTargetOptions(.{});
95+
const optimize = b.standardOptimizeOption(.{});
96+
97+
// 1. Get the dependency object from the builder
98+
const ordered_dep = b.dependency("ordered", .{});
99+
100+
// 2. Create a module for the dependency
101+
const ordered_module = ordered_dep.module("ordered");
102+
103+
// 3. Create your executable module and add ordered as import
104+
const exe_module = b.createModule(.{
105+
.root_source_file = b.path("src/main.zig"),
106+
.target = target,
107+
.optimize = optimize,
108+
});
109+
exe_module.addImport("ordered", ordered_module);
110+
111+
// 4. Create executable with the module
112+
const exe = b.addExecutable(.{
113+
.name = "your-application",
114+
.root_module = exe_module,
115+
});
116+
117+
b.installArtifact(exe);
118+
}
119+
```
120+
121+
#### Using Ordered in Your Project
122+
123+
Finally, you can `@import("ordered")` and start using it in your Zig code.
124+
125+
```zig
126+
const std = @import("std");
127+
const ordered = @import("ordered");
128+
129+
// Define a comparison function for the keys.
130+
// The function must return a `std.math.Order` value based on the comparison of the two keys
131+
fn strCompare(lhs: []const u8, rhs: []const u8) std.math.Order {
132+
return std.mem.order(u8, lhs, rhs);
133+
}
134+
135+
pub fn main() !void {
136+
const allocator = std.heap.page_allocator;
137+
138+
std.debug.print("## BTreeMap Example ##\n", .{});
139+
const B = 4; // Branching Factor for B-tree
140+
var map = ordered.BTreeMap([]const u8, u32, strCompare, B).init(allocator);
141+
defer map.deinit();
142+
143+
try map.put("banana", 150);
144+
try map.put("apple", 100);
145+
try map.put("cherry", 200);
146+
147+
const key_to_find = "apple";
148+
if (map.get(key_to_find)) |value_ptr| {
149+
std.debug.print("Found key '{s}': value is {d}\n", .{ key_to_find, value_ptr.* });
150+
}
151+
152+
const removed = map.remove("banana");
153+
std.debug.print("Removed 'banana' with value: {?d}\n", .{if (removed) |v| v else null});
154+
std.debug.print("Contains 'banana' after remove? {any}\n", .{map.contains("banana")});
155+
std.debug.print("Map count: {d}\n\n", .{map.count()});
156+
}
157+
```
56158

57159
---
58160

@@ -70,7 +172,7 @@ Check out the [examples](examples) directory for example usages of Ordered.
70172

71173
### Benchmarks
72174

73-
To be added.
175+
Check out the [benchmarks](benches) directory for local benchmarks.
74176

75177
---
76178

ROADMAP.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## Feature Roadmap
2+
3+
This document includes the roadmap for the Ordered library.
4+
It outlines features to be implemented and their current status.
5+
6+
> [!IMPORTANT]
7+
> This roadmap is a work in progress and is subject to change.
8+
9+
10+
### 1. Core Features
11+
12+
* **Collections (implemented)**
13+
* [x] B-tree map (`BTreeMap`)
14+
* [x] Skip list map (`SkipListMap`)
15+
* [x] Trie map (`TrieMap`)
16+
* [x] Cartesian tree (`CartesianTreeMap`)
17+
* [x] Array-based sorted set (`SortedSet`)
18+
* [x] Red-black tree set (`RedBlackTreeSet`)
19+
* **Common API**
20+
* [x] `init` and `deinit` lifecycle
21+
* [x] `put`, `get`, `remove`, and `contains`
22+
* [x] Iterators (in-order traversal)
23+
* [x] Size and emptiness checks

benches/README.md

Lines changed: 28 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,39 @@
1-
## Benchmarks
2-
3-
This directory contains benchmarks for the data structures in the `ordered` library.
4-
5-
### Running Benchmarks
6-
7-
Each benchmark can be run using the following command pattern:
8-
9-
```bash
10-
zig build bench-<benchmark_name>
11-
```
1+
### Ordered Benchmarks
122

133
#### Available Benchmarks
144

15-
- **BTreeMap**: `zig build bench-btree_map_bench`
16-
- **SortedSet**: `zig build bench-sorted_set_bench`
17-
- **RedBlackTree**: `zig build bench-red_black_tree_bench`
18-
- **SkipList**: `zig build bench-skip_list_bench`
19-
- **Trie**: `zig build bench-trie_bench`
20-
- **CartesianTree**: `zig build bench-cartesian_tree_bench`
21-
22-
### What Each Benchmark Tests
23-
24-
#### BTreeMap Benchmark
25-
- **Insert**: Sequential insertion of integers
26-
- **Lookup**: Finding all inserted keys
27-
- **Delete**: Removing all keys
28-
29-
#### SortedSet Benchmark
30-
- **Add**: Adding elements while maintaining a sorted order
31-
- **Contains**: Checking if elements exist
32-
- **Remove**: Removing elements from the set
5+
| # | File | Description |
6+
|---|--------------------------------------------------------|--------------------------------------------------|
7+
| 1 | [b1_btree_map.zig](b1_btree_map.zig) | Benchmarks for B-tree map implementation |
8+
| 2 | [b2_sorted_set.zig](b2_sorted_set.zig) | Benchmarks for Sorted set implementation |
9+
| 3 | [b3_red_black_tree_set.zig](b3_red_black_tree_set.zig) | Benchmarks for Red-black tree set implementation |
10+
| 4 | [b4_skip_list_map.zig](b4_skip_list_map.zig) | Benchmarks for Skip list map implementation |
11+
| 5 | [b5_trie_map.zig](b5_trie_map.zig) | Benchmarks for Trie map implementation |
12+
| 6 | [b6_cartesian_tree_map.zig](b6_cartesian_tree_map.zig) | Benchmarks for Cartesian tree map implementation |
3313

34-
#### RedBlackTree Benchmark
35-
- **Insert**: Inserting nodes with self-balancing
36-
- **Find**: Searching for nodes
37-
- **Remove**: Deleting nodes while maintaining balance
38-
- **Iterator**: In-order traversal performance
14+
#### Running Benchmarks
3915

40-
#### SkipList Benchmark
41-
- **Put**: Inserting key-value pairs with probabilistic levels
42-
- **Get**: Retrieving values by key
43-
- **Delete**: Removing key-value pairs
44-
45-
#### Trie Benchmark
46-
- **Put**: Inserting strings with associated values
47-
- **Get**: Retrieving values by string key
48-
- **Contains**: Checking if strings exist
49-
- **Prefix Search**: Finding all keys with a common prefix
50-
51-
#### CartesianTree Benchmark
52-
- **Put**: Inserting key-value pairs with random priorities
53-
- **Get**: Retrieving values by key
54-
- **Remove**: Deleting nodes
55-
- **Iterator**: In-order traversal performance
56-
57-
### Benchmark Sizes
58-
59-
Each benchmark tests with multiple dataset sizes:
60-
- Small: 1,000 items
61-
- Medium: 10,000 items
62-
- Large: 50,000 - 100,000 items (varies by data structure)
63-
64-
### Build Configuration
65-
66-
Benchmarks are compiled with `ReleaseFast` optimization mode for accurate performance measurements.
67-
68-
### Example Output
16+
To execute a specific benchmark, run:
6917

18+
```sh
19+
zig build bench-{FILE_NAME_WITHOUT_EXTENSION}
7020
```
71-
=== BTreeMap Benchmark ===
7221

73-
Insert 1000 items: 0.42 ms (420 ns/op)
74-
Lookup 1000 items: 0.18 ms (180 ns/op, found: 1000)
75-
Delete 1000 items: 0.35 ms (350 ns/op)
22+
For example:
7623

77-
Insert 10000 items: 5.23 ms (523 ns/op)
78-
Lookup 10000 items: 2.10 ms (210 ns/op, found: 10000)
79-
Delete 10000 items: 4.15 ms (415 ns/op)
24+
```sh
25+
zig build bench-b1_btree_map
8026
```
8127

82-
### Notes
83-
84-
- All benchmarks use a simple integer or string key type for consistency
85-
- Times are reported in both total milliseconds and nanoseconds per operation
86-
- Memory allocations use `GeneralPurposeAllocator` for simulating a more realistic memory usage
87-
- Results may vary based on a hardware and system load
28+
> [!NOTE]
29+
> Each benchmark measures three core operations across multiple data sizes:
30+
> 1. **Insert and Put**: measures the time to insert elements sequentially into an empty data structure
31+
> 2. **Lookup**: measures the time to search for all elements in a pre-populated structure
32+
> 3. **Delete**: measures the time to remove all elements from a pre-populated structure
33+
>
34+
> **Test Sizes**: benchmarks run with 1,000, 10,000, 100,000, and 1,000,000 elements to show performance scaling.
35+
>
36+
> **Timing Method**: uses `std.time.Timer` for high-precision nanosecond-level timing. Each operation is timed in bulk,
37+
> then divided by the number of operations to get per-operation timing.
38+
>
39+
> **Compilation**: benchmarks are compiled with `ReleaseFast` optimization (see [build.zig](../build.zig)).

benches/b1_btree_map.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub fn main() !void {
99

1010
std.debug.print("=== BTreeMap Benchmark ===\n\n", .{});
1111

12-
const sizes = [_]usize{ 1000, 10_000, 100_000 };
12+
const sizes = [_]usize{ 1000, 10_000, 100_000, 1_000_000 };
1313

1414
inline for (sizes) |size| {
1515
try benchmarkInsert(allocator, size);

benches/b2_sorted_set.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub fn main() !void {
99

1010
std.debug.print("=== SortedSet Benchmark ===\n\n", .{});
1111

12-
const sizes = [_]usize{ 1000, 10_000, 50_000 };
12+
const sizes = [_]usize{ 1000, 10_000, 100_000, 1_000_000 };
1313

1414
inline for (sizes) |size| {
1515
try benchmarkAdd(allocator, size);

benches/b3_red_black_tree.zig renamed to benches/b3_red_black_tree_set.zig

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub fn main() !void {
99

1010
std.debug.print("=== RedBlackTree Benchmark ===\n\n", .{});
1111

12-
const sizes = [_]usize{ 1000, 10_000, 100_000 };
12+
const sizes = [_]usize{ 1000, 10_000, 100_000, 1_000_000 };
1313

1414
inline for (sizes) |size| {
1515
try benchmarkInsert(allocator, size);
@@ -28,7 +28,7 @@ const Context = struct {
2828
};
2929

3030
fn benchmarkInsert(allocator: std.mem.Allocator, size: usize) !void {
31-
var tree = ordered.RedBlackTree(i32, Context).init(allocator, Context{});
31+
var tree = ordered.RedBlackTreeSet(i32, Context).init(allocator, Context{});
3232
defer tree.deinit();
3333

3434
var timer = try Timer.start();
@@ -50,7 +50,7 @@ fn benchmarkInsert(allocator: std.mem.Allocator, size: usize) !void {
5050
}
5151

5252
fn benchmarkFind(allocator: std.mem.Allocator, size: usize) !void {
53-
var tree = ordered.RedBlackTree(i32, Context).init(allocator, Context{});
53+
var tree = ordered.RedBlackTreeSet(i32, Context).init(allocator, Context{});
5454
defer tree.deinit();
5555

5656
var i: i32 = 0;
@@ -79,7 +79,7 @@ fn benchmarkFind(allocator: std.mem.Allocator, size: usize) !void {
7979
}
8080

8181
fn benchmarkRemove(allocator: std.mem.Allocator, size: usize) !void {
82-
var tree = ordered.RedBlackTree(i32, Context).init(allocator, Context{});
82+
var tree = ordered.RedBlackTreeSet(i32, Context).init(allocator, Context{});
8383
defer tree.deinit();
8484

8585
var i: i32 = 0;
@@ -106,7 +106,7 @@ fn benchmarkRemove(allocator: std.mem.Allocator, size: usize) !void {
106106
}
107107

108108
fn benchmarkIterator(allocator: std.mem.Allocator, size: usize) !void {
109-
var tree = ordered.RedBlackTree(i32, Context).init(allocator, Context{});
109+
var tree = ordered.RedBlackTreeSet(i32, Context).init(allocator, Context{});
110110
defer tree.deinit();
111111

112112
var i: i32 = 0;

0 commit comments

Comments
 (0)