Skip to content

Latest commit

 

History

History
98 lines (74 loc) · 5.61 KB

AGENTS.md

File metadata and controls

98 lines (74 loc) · 5.61 KB

Numerical Toolbox — Agent Rules (canonical)

Single source of truth for Claude and Copilot. CLAUDE.md and .github/copilot-instructions.md point here. Code-file specifics load on demand from .github/instructions/. Deployment recipe: roadmap/DEPLOYMENT.md.

Numerical algorithms library (DSP, control, filters, estimators, solvers) for resource-constrained embedded systems. Real-time, deterministic, no heap.

Numeric policy — FLOAT-ONLY (current)

  • New algorithms are float-only. Write generic template<typename T>, add static_assert(std::is_floating_point_v<T>, "..."), and instantiate/test float only.
  • Do NOT implement Q15/Q31 now. The generic T keeps a fixed-point specialisation cheap later.
  • std::numbers::pi_v<float> — never hardcode constants.

Memory — no heap

  • Forbidden: new/delete/malloc/free, make_unique/make_shared, std::vector/string/deque/list/map/set.
  • Use: infra::BoundedVector<T>::WithMaxSize<N>, infra::BoundedString::WithStorage<N>, infra::BoundedDeque<T>::WithMaxSize<N>, infra::BoundedList<T>::WithMaxSize<N>, std::array<T,N>, std::optional<T>. Stack/static only. No recursion. Tests too.

Embedded optimizations

  • No #pragma GCC optimize and no optimize attribute, in headers or sources. GCC does not inline a callee whose optimization options differ from its caller's, so options set per header or per function turn every small helper on a hot path (element access, accessors, math:: wrappers) into an out-of-line call.
  • The optimization level and floating-point model belong to the consumer and apply to whole translation units. For embedded targets: -O2 or -O3 with -ffast-math -fno-finite-math-only.
  • OPTIMIZE_FOR_SPEED on hot paths (Filter/Compute/Update/Solve/Step). Pure interfaces exempt. With NumericalToolbox_ENABLE_OPTIMIZATIONS it forces inlining (always_inline, hot, inline); without it, it expands to nothing.
  • Use math::IsFinite for finiteness checks: a consumer may still build with -ffinite-math-only.

Style

  • Allman braces, 4-space indent, .clang-format authoritative.
  • Brace-init {} everywhere. PascalCase types/methods, camelCase members, lowercase namespaces.
  • Functions ≤ ~30 lines. const/constexpr-correct. Fixed-width ints. Full descriptive names.
  • No comments except license headers, NOLINT, and a brief note on genuinely non-obvious math.

Interfaces & errors

  • Interfaces = pure virtual classes with a protected:, non-virtual ~I() = default — never a virtual or = 0 destructor. A virtual one puts a deleting destructor in every vtable, which links operator delete/free into heap-free firmware.
  • Concrete classes declare no destructor unless they need one. Never own or delete through an interface pointer (no std::unique_ptr<I>): hold the concrete type, pass I&.
  • No exceptions. Use std::optional<T> or status enums. assert/really_assert for preconditions.
  • SOLID + DIP: constructor injection; depend on abstractions. RAII. No virtual calls in real-time paths.

Namespaces

analysis, windowing, control_analysis, controllers, estimators, filters (active Kalman family — not filters::active), filters::passive, math, optimization, regularization, solvers, and new: robust_control, nonlinear_control, motion_profiles.

Math functions

  • Production code calls math::Sin, math::Cos, math::Abs, etc. from numerical/math/Math.hpp. Never call std:: cmath functions directly in production.
  • If a needed <cmath> function is missing from Math.hpp, add a constexpr wrapper there first, then use math::FunctionName at the call site.
  • Each function is individually overridable at compile time via #ifndef MATH_<NAME>_OVERRIDE (e.g. MATH_SIN_OVERRIDE). Define the macro in CMake and supply your own template definition.
  • Unit tests call std::sin, std::cos, std::abs, etc. directly. Never use math:: in tests.

Testing

  • GoogleTest. TEST_F on float — no TYPED_TEST, no multi-type. Never plain TEST().
  • StrictMock only (no NiceMock/bare). Fixture + aliases in an anonymous namespace; macros outside it.
  • No redundant tests — implement exactly the spec's enumerated cases, one behavior per test, Arrange/Act/Assert, EXPECT_NEAR + math::Tolerance<float>(). No heap.
  • Coverage ≥ 90 % — measured by SonarQube. Every new algorithm must reach this threshold before merge; CI fails below it.

CMake (numerical/ targets)

  • numerical_add_header_library(<target>), numerical_add_coverage_sources(<target> <Name>.cpp), ${NUMERICAL_VISIBILITY}.
  • Coverage .cpp: template class <Name><float, ...>;. extern template guarded by #ifdef NUMERICAL_TOOLBOX_COVERAGE_BUILD.
  • NumericalToolbox_ENABLE_OPTIMIZATIONS (enables OPTIMIZE_FOR_SPEED) is a usage requirement of numerical.math, gated by NUMERICAL_TOOLBOX_ENABLE_OPTIMIZATIONS. Never move it to add_definitions, and downstream repos must not rename it: link numerical.* to inherit it.

Docs

Design-first doc/<domain>/<Name>.md per doc/TEMPLATE.md. Math/theory/complexity/pitfalls — no code, no class names, no usage examples. Update doc/<domain>/README.md when adding a new algorithm.

Assistant behavior — be terse

  • Minimal prose. No preamble/postamble, no restating the plan, no summaries unless asked.
  • Report results as file paths + pass/fail. Don't narrate routine tool calls.
  • Don't re-read files already read; batch reads; prefer targeted edits.
  • Build: cmake --preset host && cmake --build --preset host · Test: ctest --preset host.