@@ -548,96 +548,143 @@ NEW_UNIT(Luminosity, candela, 0, 0, 0, 0, 0, 0, 1, 0);
548548NEW_UNIT (Moles, mol, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 );
549549
550550namespace units {
551- template <isQuantity Q> constexpr Q abs (const Q& lhs) { return Q (std::abs (lhs.internal ())); }
552-
553- template <isQuantity Q, isQuantity R> constexpr Q max (const Q& lhs, const R& rhs)
554- requires Isomorphic<Q, R>
555- {
556- return (lhs > rhs ? lhs : rhs);
551+ // Helper: if T is arithmetic, convert it to Number; otherwise leave it unchanged.
552+ template <typename T> constexpr auto to_quantity (T value) -> std::conditional_t<std::is_arithmetic_v<T>, Number, T> {
553+ if constexpr (std::is_arithmetic_v<T>) return Number (value);
554+ else return value;
557555}
558556
559- template <isQuantity Q, isQuantity R> constexpr Q min (const Q& lhs, const R& rhs)
560- requires Isomorphic<Q, R>
561- {
562- return (lhs < rhs ? lhs : rhs);
557+ // abs: works with either a quantity or an arithmetic value.
558+ template <typename T> constexpr auto abs (const T& lhs) {
559+ auto q = to_quantity (lhs);
560+ using Q = decltype (q);
561+ return Q (std::abs (q.internal ()));
563562}
564563
565- template <isQuantity Q> constexpr Number sgn (const Q& lhs) {
566- if (lhs.internal () > 0 ) return 1 ;
567- if (lhs.internal () < 0 ) return -1 ;
568- return 0 ;
564+ // max: converts both arguments; a static_assert ensures the resulting quantities are isomorphic.
565+ template <typename T, typename U> constexpr auto max (const T& lhs, const U& rhs) {
566+ auto qlhs = to_quantity (lhs);
567+ auto qrhs = to_quantity (rhs);
568+ static_assert (Isomorphic<decltype (qlhs), decltype (qrhs)>, " max: Quantities must be isomorphic" );
569+ return (qlhs > qrhs ? qlhs : qrhs);
569570}
570571
571- template <int R, isQuantity Q, isQuantity S = Exponentiated<Q, std::ratio<R>>> constexpr S pow (const Q& lhs) {
572- return S (std::pow (lhs.internal (), R));
572+ // min: similar to max.
573+ template <typename T, typename U> constexpr auto min (const T& lhs, const U& rhs) {
574+ auto qlhs = to_quantity (lhs);
575+ auto qrhs = to_quantity (rhs);
576+ static_assert (Isomorphic<decltype (qlhs), decltype (qrhs)>, " min: Quantities must be isomorphic" );
577+ return (qlhs < qrhs ? qlhs : qrhs);
573578}
574579
575- template <isQuantity Q, isQuantity S = Exponentiated<Q, std::ratio<2 >>> constexpr S square (const Q& lhs) {
576- return pow<2 >(lhs);
580+ // sgn: always returns a Number.
581+ template <typename T> constexpr auto sgn (const T& lhs) {
582+ auto q = to_quantity (lhs);
583+ if (q.internal () > 0 ) return Number (1 );
584+ if (q.internal () < 0 ) return Number (-1 );
585+ return Number (0 );
577586}
578587
579- template <isQuantity Q, isQuantity S = Exponentiated<Q, std::ratio<3 >>> constexpr S cube (const Q& lhs) {
580- return pow<3 >(lhs);
588+ // pow: now accepts an arithmetic or quantity value.
589+ template <int R, typename T> constexpr auto pow (const T& lhs) {
590+ auto q = to_quantity (lhs);
591+ using Q = decltype (q);
592+ using S = Exponentiated<Q, std::ratio<R>>;
593+ return S (std::pow (q.internal (), R));
581594}
582595
583- template <int R, isQuantity Q, isQuantity S = Rooted<Q, std::ratio<R>>> constexpr S root (const Q& lhs) {
584- return S (std::pow (lhs.internal (), 1.0 / R));
596+ // square and cube call pow with 2 or 3.
597+ template <typename T> constexpr auto square (const T& lhs) { return pow<2 >(lhs); }
598+
599+ template <typename T> constexpr auto cube (const T& lhs) { return pow<3 >(lhs); }
600+
601+ // root: similarly for roots.
602+ template <int R, typename T> constexpr auto root (const T& lhs) {
603+ auto q = to_quantity (lhs);
604+ using Q = decltype (q);
605+ using S = Rooted<Q, std::ratio<R>>;
606+ return S (std::pow (q.internal (), 1.0 / R));
585607}
586608
587- template <isQuantity Q, isQuantity S = Rooted<Q, std::ratio< 2 >>> constexpr S sqrt (const Q & lhs) { return root<2 >(lhs); }
609+ template <typename T> constexpr auto sqrt (const T & lhs) { return root<2 >(lhs); }
588610
589- template <isQuantity Q, isQuantity S = Rooted<Q, std::ratio< 3 >>> constexpr S cbrt (const Q & lhs) { return root<3 >(lhs); }
611+ template <typename T> constexpr auto cbrt (const T & lhs) { return root<3 >(lhs); }
590612
591- template <isQuantity Q, isQuantity R> constexpr Q hypot (const Q& lhs, const R& rhs)
592- requires Isomorphic<Q, R>
593- {
594- return Q (std::hypot (lhs.internal (), rhs.internal ()));
613+ // hypot: requires the two quantities be isomorphic.
614+ template <typename T, typename U> constexpr auto hypot (const T& lhs, const U& rhs) {
615+ auto qlhs = to_quantity (lhs);
616+ auto qrhs = to_quantity (rhs);
617+ static_assert (Isomorphic<decltype (qlhs), decltype (qrhs)>, " hypot: Quantities must be isomorphic" );
618+ return decltype (qlhs)(std::hypot (qlhs.internal (), qrhs.internal ()));
595619}
596620
597- template <isQuantity Q, isQuantity R> constexpr Q mod (const Q& lhs, const R& rhs)
598- requires Isomorphic<Q, R>
599- {
600- return Q (std::fmod (lhs.internal (), rhs.internal ()));
621+ // mod: using std::fmod.
622+ template <typename T, typename U> constexpr auto mod (const T& lhs, const U& rhs) {
623+ auto qlhs = to_quantity (lhs);
624+ auto qrhs = to_quantity (rhs);
625+ static_assert (Isomorphic<decltype (qlhs), decltype (qrhs)>, " mod: Quantities must be isomorphic" );
626+ return decltype (qlhs)(std::fmod (qlhs.internal (), qrhs.internal ()));
601627}
602628
603- template <isQuantity Q, isQuantity R> constexpr Q remainder (const Q& lhs, const R& rhs) {
604- return Q (std::remainder (lhs.internal (), rhs.internal ()));
629+ // remainder: using std::remainder.
630+ template <typename T, typename U> constexpr auto remainder (const T& lhs, const U& rhs) {
631+ auto qlhs = to_quantity (lhs);
632+ auto qrhs = to_quantity (rhs);
633+ return decltype (qlhs)(std::remainder (qlhs.internal (), qrhs.internal ()));
605634}
606635
607- template <isQuantity Q1, isQuantity Q2> constexpr Q1 copysign (const Q1& lhs, const Q2& rhs) {
608- return Q1 (std::copysign (lhs.internal (), rhs.internal ()));
636+ // copysign: applies std::copysign.
637+ template <typename T, typename U> constexpr auto copysign (const T& lhs, const U& rhs) {
638+ auto qlhs = to_quantity (lhs);
639+ auto qrhs = to_quantity (rhs);
640+ return decltype (qlhs)(std::copysign (qlhs.internal (), qrhs.internal ()));
609641}
610642
611- template <isQuantity Q> constexpr bool signbit (const Q& lhs) { return std::signbit (lhs.internal ()); }
643+ // signbit: returns a bool.
644+ template <typename T> constexpr auto signbit (const T& lhs) {
645+ auto q = to_quantity (lhs);
646+ return std::signbit (q.internal ());
647+ }
612648
613- template <isQuantity Q, isQuantity R, isQuantity S> constexpr Q clamp (const Q& lhs, const R& lo, const S& hi)
614- requires Isomorphic<Q, R, S>
615- {
616- return Q (std::clamp (lhs.internal (), lo.internal (), hi.internal ()));
649+ // clamp: requires three isomorphic quantities.
650+ template <typename T, typename U, typename V> constexpr auto clamp (const T& lhs, const U& lo, const V& hi) {
651+ auto qlhs = to_quantity (lhs);
652+ auto qlo = to_quantity (lo);
653+ auto qhi = to_quantity (hi);
654+ static_assert (Isomorphic<decltype (qlhs), decltype (qlo), decltype (qhi)>, " clamp: Quantities must be isomorphic" );
655+ return decltype (qlhs)(std::clamp (qlhs.internal (), qlo.internal (), qhi.internal ()));
617656}
618657
619- template <isQuantity Q, isQuantity R> constexpr Q ceil (const Q& lhs, const R& rhs)
620- requires Isomorphic<Q, R>
621- {
622- return Q (std::ceil (lhs.internal () / rhs.internal ()) * rhs.internal ());
658+ // ceil: rounds up to a multiple of rhs.
659+ template <typename T, typename U> constexpr auto ceil (const T& lhs, const U& rhs) {
660+ auto qlhs = to_quantity (lhs);
661+ auto qrhs = to_quantity (rhs);
662+ static_assert (Isomorphic<decltype (qlhs), decltype (qrhs)>, " ceil: Quantities must be isomorphic" );
663+ return decltype (qlhs)(std::ceil (qlhs.internal () / qrhs.internal ()) * qrhs.internal ());
623664}
624665
625- template <isQuantity Q, isQuantity R> constexpr Q floor (const Q& lhs, const R& rhs)
626- requires Isomorphic<Q, R>
627- {
628- return Q (std::floor (lhs.internal () / rhs.internal ()) * rhs.internal ());
666+ // floor: rounds down.
667+ template <typename T, typename U> constexpr auto floor (const T& lhs, const U& rhs) {
668+ auto qlhs = to_quantity (lhs);
669+ auto qrhs = to_quantity (rhs);
670+ static_assert (Isomorphic<decltype (qlhs), decltype (qrhs)>, " floor: Quantities must be isomorphic" );
671+ return decltype (qlhs)(std::floor (qlhs.internal () / qrhs.internal ()) * qrhs.internal ());
629672}
630673
631- template <isQuantity Q, isQuantity R> constexpr Q trunc (const Q& lhs, const R& rhs)
632- requires Isomorphic<Q, R>
633- {
634- return Q (std::trunc (lhs.internal () / rhs.internal ()) * rhs.internal ());
674+ // trunc: rounds toward zero.
675+ template <typename T, typename U> constexpr auto trunc (const T& lhs, const U& rhs) {
676+ auto qlhs = to_quantity (lhs);
677+ auto qrhs = to_quantity (rhs);
678+ static_assert (Isomorphic<decltype (qlhs), decltype (qrhs)>, " trunc: Quantities must be isomorphic" );
679+ return decltype (qlhs)(std::trunc (qlhs.internal () / qrhs.internal ()) * qrhs.internal ());
635680}
636681
637- template <isQuantity Q, isQuantity R> constexpr Q round (const Q& lhs, const R& rhs)
638- requires Isomorphic<Q, R>
639- {
640- return Q (std::round (lhs.internal () / rhs.internal ()) * rhs.internal ());
682+ // round: rounds to the nearest.
683+ template <typename T, typename U> constexpr auto round (const T& lhs, const U& rhs) {
684+ auto qlhs = to_quantity (lhs);
685+ auto qrhs = to_quantity (rhs);
686+ static_assert (Isomorphic<decltype (qlhs), decltype (qrhs)>, " round: Quantities must be isomorphic" );
687+ return decltype (qlhs)(std::round (qlhs.internal () / qrhs.internal ()) * qrhs.internal ());
641688}
642689} // namespace units
643690
0 commit comments