`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>
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>
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>
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>
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>
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>
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>
`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>
`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>
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>
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>
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>
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>
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>
`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>
- 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>
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>
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>
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>
`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>
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>
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>
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>
Replace `using enum quantity_character_legacy` plus bare character names in the ISQ/HEP
tests with local `quantity_character` constants (`scalar`, `complex_scalar`, `vector`,
`tensor`) built from `quantity_tensor_order` / `quantity_field`, and switch the
`RepresentationOf` / `QUANTITY_SPEC_` character arguments in the concept and quantity-spec
tests to the bare-axis spelling. `real_scalar` is renamed to `scalar` for consistency with
the order-named constants. `apparent_power` (derived from the complex `complex_power`)
pins `quantity_field::real`, matching the library definition.
This removes the final legacy-enum usage from the repository.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the remaining internal `quantity_character::{real_scalar,vector,...}` uses in the
framework with the two-axis form: equality checks now compare against `quantity_character{}`
or `quantity_character{quantity_tensor_order::vector}` etc., and the `quantity_spec` base
seed and the `prefix_utils` default parameter use `quantity_character{}`. `IsOfCharacter`
keeps its exact per-character matching (to be reworked into the rank rule later). The
`quantity_spec` doc example now shows the recommended `quantity_tensor_order::vector` /
`quantity_field::complex` spelling.
The library is now free of the legacy flat enum internally (only the user-facing
compatibility spelling remains), clearing the way to deprecate `quantity_character_legacy`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The QUANTITY_SPEC parser only recognized `quantity_character::` as a character override,
and the generated metadata-extraction program `switch`ed over the character enum. Both
broke once the ISQ definitions moved to the `quantity_tensor_order` / `quantity_field`
spelling and `quantity_character` became a struct.
- Treat `quantity_tensor_order::` and `quantity_field::` (alongside `quantity_character::`)
as character overrides when deciding whether the third macro argument is an equation.
- Rewrite `character_to_string` to compare against the two-axis values instead of switching
on an enum.
The regenerated reference is byte-identical (only the source hash changes).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the legacy flat `quantity_character::{real_scalar,complex_scalar,vector,tensor}`
overrides in the ISQ and HEP quantity definitions with the orthogonal
`quantity_tensor_order` / `quantity_field` spelling. Behavior is unchanged; the ISQ test
suite verifies every quantity's character.
A few real-valued quantities derived from a complex parent (`reactance`,
`apparent_impedance`, `resistance_to_alternating_current`, `susceptance`,
`apparent_admittance`, `apparent_power`) previously used `real_scalar` as a full-character
replace. They now override just `quantity_field::real`, which reads as "the real-valued
counterpart of a complex quantity" and keeps the scalar order inherited from the parent.
Part of removing internal legacy character usage ahead of deprecating
`quantity_character_legacy`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce a two-axis `quantity_character`: a `quantity_tensor_order` (scalar, vector,
tensor) and a `quantity_field` (real, complex), replacing the flat enum. The pre-2.6.0
flat spelling (`quantity_character::real_scalar`, `::complex_scalar`, `::vector`,
`::tensor`) stays available through `quantity_character_legacy` plus implicit conversions,
so existing user code keeps compiling. The only casualty is `using enum quantity_character`
(a struct cannot be `using enum`-ed); users switch to `using enum quantity_character_legacy`.
- `quantity_character` is a struct `{order, field}` with `consteval` constructors and a
defaulted `<=>`. The lexicographic ordering reproduces the previous character ordering
(`real_scalar < complex_scalar < vector < tensor`) used by the `max`-based combination,
and the generated `==` allows use as a non-type template argument.
- `quantity_spec` accepts the legacy enum or bare `quantity_tensor_order` / `quantity_field`
template args; each axis overrides independently (inheriting the other from the quantity
equation). This also makes complex-vector and complex-tensor characters expressible (they
are declarable now but not usable until the representation concepts are refactored).
- `RepresentationOf` accepts any character value via the new `detail::QuantityCharacter`
concept (the two-axis struct, the legacy enum, or either bare axis).
Behavior is unchanged and all tests pass. The library's internal source still uses the
legacy spelling (migrated in a follow-up), after which the legacy enum will be deprecated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `cartesian_tensor<T>`, a fixed 3x3 second-order Cartesian tensor that
serves as a representation type for tensor-character quantities (e.g.
`isq::stress`, `isq::strain`, `isq::moment_of_inertia`), which previously had
no instantiable representation. It mirrors the `cartesian_vector` design
(hidden-friend operations in a `*_iface` base, const-qualified operator
constraints, no `operator%`).
Operations follow ISO 80000-2:2019 clause 18 (second order): `+`, `-`, unary
`-`, scalar `*`//`, compound assignments, `==`; `tensor_product` (dyadic of two
vectors, 2-18.21), `inner_product` (tensor.tensor 2-18.23, tensor.vector
2-18.24), `scalar_product` (double-dot `:`, 2-18.25), and a Frobenius norm.
Fourth-order `T(x)S` (2-18.22), `transpose`/`trace`, and `a.T` are intentionally
out of scope for now.
Activate the previously stubbed tensor representation concepts: `Tensor` is a
permissive mirror of `Vector` (a tensor of order zero is a scalar and of order
one is a vector, ISO 80000-2:2019 18), so real scalars and `cartesian_vector`
also satisfy tensor character as degenerate lower-rank cases. A genuine
second-order `cartesian_tensor` opts out of the lower-rank `Vector` concept via
`disable_vector`, and `std::complex` opts out of `Tensor` via `disable_tensor`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
actionlint runs shellcheck on every `run:` block whenever shellcheck is on
PATH. For our workflows that only surfaces GitHub Actions idioms (e.g.
`>> $GITHUB_ENV` need not be quoted) and false positives on numeric
`${{ matrix.* }}` comparisons, which failed the formatting CI. The standalone
shellcheck hook still lints real shell scripts, and actionlint keeps validating
workflow syntax, expressions, and actions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Refine the requires-expressions of the `cartesian_vector` operators to
constrain on `const`-qualified operands, matching the fact that the
operators take their arguments by `const` reference. A type whose
`operator+` (etc.) is not const-correct would previously have wrongly
satisfied the constraint.
The compound-assignment constraints are ref-qualified as well (mutable
lhs, const rhs) while keeping the `-> std::same_as<T&>` return-type check.
Also strengthen the unit-vector test to assert the resulting component
values (not just that the norm is 1) and to exercise the free
`unit_vector()` function.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The vector_components.h header added with vector quantity decomposition
was not listed in the core FILE_SET HEADERS, so it was excluded from the
install set. Consumers (e.g. test_package) failed with 'file not found'
when quantity.h tried to include it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>