Go to file
2026-05-29 21:16:48 +08:00
premake/bin Add premake binaries 2026-05-27 16:46:36 +08:00
scripts Add project configuration script for vs2022 2026-05-27 16:35:56 +08:00
src Rewrite 2026-05-29 21:16:48 +08:00
thirdparty/gtest Add google test 2026-05-28 10:55:30 +08:00
.gitignore Update gitignore 2026-05-27 16:36:09 +08:00
LICENSE.txt Init repo 2026-05-27 16:14:05 +08:00
premake5.lua Update project configuration 2026-05-28 13:26:45 +08:00
README.md Add README 2026-05-29 11:43:11 +08:00

tenvex

A header-only C++20 SIMD math library for high-performance 4D vector arithmetic.

tenvex uses expression templates — a C++ template metaprogramming technique that enables lazy, zero-overhead evaluation of chained vector operations. Complex expressions like norm(a + b * 2.0f) * dot(b, c) + c * 3.0f compile down to a single fused sequence of SSE/AVX intrinsics with no intermediate temporaries.

Features

  • Header-only — just include tenvex.h
  • Expression template engine with zero-cost abstractions
  • All arithmetic backed by SSE/AVX intrinsics (__m128)
  • C++20 concepts enforce type safety at compile time
  • Smart storage policy: small trivial types stored by value, others by const&
  • Supported operations: +, -, * (scalar), dot, cross, norm, magnitude

Requirements

  • C++20 compiler (MSVC, GCC, Clang)
  • x64 CPU with SSE2 support
  • Premake5 (binaries included for Windows, Linux, macOS)

Getting Started

tenvex is header-only. Copy src/tenvex/ into your project and include the single public header:

#include "tenvex.h"

Basic Usage

#include "tenvex.h"

using namespace tnvx;

vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f };
vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f };
vec4 c = { 1.0f, 0.0f, 1.0f, 0.0f };

// Basic arithmetic
vec4 sum       = a + b;
vec4 diff      = a - b;
vec4 scaled    = a * 2.0f;
vec4 scaled2   = 3.0f * b;

// Vector operations
float d        = dot(a, b);
vec4  cr       = cross(a, b);
vec4  n        = norm(a);
float mag      = magnitude(a);

// Compound expressions — evaluated lazily, no temporaries
vec4 result    = norm(a + b * 2.0f) * dot(b, c) + c * 3.0f;

vec4 Member Functions

vec4 v = { 1.0f, 2.0f, 3.0f, 4.0f };

float x   = v.x();           // component accessors
float y   = v.y();
float z   = v.z();
float w   = v.w();

auto arr  = v.data();        // std::array<float, 4>
float mag = v.magnitude();   // vector length
vec4  n   = v.norm();        // unit vector

Comparison

vec4 a = { 1.0f, 2.0f, 3.0f, 4.0f };
vec4 b = { 1.0f, 2.0f, 3.0f, 4.0f };

bool exact  = (a == b);                  // exact equality
bool approx = approx_eq(a, b);           // epsilon = 1e-3 (default)
bool approx = approx_eq(a, b, 1e-6f);   // custom epsilon

Architecture

Expression Templates

Every operation returns a lazy expression node rather than evaluating immediately. Nodes inherit from Expr<Derived> via CRTP and expose a single __m128 eval() const method. Evaluation only happens when the result is assigned to a vec4.

vec4 result = norm(a + b * 2.0f) * dot(b, c) + c * 3.0f;
//           └─ Mul<Norm<Add<vec4, Mul<vec4, Scalar>>>, Mul<Dot<vec4,vec4>, Mul<Scalar,...>>>
//              ↑ entire tree collapsed to ~5 SSE instructions at assignment

C++20 Concepts

Concept Meaning
expression<T> Any Expr<T> subtype with __m128 eval()
vector_expression<T> An expression that is not a Scalar

Storage Policy

To avoid dangling references to temporary expression nodes, storage is chosen automatically:

// trivially copyable & destructible & sizeof <= 16  →  stored by value
// otherwise                                          →  stored as const T&
template<typename T>
using tnvx_type_storage = std::conditional_t<tnvx_store_by_value<T>, T, const T&>;

Operation Nodes

Node Intrinsic(s) Notes
Add<L,R> _mm_add_ps
Sub<L,R> _mm_sub_ps
Mul<E,Scalar> _mm_mul_ps Scalar multiplication only
Dot<L,R> _mm_mul_ps + horizontal shuffle Result broadcast to all 4 lanes
Cross<L,R> _mm_mul_ps + _mm_sub_ps + shuffles 3-component; w = 0
Norm<E> _mm_div_ps + _mm_sqrt_ps Divide by magnitude
Magnitude<E> _mm_sqrt_ss Scalar result
Scalar _mm_set1_ps Broadcasts float to all 4 lanes

Building

The build system uses Premake5. Premake5 binaries are included in premake/bin/ for all platforms.

Windows (Visual Studio 2022)

scripts\config-vs2022.bat

Then open the generated solution in build/ and build the tenvex_tests project.

Other Platforms

Run the appropriate Premake5 binary manually:

# Linux
./premake/bin/linux/premake5 gmake2

# macOS
./premake/bin/macos/premake5 gmake2

Then:

make config=debug

Build output is written to bin/<system>/x64/<debug|release>/output/.

Running Tests

The test suite uses Google Test (vendored in thirdparty/gtest/) and covers 18 cases:

Category Tests
Basic operations add, sub, mult_a, mult_b, dot, cross, norm_a/b, magnitude_a/b
Compound expressions expression_a, expression_b, expression_c
Math properties cross_anticommutative, dot_commutative, norm_magnitude, dot_perpendicular, cross_parallel

Build and run tenvex_tests from the generated project or makefile.

Project Structure

tenvex/
├── src/
│   ├── tenvex/             # Library headers (header-only)
│   │   ├── tenvex.h        # Single public include
│   │   ├── vec4.h / vec4_impl.hpp
│   │   └── internal/       # Expression nodes and infrastructure
│   └── tests/
│       └── vec4_tests.cpp  # Google Test suite
├── thirdparty/
│   └── gtest/              # Vendored Google Test
├── premake/
│   └── bin/                # Premake5 binaries (Windows, Linux, macOS)
├── scripts/
│   └── config-vs2022.bat   # VS2022 project generation
└── premake5.lua            # Build configuration

License

BSD 3-Clause License. Copyright (c) 2026 Artem Zyktin. See LICENSE.txt.