aboutsummaryrefslogtreecommitdiff
path: root/STYLE.md
blob: 856287f3ca22e453bcdb1fdd2a60cfa50705acf9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# psi C style guide

This document describes the conventions for the C implementation of `psi`. Formatting
is enforced by [`.clang-format`](.clang-format) and naming by [`.clang-tidy`](.clang-tidy);
everything a tool cannot check is described here and is expected in review.

The guiding idea: **idiomatic, data-driven C that reads like the Rust original in naming**,
with Allman bracing as the one deliberate departure from typical C style.

---

## 1. Language & build

- **C17**, compiled with `-Wall -Wextra -Werror -pedantic`. Warnings are errors; keep every
  build clean.
- Build system is **premake5** (`premake5 gmake && make config=release`). Public headers live
  in `include/`, implementation in `src/`, the test runner in `tester/`.
- Portable across x86_64 and ARM64 (the only supported architectures). Platform-specific code
  (SIMD intrinsics) is `#ifdef`-gated with a scalar fallback.

---

## 2. Formatting (enforced by clang-format)

Run before committing:

```bash
find include src tester -name '*.c' -o -name '*.h' | xargs clang-format -i
```

CI check (no diffs allowed):

```bash
find include src tester -name '*.c' -o -name '*.h' | xargs clang-format --dry-run -Werror
```

### Braces — Allman, everywhere

Opening brace on its own line for functions, structs, enums, unions, and control blocks.

```c
struct PsiComplex
{
	double real;
	double imaginary;
};

double psi_abs_complex(struct PsiComplex z)
{
	return sqrt(psi_norm2_complex(z));
}
```

**Exception (tool limitation):** compound literals keep the brace on the same line as the
type, because clang-format cannot break it:

```c
return (struct PsiComplex){
	a.real + b.real,
	a.imaginary + b.imaginary,
};
```

### Single statements omit braces

An `if`/`for`/`while` with a single-statement body has no braces; the statement goes on the
next line.

```c
if (index >= row->width)
	return;

for (size_t i = 0; i < count; i++)
	sum = psi_add_complex(sum, values[i]);
```

Use braces once a body has more than one statement.

### Indentation — tabs

Tabs for indentation (width 4). Alignment (e.g. wrapped arguments under an open paren) uses
spaces, so alignment survives any tab width.

### Pointers bind to the type

```c
struct PsiComplex* data;
void psi_free_vector(struct PsiVector* v);
const size_t* targets;
```

Not `struct PsiComplex *data`.

### Line length & wrapping

- 100-column limit. Long lines wrap; continuation indents two tabs, wrapped call/parameter
  lists align under the opening paren.

```c
static void execute_kernels(struct PsiVector* state, const struct PsiKernel* kernels,
						    size_t count, size_t num_qubits, struct PsiRuntimeConfig config)
{
	...
}
```

### Includes

Not sorted by the tool — order them yourself. Convention: system headers (`<...>`) first, a
blank line, then project headers (`"..."`). `include/psi.h` orders its headers by dependency
layer, not alphabetically.

---

## 3. Naming (enforced by clang-tidy)

| Kind | Convention | Example |
|------|-----------|---------|
| Structs | `Psi` + PascalCase | `struct PsiQuantumRegister` |
| Enums | `Psi` + PascalCase | `enum PsiGateType` |
| Enum constants | `UPPER_CASE` | `PSI_GATE_H`, `PSI_SIMD_NEON` |
| Functions | `snake_case` | `psi_add_complex`, `apply_kernel` |
| Parameters / variables | `snake_case` | `num_qubits`, `target_bit` |
| Macros / compile-time consts | `THIS_CASE` | `PSI_VERSION_MAJOR`, `INV_SQRT_2` |
| Function-like macros | `psi_` + `snake_case` | `psi_matrix`, `psi_column_vector` |

### Library prefix

Every **public** symbol is namespaced:

- functions → `psi_...`
- structs → `Psi...`
- macros / constants → `PSI_...`

File-local `static` helpers are **not** prefixed (`apply_pair`, `build_pairs`, `op_to_kernel`).

### Data-driven, action-first (not OOP)

The operation leads and the data is an argument — think free functions over data, not methods
bound to a type. `psi_<operation>_<type>(...)`:

```c
psi_add_complex(a, b)       /* not psi_complex_add   */
psi_new_quantum_gate(...)   /* not gate_new(...)     */
psi_apply_gate(&reg, ...)   /* verb first            */
```

*Not enforceable by tooling — maintained in review.*

---

## 4. Types

- **`double` only** for floating point. No `float`, no fixed-width float aliases (their sizes
  are not guaranteed across platforms).
- **Fixed-width `<stdint.h>`** types (`uint32_t`, `int64_t`, …) over `int`/`unsigned`/`long`.
- **`size_t`** for sizes, counts, and indices (the Rust `usize`).
- **No `typedef` on structs, enums, or unions** — always spell `struct PsiComplex`,
  `enum PsiGateType`.

---

## 5. Memory & ownership

There is no garbage collector; ownership is explicit and follows a few rules.

- Every type that owns a heap allocation has a matching `psi_new_*` / `psi_free_*` pair, and
  `psi_free_*` takes a pointer and nulls the freed fields.

```c
struct PsiVector v = psi_new_vector(4, PSI_COLUMN_VECTOR);
...
psi_free_vector(&v);
```

- **The caller frees.** Functions that allocate and return a value (`psi_add_vector`,
  `psi_clone_matrix`, the gate factories, the renderers' returned strings) transfer ownership
  to the caller.
- **Read-only args are passed by value**, which shares the underlying buffer and never frees
  it. Mutators and destructors take a pointer.
- **Constructors that take ownership** consume what they are handed (e.g. a gate takes
  ownership of its matrix; `psi_apply_custom` takes ownership of the custom gate). Builders
  that take an existing value they should not consume make a copy (`psi_new_vector_from`,
  `psi_new_quantum_register_from`).
- **Allocation idiom:** `sizeof *ptr`, not the repeated type.

```c
struct PsiKernel* out = malloc(count * sizeof *out);
```

- Verify with sanitizers during development:

```bash
clang -std=c17 -Iinclude -Isrc -Itester -fsanitize=address,undefined -g \
	src/psi.c src/**/*.c tester/*.c -o /tmp/psi -lm && /tmp/psi
```

---

## 6. Error handling

- Preconditions and programmer errors (dimension mismatches, out-of-range indices) use
  `assert`. The Rust original returned `Option`/panicked; in C these are `assert`s, since they
  indicate caller bugs rather than recoverable conditions.
- `malloc`/`calloc`/`realloc` results are `assert`ed non-NULL (with `|| size == 0` where a
  zero-size allocation is legal).

---

## 7. Comments

- **Avoid narration.** Code should read on its own; do not annotate ported steps or restate
  what a line does.
- **Short math-formula comments are welcome** where they clarify an expression:

```c
struct PsiComplex psi_mul_complex(struct PsiComplex a, struct PsiComplex b)
{
	// (a + bi)(c + di) = (ac - bd) + (ad + bc)i
	return (struct PsiComplex){
		a.real * b.real - a.imaginary * b.imaginary,
		a.real * b.imaginary + a.imaginary * b.real,
	};
}
```

*Not enforceable by tooling — maintained in review.*

---

## 8. Project layout

```
include/          public API (mirrors src/ subtree)
  psi.h           umbrella header — includes the whole public API
  maths/          complex, vector, matrix, format, simd
  core/           quantum_components, gates, custom_gate, classical_components,
                  circuit, kernel, runtime, noise
  visualizer/     renderer
src/              implementation, same subtree; may also hold private headers
tester/           assertion-based test suite
```

- Headers are `#pragma once` and included **root-relative**: `#include "maths/complex.h"`.
- `include/psi.h` is the umbrella: `#include <psi.h>` pulls in the entire public API. Add each
  new module's public header to it.
- Private, implementation-only headers live under `src/` (e.g. `src/visualizer/grid.h`).

---

## 9. Testing

- `tester/` is an assertion suite with subcommands
  (`clifford`, `non-clifford`, `custom`, `kernels`, `simd`, `noise`, `all`, `help`).
- Tests assert against known states (Bell, GHZ, rotations, fusion, noise purity, …) and check
  that all runtimes agree. The process exit code reflects pass/fail (CI-friendly).

```bash
premake5 gmake && make config=release
./bin/release-<system>/tester            # all suites
./bin/release-<system>/tester noise      # one suite
```

---

## Tooling summary

| Concern | Tool | Command |
|---------|------|---------|
| Formatting | clang-format | `... | xargs clang-format --dry-run -Werror` |
| Naming | clang-tidy | `clang-tidy <file> -- -std=c17 -Iinclude -Isrc` |
| Build | premake5 + make | `premake5 gmake && make config=release` |
| Tests | tester | `./bin/release-<system>/tester` |
| Leaks / UB | sanitizers | build with `-fsanitize=address,undefined` |