// The MIT License (MIT) // // Copyright (c) 2018 Mateusz Pusz // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. #include #include #include #include #include #include #ifdef MP_UNITS_IMPORT_STD import std; #else #include #include #endif using namespace mp_units; using namespace mp_units::si::unit_symbols; namespace { using vec3 = utility::cartesian_vector; // the aviation hierarchy: a 3D whole plus 1D-vector component axes (each its own kind) QUANTITY_SPEC(flight_velocity, isq::velocity); QUANTITY_SPEC(forward_velocity, isq::velocity, is_kind); QUANTITY_SPEC(wind_drift, isq::velocity, is_kind); QUANTITY_SPEC(sink_rate, isq::velocity, is_kind); } // namespace template<> struct mp_units::vector_components : mp_units::vector_axes {}; namespace { using Q = quantity; inline constexpr Q v = flight_velocity(vec3{30.0, -4.0, -2.5} * km / h); // ---- the whole is decomposable ---- static_assert(detail::Decomposable); // ---- get and get return 1D-vector quantities of the right axis spec ---- static_assert(QuantityOf(v)), forward_velocity>); static_assert(QuantityOf(v)), sink_rate>); static_assert(decltype(get<0>(v))::quantity_spec.character == quantity_character{quantity_tensor_order::vector}); static_assert(std::is_same_v(v))::rep, double>); // component rep is the vector's value_type static_assert(std::is_same_v(v)), decltype(get(v))>); // ---- values ---- static_assert(get<0>(v).numerical_value_in(km / h) == 30.0); static_assert(get<1>(v).numerical_value_in(km / h) == -4.0); static_assert(get(v).numerical_value_in(km / h) == -2.5); // ---- tuple-like protocol (structured bindings) ---- static_assert(std::tuple_size_v == 3); static_assert(std::is_same_v, decltype(get<2>(v))>); // ---- arithmetic: collinear (same axis) allowed, orthogonal (cross axis) rejected ---- template concept addable = requires(A a, B b) { a + b; }; static_assert(addable(v)), decltype(get<0>(v))>); static_assert(!addable(v)), decltype(get<1>(v))>); // ---- get is constrained: index in range, spec must be one of the declared axes ---- static_assert(detail::DecomposableIndex); // in range static_assert(!detail::DecomposableIndex); // index past the last axis static_assert(detail::DecomposableAxis); // a declared axis static_assert(!detail::DecomposableAxis); // not an axis // ---- negative cases ---- QUANTITY_SPEC(w_foreign, isq::velocity); QUANTITY_SPEC(w_too_many, isq::velocity); QUANTITY_SPEC(w_axis_eq_whole, isq::velocity); QUANTITY_SPEC(w_no_base, isq::velocity); QUANTITY_SPEC(px, isq::velocity); // plain children (not is_kind) -> share the velocity kind QUANTITY_SPEC(py, isq::velocity); QUANTITY_SPEC(pz, isq::velocity); QUANTITY_SPEC(thrust, isq::force, is_kind); // forces: a different hierarchy root than velocity QUANTITY_SPEC(drag, isq::force, is_kind); QUANTITY_SPEC(lift, isq::force, is_kind); QUANTITY_SPEC(a1, isq::velocity, is_kind); QUANTITY_SPEC(a2, isq::velocity, is_kind); QUANTITY_SPEC(a3, isq::velocity, is_kind); QUANTITY_SPEC(a4, isq::velocity, is_kind); // axis-intrinsic violations are rejected by `vector_axes` itself (such a specialization is // ill-formed at the point of definition); tested here through the validity concept static_assert(detail::ValidVectorAxes); // the valid set static_assert(!detail::ValidVectorAxes); // fewer than two axes static_assert(!detail::ValidVectorAxes); // a scalar axis static_assert(!detail::ValidVectorAxes); // mixed hierarchy roots static_assert(!detail::ValidVectorAxes); // not distinct kinds } // namespace // these axis lists ARE intrinsically valid, so the specializations compile; they fail relationally template<> struct mp_units::vector_components : mp_units::vector_axes {}; template<> struct mp_units::vector_components : mp_units::vector_axes {}; template<> struct mp_units::vector_components : mp_units::vector_axes {}; template<> struct mp_units::vector_components { // intentionally does NOT inherit from vector_axes static constexpr std::size_t size = 3; }; namespace { // negative tests as a custom concept asserted via static_assert (the `invalid_types` idiom) template concept invalid_decompositions = requires { requires !detail::Decomposable; // axes valid, but foreign to the whole's root requires !detail::Decomposable; // an axis shares the whole's kind requires !detail::Decomposable; // more axes than the representation holds requires !detail::Decomposable; // vector_components does not derive vector_axes requires !detail::Decomposable; // a scalar whole (no vector_components) requires !detail::Decomposable; // a non-indexable (scalar) representation }; static_assert(invalid_decompositions<>); } // namespace // ---- complex-field vector decomposition (the field pin was removed: any field, order must stay vector) ---- namespace { QUANTITY_SPEC(cplx_flight_velocity, isq::velocity, quantity_field::complex); QUANTITY_SPEC(cplx_forward, isq::velocity, is_kind, quantity_field::complex); QUANTITY_SPEC(cplx_drift, isq::velocity, is_kind, quantity_field::complex); QUANTITY_SPEC(cplx_sink, isq::velocity, is_kind, quantity_field::complex); QUANTITY_SPEC(cplx_speed, isq::speed, quantity_field::complex); // a complex *scalar* } // namespace template<> struct mp_units::vector_components : mp_units::vector_axes {}; namespace { using cvec3 = utility::cartesian_vector>; using CQ = quantity; inline constexpr CQ cv = cplx_flight_velocity(cvec3{std::complex{30.0, 1.0}, std::complex{-4.0, 0.0}, std::complex{-2.5, 2.0}} * (km / h)); // a complex vector whole is decomposable, and complex vector axes are valid (field no longer pinned to real) static_assert(detail::Decomposable); static_assert(detail::ValidVectorAxes); // order is still enforced regardless of field: a complex *scalar* axis is rejected static_assert(!detail::ValidVectorAxes); // get yields the right complex 1D-vector axis quantity static_assert(QuantityOf(cv)), cplx_forward>); static_assert(get<0>(cv).numerical_value_in(km / h) == std::complex{30.0, 1.0}); static_assert(get<2>(cv).numerical_value_in(km / h) == std::complex{-2.5, 2.0}); } // namespace