@@ -548,96 +548,143 @@ NEW_UNIT(Luminosity, candela, 0, 0, 0, 0, 0, 0, 1, 0);
548
548
NEW_UNIT (Moles, mol, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 );
549
549
550
550
namespace 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;
557
555
}
558
556
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 ()));
563
562
}
564
563
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);
569
570
}
570
571
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);
573
578
}
574
579
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 );
577
586
}
578
587
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));
581
594
}
582
595
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));
585
607
}
586
608
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); }
588
610
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); }
590
612
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 ()));
595
619
}
596
620
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 ()));
601
627
}
602
628
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 ()));
605
634
}
606
635
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 ()));
609
641
}
610
642
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
+ }
612
648
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 ()));
617
656
}
618
657
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 ());
623
664
}
624
665
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 ());
629
672
}
630
673
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 ());
635
680
}
636
681
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 ());
641
688
}
642
689
} // namespace units
643
690
0 commit comments