5001 Commits

Author SHA1 Message Date
Mateusz Pusz e0e58b5588 fix: avoid shadowing unit symbols in polar/spherical facades
The single-letter locals `t`, `h`, and `s` in the polar/spherical vector
facades shadowed the `t` (tonne), `h` (hour), and `s` (second) unit symbols
brought into scope by tests, tripping MSVC's C4459 (warning-as-error). Rename
them to descriptive, non-colliding names (`half`, `incl`, `scalar`, and
`[norm_theta, norm_phi]` for the canonical-angle bindings).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 10:49:04 +02:00
Mateusz Pusz b57bba5594 fix: VERIFY_INTERFACE_HEADER_SETS fixed on deprecated utility headers 2026-07-05 23:52:24 +02:00
Mateusz Pusz 8fd725bc01 feat: generalize polar/spherical facades to any tuple-like vector rep
The polar_vector/spherical_vector conversion facades were tied to
cartesian_vector. Generalize them to accept and produce any
structured-bindings-compliant vector representation of the matching
dimension that models the exported utility::Vector concept:

- from-vector construction reads the dimension via std::tuple_size and
  the components via structured bindings
- to_cartesian<To>() takes the destination representation as a parameter,
  using parenthesized init and falling back to braced init for
  initializer_list-only reps such as Blaze's StaticVector
- both sides are gated by detail::VectorRepOf<To, N>

Add the tuple protocol (std::tuple_size / std::tuple_element and an
ADL-found get) for fixed-size Eigen vectors in the eigen integration, so
Eigen::Vector2d/Vector3d work directly as a Cartesian representation.
Blaze's StaticVector already ships the protocol, so its integration needs
nothing. Add a per-backend polar_spherical_integration_test exercising
both backends, and a how-to guide section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 23:26:36 +02:00
Mateusz Pusz 4c261bb33b feat(utility): polar_vector and spherical_vector conversion facades
Add `mp_units::utility::polar_vector` (2-D) and `spherical_vector` (3-D, ISO
physics convention) as *conversion facades* between native (radius + typed
angles) coordinates and a Cartesian `quantity<unit, cartesian_vector<T, N>>`.
They are not quantity representations: a value carries two different units and
the additive structure is not component-wise, so `+`/`-`/dot/cross are omitted
(convert to Cartesian for those). Radial scaling and rotation are provided
natively; angles are kept canonical so equality is geometric.

Key design points:
- Radius is a scalar `Reference` (a unit like `si::metre`, or `isq::radius[m]`),
  constrained to scalar character (rejects a vector- or angle-typed radius).
- Angle unit defaults to `si::radian`; SI angles and the strong `angular`
  system work out of the box.
- `radian_of<kind>` is an exported, kind-keyed customization point so any angle
  system can opt in with one specialization; the key is constrained to a kind.
- Facade<->facade and facade<->cartesian_vector conversions follow the
  `cartesian_vector` rep policy (constructible_from + ImplicitlyConvertibleScalar,
  static_cast body).
- CTAD from a vector quantity derives the radius from the vector's `magnitude()`
  when it has one (a vector-character quantity), so the radius upgrades to the
  strong scalar (e.g. isq::speed) for free once V3 names it; otherwise it falls
  back to the bare unit.

Headers ship under `mp-units/utility/`. Includes a how-to guide.

Verified full runtime + static on gcc-12/gcc-15 (headers) and clang-16 (headers)
/ clang-21 (modules): 129989 assertions each.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 15:34:01 +02:00
Mateusz Pusz c5f65ca4c4 refactor: extract utility::unspecified/specified to mp-units/utility/unspecified.h
The customization-point sentinel and its predicate were declared inline in
framework/customization_points.h even though they live in the mp_units::utility
namespace, leaving the header path out of step with the namespace (unlike
constrained/safe_int/representation, which already ship from mp-units/utility/).

Move them into a small dedicated mp-units/utility/unspecified.h (core component,
same arrangement as the other core-shipped utility headers) and rename the
sentinel undefined -> unspecified so it reads as the exact antonym of the
`specified` concept it is tested against (`cp = unspecified;` ... `specified<...>`).
`undefined` also carried an unwanted "undefined behaviour" connotation.

customization_points.h now includes the new header; core.h and the core module
pick it up; the in-core consumers (tensor_order, numeric_field, frame_projection,
quantity_point bounds) use the utility::unspecified/specified names.

Verified full runtime + static on gcc-12/gcc-15 (headers) and clang-16 (headers)
/ clang-21 (modules): 129906 assertions each.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:54:49 +02:00
Mateusz Pusz 0763cd79ea refactor: move utility-component headers under mp-units/utility/
The mp_units::utility representation types shipped from flat header paths
(mp-units/cartesian_vector.h, cartesian_tensor.h, random.h) while the
core-side utility headers (safe_int, constrained, representation) already
live under mp-units/utility/. Unify the layout so the include path mirrors
the mp_units::utility namespace:

- move cartesian_vector.h, cartesian_tensor.h, random.h into mp-units/utility/;
- keep deprecated forwarding headers at the old paths for the released
  cartesian_vector and random (a #warning plus an include of the new path);
- cartesian_tensor is new in 2.6.0, so it ships only at the new path;
- repoint all in-tree consumers (tests, example, docs) to the new paths so
  they do not trip the deprecation #warning under -Werror;
- update the Project Structure guide and the prose reference in
  quantity_arithmetics.

Verified full runtime + static suites on gcc-12/gcc-15 (headers) and
clang-16 (headers) / clang-21 (modules): 129906 assertions each.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 14:08:21 +02:00
Mateusz Pusz 2ca483b635 refactor: move undefined_t/undefined/specified to mp_units::utility
The customization-point sentinel (undefined_t/undefined) and its companion
`specified` concept lived in the private mp_units::detail namespace, so they
could not be reused outside core - e.g. by mp_units::utility tools that want
the same "no default provided" vocabulary for their own customization points.

Promote them to the public, exported mp_units::utility namespace (they are the
authoring vocabulary of the customization-point pattern, not incidental detail)
while keeping them physically in core. Update the in-core consumers accordingly:
tensor_order, numeric_field, field_reachable, frame_projection/HasFrameProjection,
and the quantity_point bounds selector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 13:36:07 +02:00
Mateusz Pusz b1d1753ba9 docs: post-review fixes to the character post
Add a dated revision note (matching the introducing-absolute-quantities pattern) and correct the
pound example: name the gravitational foot-pound-force engineering convention rather than a
non-existent "US customary gravitational system," so the usc system name is not misrepresented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:44:44 +02:00
Mateusz Pusz de122e1f03 docs: revise the "Why a Quantity Has a Character" blog post
Reframe the pound as a system-dependent mass/force ambiguity, add Trap 6 (field detection) and
Trap 7 (the order band), name the bivector cost with an explicit-dual bridge to the new how-to
guide, and align the customization-point section and Trap 6 with the shipped element-based field
and undefined-default order.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:01:07 +02:00
Mateusz Pusz fdf4b5e59b docs: reconcile representation-types docs with the element-based field and undefined order
numeric_field reads the field off a scalar element (no field adapter for Eigen/Blaze) and follows
tensor_order's availability; tensor_order is undefined for an ambiguous type and must be specialized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:01:07 +02:00
Mateusz Pusz 8a505080b5 feat: reject ambiguous tensor order; field and order share an undefined default
tensor_order's primary template is now left undefined: a partial specialization detects the order
structurally for a type that exposes exactly one indexing shape, while a type exposing both (an
N x 1 matrix modeling a vector, as Eigen does) is ambiguous, has no default, and must be specialized
by an adapter or an ordinary template<>. numeric_field consults tensor_order to reach a scalar
element rather than a structural guess, and is defined only where the order is, so an ambiguous
unspecialized type is rejected on both axes (SFINAE-friendly, closing the cross-TU ODR hazard of
guessing). A shared detail::specified concept replaces the open-coded undefined_t checks in
frame_projection and quantity_point bounds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:00:58 +02:00
Mateusz Pusz 4350baba78 docs: regenerate systems reference for pound_mass
Auto-generated from src/systems (scripts/systems_reference.py): adds the
pound_mass alias entry to the yard-pound reference and the global unit index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:39:09 +02:00
Mateusz Pusz cfe322b7f2 docs: add how-to guide for representing an axial vector as a tensor
Show how to model a quantity the ISQ defines as a vector (e.g. angular
velocity) as a skew-symmetric tensor in the user's own domain, bridged to
isq::angular_velocity by an explicit hat/vee dual, and explain why the tensor
form is a separate quantity rather than the ISQ one relaxed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:35:39 +02:00
Mateusz Pusz 79934e13f7 feat: add pound_mass alias and document the pound mass/force distinction
pound (lb) is a unit of mass; pound_mass is a clarifying alias. The yard-pound
guide now explains why lb is mass and pound_force (lbf) is the separate unit of
force, since 'pound' names a force in US customary engineering practice.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:35:15 +02:00
Mateusz Pusz 66a89de194 feat: add C++23 multidimensional subscript to cartesian_tensor
Expose t[i, j] alongside t(i, j) where the compiler supports it (excluding
GCC 12, which defines __cpp_multidimensional_subscript but does not implement it).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:35:15 +02:00
Mateusz Pusz d19269734d fix: numeric_field reads the field off a representation's element
Detect the field (real/complex) from a scalar element reached by structural
indexing rather than from a container's real()/imag() surface, which a real
Eigen/Blaze matrix exposes too. Because the element is reached structurally,
independent of the tensor_order trait, the answer no longer depends on whether
an integration adapter is in scope: this closes an ODR hazard and removes the
need for the Eigen and Blaze numeric_field overrides.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:35:15 +02:00
Mateusz Pusz a438594b8c fix: use explicit quantity construction instead of deprecated Rep * reference for offset units (#808)
`round`/`floor`/`ceil` with an integer representation built a one-unit
delta via `representation_values<Rep>::one() * reference`. That operator
is `[[deprecated]]` for offset units (e.g. temperatures) because such a
product is ambiguous between a point and a delta, so calling these
functions on `deg_C`/`deg_F` quantities triggered a deprecation warning
(a hard error under `-Werror=deprecated-declarations`).

Replace the pattern with the explicit two-argument `quantity{value,
reference}` constructor (the delta interpretation, matching what `delta`
does internally) everywhere it appeared:

- `math.h`: `floor`, `ceil`, `round`
- `bits/sudo_cast.h`: the zero-delta used in quantity_point conversions,
  where the reference is genuinely an offset unit for temperature points
- `utility/random.h`: all distributions, whose `Q` is always a delta

Add regression tests exercising `floor`/`ceil`/`round` on integer
`deg_C` deltas (including negatives and round-half-to-even) plus the
originally reported `round<deg_F>(delta<deg_C>(1))` case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 23:56:57 +02:00
Mateusz Pusz 88d40e7ce5 fix: hep::velocity should be a child of hep::speed 2026-07-03 23:56:50 +02:00
Mateusz Pusz f24e33bbf0 chore: broken link in GitHub Issue template fixed 2026-07-03 19:59:06 +02:00
mpusz d7b11dec7f docs: update contributors list 2026-07-02 19:12:39 +02:00
Mateusz Pusz 7ac9474066 docs: "Why a Quantity Has a Character" published 2026-06-29 18:09:25 +02:00
Mateusz Pusz e0cc4eec90 docs(utility): document the utility component, namespace tiers, and the safe_int/constrained move
Add a Namespaces section to the project structure docs describing the three
public tiers (mp_units / mp_units::utility / mp_units::detail), show the
core -> systems -> utility module layering, and list the utility headers.
Update the representation and custom-representation guides to introduce the
built-in cartesian types via mp_units::utility, and update the safe_int /
constrained material (guide, blog posts, tutorials) to the new
<mp-units/utility/...> include paths and mp_units::utility names.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 18:07:49 +02:00
Mateusz Pusz 7a03123a01 refactor(utility): move safe_int and constrained into the mp_units::utility namespace
safe_int, constrained, their error policies, and the safe_iN aliases are
non-framework add-ons, so their public names move out of mp_units into the
mp_units::utility extension tier (next to the representation concepts already
there). The headers move to core/include/mp-units/utility/ accordingly but stay
in the core component: safe_int reuses the core 128-bit integer toolkit
(integral, is_signed_v, int128_t, ...) that also backs the scaling engine, so it
bridges to mp_units::detail with a single using-directive rather than relocating
or duplicating that toolkit. overflow_policies stays framework (it backs bounded
quantity point origins). No deprecation shims: these types are unreleased.

Also fixes utility/representation.h, whose public concepts delegated via the
unqualified detail::X. That resolved to mp_units::detail only as long as
mp_units::utility::detail did not exist; now that constrained/safe_int introduce
it, the references are qualified to ::mp_units::detail:: so they stay
order-independent (this surfaced only in the module build).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 23:58:39 +02:00
Mateusz Pusz 2785b52fb3 style: pre-commit 2026-06-27 19:27:43 +02:00
Mateusz Pusz 59959d6386 chore: SECURITY.md added 2026-06-27 18:56:02 +02:00
Mateusz Pusz 4b7755f55f docs: README toned down a bit 2026-06-27 18:55:44 +02:00
Mateusz Pusz ce833d344d refactor(utility): deprecate the mp_units:: rep shims and migrate consumers
The cartesian_vector and random distribution types now live in
mp_units::utility. Turn the transitional mp_units:: shims into proper
[[deprecated]] aliases (gcc-12 keeps a plain using-declaration because
CTAD through a deprecated alias template is broken there), and migrate
all in-tree consumers to mp_units::utility:: so the deprecations don't
trip -Werror. cartesian_tensor keeps no shim: it is unreleased (added in
2.6.0, never shipped), so it lives only at mp_units::utility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 18:55:19 +02:00
Mateusz Pusz 3ebb6076b6 feat(utility): introduce mp-units::utility component for non-standardizable add-ons
Add a third component, mp-units::utility, layered core -> systems -> utility, for
representation types and helpers that are intentionally NOT part of the minimal,
standardization-targeted mp_units:: API (core = framework + math, the C++29 target).

- New mp_units::utility namespace. core/utility/representation.h exposes the
  representation-authoring concept family (Real, Complex, RealScalar, ComplexScalar,
  Scalar, Vector, Tensor) as public faces of the private detail concepts, so detail
  stays invisible and the standardization surface is exactly mp_units:: minus
  mp_units::utility.
- cartesian_vector, cartesian_tensor, random move into mp_units::utility (and the new
  utility component). Their core framework includes are guarded for module builds
  (relying on `import mp_units.core`), mirroring systems headers.
- Transitional exported using-declaration shims keep mp_units::cartesian_vector etc.
  working with stable include paths; the umbrella re-exports utility so consumers build
  unchanged. Deprecating the shims, removing the (unreleased) cartesian_tensor shim,
  migrating internal consumers, and relocating ratio/format helpers are follow-ups.

Verified on the configured-preset ends: gcc-12 (C++20), gcc-16, clang-16 (C++20),
clang-21 (modules); utility module + umbrella also build clean on clang-22.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 14:28:37 +02:00
Mateusz Pusz f5a5062f7a docs: mention the 2D cartesian_vector/tensor in the framework guides
Describe `cartesian_vector<T, N>` / `cartesian_tensor<T, N>` as N-dimensional /
NxN with a compile-time dimension N (2 or 3, default 3), and show dimension
deduction plus embed/project converting between the plane and space.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 23:00:55 +02:00
Mateusz Pusz 851511395b refactor(representation): make cartesian embed/project hidden friends
Move `embed`/`project` for both `cartesian_vector` and `cartesian_tensor` from
namespace-scope free functions into their respective hidden-friend interfaces,
matching every other operation on those types. They are now found only via ADL
(no longer on the `mp_units` namespace surface) and no longer need
`MP_UNITS_EXPORT`. No behavioral change: unqualified calls resolve as before.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 23:00:55 +02:00
Mateusz Pusz 805bb969d2 feat(representation): add embed/project conversions between 2D and 3D cartesian types
`embed` is the canonical inclusion of the plane into space and `project` the
canonical projection back, for both vectors and tensors:
- vector: embed (x,y) -> (x,y,0); project (x,y,z) -> (x,y)
- tensor: embed the 2x2 into the top-left block of a zeroed 3x3; project keeps
  that top-left 2x2 block

They are explicit named free functions (no implicit cross-dimension conversion),
each defined for a single source dimension (embed: 2D only; project: 3D only).
The zero fill is the additive identity derived from a component (`x - x`), so no
value-initialization of the element type is required.

Also route `tensor_product` through `cartesian_tensor_from` instead of a
default-constructed result, matching the other constructing operations (the
element type need not be default-constructible).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:16:14 +02:00
Mateusz Pusz f84e10aef2 feat(representation): parameterize cartesian_vector/tensor on a compile-time dimension N
`cartesian_vector<T, N>` and `cartesian_tensor<T, N>` now take a compile-time
dimension N in {2, 3} (default 3), so a planar model pays nothing for an unused
third component. Operations close at a matching N: tensor_product of two
N-vectors yields an N x N tensor, inner_product(N x N tensor, N-vector) yields
an N-vector, and mixing dimensions is ill-formed. vector_product is the 3D axial
vector for N=3 and the 2D perp-dot (pseudo)scalar for N=2. N is deduced from the
argument count (2/3 components -> 2D/3D vector; 4/9 -> 2x2/3x3 tensor).

Also:
- element conversions build via member initializer lists (no default-construct-
  then-assign), and every constructing tensor operation builds through a new
  cartesian_tensor_from helper instead of a default-constructed result, so the
  element type need not be default-constructible
- a compile-time `extent` query (per-axis count, std::extent semantics) on both,
  usable as both `x.extent` and `x.extent()`

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 22:02:53 +02:00
Mateusz Pusz 3195c7d0eb fix(representation): gate cartesian_vector/tensor element conversions on implicitly_scalable
The converting and variadic constructors (and converting assignment) keyed their implicit/explicit
decision on `std::convertible_to` alone, which let a truncating floating-point -> integer element
conversion (e.g. `cartesian_vector<double>` -> `cartesian_vector<int>`) convert implicitly -
inconsistent with a quantity's rep, where the conversion must also be non-truncating.

Add a `detail::ImplicitlyConvertibleScalar` guard that keeps `std::convertible_to` (an element type
need not be fundamental and may define its own implicit-conversion rules) and defers the
non-truncating decision to the `implicitly_scalable` customization point, passing identical `one`
units so it degenerates to the rep-only decision. Deferring to the CPO rather than hardcoding its
`treat_as_floating_point`-based default keeps element conversions consistent with a user's
`implicitly_scalable` specialization. An FP target or a widening stays implicit; an FP -> integer
element conversion becomes explicit (still constructible). Converting assignment is restricted to the
implicit case for the same reason.

Adds static_assert coverage to the cartesian vector/tensor runtime tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 20:25:25 +02:00
Mateusz Pusz a2bf639be0 docs: "Documentation is prose, not a Doxygen dump" blog article
Add the draft blog post on documentation philosophy and its two Diátaxis diagrams.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 18:41:27 +02:00
Mateusz Pusz 40a10be65a docs: note tensor and complex quantities also expose quantity-level magnitude()
Broaden the "a vector quantity supports magnitude()" note to "vector or tensor quantity" and
name the norm variants (Euclidean for a real vector, Frobenius for a tensor, Hermitian for a
complex one), matching the shipped magnitude() character constraint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 18:40:10 +02:00
Mateusz Pusz 7d832d6fca docs: ground the quantity-character article in ISO 80000-2 rank-ordering
Move the "a vector is a tensor of the first order and a scalar is a tensor of order zero"
quote to the underdetermined-type section, where the article shows one double serving scalar,
vector, and tensor quantities, and add a matrix-vs-tensor paragraph to "The type lies": a
matrix is storage, a tensor is defined by its transformation law, so whether a Matrix3d is a
tensor is a fact about the quantity, not the type.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 17:41:18 +02:00
Mateusz Pusz aa2f91346d docs: document the two-axis quantity character
Rework the "Character of a Quantity" chapter for the order x field model and record the change
in the changelog.

- character_of_a_quantity.md: two-axis framing up front; a new "Real and complex quantities"
  section; the `quantity_tensor_order`/`quantity_field` enum and `quantity_character` struct
  definitions shown before use; an expanded ordering example (explicit vector, inherited
  vector, explicit tensor) and a complex declaration; the ISO 80000-2 rank-ordering quote; and
  the experimental warning removed. Deprecated `quantity_character::vector` examples updated to
  the two-axis spelling.
- CHANGELOG.md: 2.6.0 entries for the two-axis split, `cartesian_tensor`, the
  `numeric_field`/`tensor_order` customization points, the flat-enum deprecation and
  customization rework, and the magnitude/decomposition fixes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 17:41:12 +02:00
Mateusz Pusz 34b1cade2c docs: revise the "Why a Quantity Has a Character" article
Iterate the in-progress essay (still draft). Restructure the motivation as a three-tab
strong-quantities comparison (plain double, loose auto parameters, proper constraints) built
on a speedometer `closing_speed` example, and split character's two jobs into a bulleted list.

Correct several technical claims surfaced in review:

- mass/weight differ by dimension, not character (matching the article's own opening);
- a real representation cannot grow an imaginary part - there is no silent complex-to-real
  drop, since `std::complex` does not convert to `double`;
- the "scalar-as-vector workaround" was the `is_vector` opt-in the Kalman example carried, not
  the later `disable_vector` opt-out;
- `magnitude()` is defined on vectors and tensors, real and complex, and ships today for the
  scalar-rejection case.

Add concrete complex vector/tensor quantities (phasor field, permittivity tensor), the
degenerate stress-tensor case in the underdetermined-type example, and an admonition for the
thesis.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 16:36:39 +02:00
Mateusz Pusz e1c2b9562b fix(representation): key magnitude and vector decomposition on character order, support complex
`quantity::magnitude()` was constrained to an exact `quantity_character{vector}` (i.e.
`{vector, real}`), which silently excluded tensor quantities and complex vectors even though
both have a well-defined norm. It now requires `order >= vector` (vectors and tensors, any
field) plus `HasMagnitude<rep>` so the storage can actually produce one - the latter cleanly
withholds the member instead of letting the body hard-error when the rep has no magnitude.

The `magnitude` CPO gained the missing complex-scalar branch: a complex scalar is a degenerate
1D complex vector, so its magnitude is the modulus `|z|`, symmetric with the real-scalar
`std::abs` branch (a `double` standing in as a 1D real vector).

Vector decomposition (`vector_components` primary template and `ValidVectorAxes`) dropped the
real-field pin, keying on `order == vector` alone, so complex vector quantities decompose into
their complex 1D components. The order pin stays (a tensor is not a flat vector decomposition).

Adds static tests (magnitude over scalar/vector/tensor and real/complex/degenerate reps;
complex-field decomposition) asserting the intended design, and documents the complex-scalar
magnitude path in the representation-types guide.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 13:47:44 +02:00
Mateusz Pusz 9813a9097c docs: draft flag was not set in the previous article 2026-06-26 01:08:35 +02:00
Mateusz Pusz fe03f4b273 docs: "Why a Quantity Has a Character" blog article 2026-06-26 01:03:13 +02:00
Mateusz Pusz 5822f16cb8 fix(representation): guard C++23 multidimensional subscript on GCC 12 and hosted-only tests
- GCC 12 defines `__cpp_multidimensional_subscript` but does not implement it:
  `t[i, j]` is still parsed as the deprecated comma-subscript and fails under
  `-Werror=comma-subscript`, even inside a `requires`. Skip the
  multidimensional-subscript probe in `detect_tensor_order` on GCC 12 (the
  two-index call operator `t(i, j)` still covers order-2 detection there), and
  keep the matching `order_detection` test in step.
- `concepts_test` exercised the character concepts on `cartesian_vector`,
  `cartesian_tensor`, and `std::complex` unconditionally, which broke the
  freestanding build where those headers are not available. Move those
  assertions under `#if MP_UNITS_HOSTED`; the `int`/`double` coverage stays
  unconditional.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 21:54:28 +02:00
Mateusz Pusz e061bf0ef8 refactor(representation): replace disable_real/NotQuantity with disable_representation
Consolidate the representation opt-outs into one character-agnostic customization
point and align the documentation with the two-axis model.

- Add `disable_representation<T>` in `customization_points.h`: a specializable
  opt-out that bars a type from being a quantity representation regardless of
  character. Its default (`is_quantity_abstraction<value_type_t<T>>`) rejects a
  quantity or quantity-like type, and any container of them; `bool` is opted out
  explicitly. This retires both `disable_real` and the `NotQuantity` guard.
- Rename the internal predicate `is_quantity_like` -> `is_quantity_abstraction`
  (it covers `Quantity || QuantityLike`) so it no longer collides with the
  `QuantityLike` customization family.
- The representation tier now leads with one shared `RepresentationBaseline`
  guard (not opted out + `UnitMagnitudeScalable`), which short-circuits a quantity
  before any character concept instantiates its operators (avoiding a
  satisfaction cycle).
- Docs: restructure `representation_types.md` around the two character axes
  (field via `numeric_field`, order via `tensor_order`) with a common baseline,
  the new `disable_representation` section, and consistent unit-magnitude-aware
  scaling terminology; align `concepts.md` and `using_custom_representation_types.md`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 21:36:07 +02:00
Mateusz Pusz 703235d1d4 refactor(representation): intrinsic order/field traits and two-tier character concepts
Replace the `disable_vector`/`disable_tensor` opt-outs with intrinsic,
adapter-overridable traits and split representation classification into two tiers.

- `customization_points.h` now owns the customizable surface: the `real`/`imag`/
  `modulus` CPOs plus the `numeric_field` (field axis) and `tensor_order` (order
  axis) traits. Adapters specialize these; the derived concepts stay with the
  rest of the concept model in `representation_concepts.h`.
- `numeric_field` is the single source of truth for the field axis (default:
  `real()`/`imag()` API detection). Field matching is exact and disjoint - a real
  quantity needs a real representation and a complex one a complex representation.
- `tensor_order` is detected structurally (two-index access -> 2, one-index -> 1,
  otherwise 0) and overridable. Order matching is rank-ordered: a lower-order
  representation fills a higher-order slot.
- Character concepts (`Real`/`Complex`, rank-ordered `Scalar`/`Vector`/`Tensor`)
  carry no representation-validity, so in V3 they can also classify a quantity by
  its character. The `*Representation` concepts are `NotQuantity`-first + character
  + `UnitMagnitudeScalable`; leading with `NotQuantity` rejects a quantity before
  its operators are instantiated, avoiding a constraint-satisfaction cycle.
- Eigen/Blaze adapters declare `numeric_field` from their element type (they expose
  `real()`/`imag()` on real types too, which the API default would misread as complex).
- Rename `MagnitudeScalable` -> `UnitMagnitudeScalable` and `UsesMagnitudeAwareScaling`
  -> `UsesUnitMagnitudeAwareScaling` to disambiguate scaling by a `unit_magnitude`
  from the value/L2 magnitude (the `magnitude` CPO).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 19:17:05 +02:00
Mateusz Pusz 5d040dbd5f fix(quantity_character): include <compare> for a self-contained header
The defaulted `operator<=>` needs `std::strong_ordering`, which previously came only through
transitive includes. The header-set verification (Clang 16) flagged the missing direct
include.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:54:18 +02:00
Mateusz Pusz 25c402528b fix(example): migrate examples off the deprecated flat character spelling
`linear_algebra.cpp` and `measurement.cpp` still used `quantity_character::vector` and
`::real_scalar`, which now warn under the 2.6.0 deprecation and break the `-Werror` example
builds. Switch the comparisons to `quantity_character{quantity_tensor_order::vector}` and the
`RepresentationOf` arguments to the bare `quantity_tensor_order` axis.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 09:35:24 +02:00
Mateusz Pusz bf82b8d28d docs: fix overlong line in representation_types disable_tensor note
Rewrap a 91-column line in the `disable_tensor` paragraph to satisfy markdownlint MD013.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 09:16:59 +02:00
Mateusz Pusz 63291437d8 feat(quantity_character): deprecate the legacy flat character spelling
Mark `quantity_character_legacy` and its enumerators `[[deprecated]]`, so user code using
the pre-2.6.0 flat spelling (`quantity_character::real_scalar`, `::vector`, ..., or the enum
directly) is steered toward the two-axis `quantity_tensor_order` / `quantity_field` form.

The two-axis `quantity_character` necessarily refers to the legacy enum at a few bridge
points: the `using enum`, the legacy conversion constructor, the `quantity_character_init`
assembly, and the `QuantityCharacter` concept. Each is wrapped in
`MP_UNITS_DIAGNOSTIC_IGNORE_DEPRECATED` so the library and its tests build clean under
`-Werror` while only user code is warned. Verified on GCC 15 and Clang 20.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 09:14:18 +02:00
Mateusz Pusz 036efbdc0b feat(scripts): two-axis character labels and a merged Traits column in the reference
Update the systems reference generator for the two-axis quantity character and to reduce
the quantities table width:

- The Character column now prints the order with a `complex` prefix only when the field is
  complex (`scalar`, `complex scalar`, `vector`, `tensor`, `complex vector`,
  `complex tensor`), replacing the old `Real`/`Complex`/`Vector`/`Tensor` labels that mixed
  the two axes and could not express complex vectors or tensors.
- The separate `is_kind` and `non_negative` boolean columns are merged into one `Traits`
  column that shows `kind` and/or `≥ 0` only when set (blank otherwise), dropping a column
  and the per-row ticks.

Regenerated the affected systems reference pages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 00:21:18 +02:00
Mateusz Pusz d4230f46c5 chore(docs): refresh systems reference source-hash cache after character migration
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 00:10:46 +02:00