feat: math trigonometric functions can now be called with integral quantities

Relates to #485
This commit is contained in:
Mateusz Pusz
2023-08-29 16:05:48 +02:00
parent f64c08bac3
commit 24803a96cc
2 changed files with 150 additions and 90 deletions

View File

@@ -297,57 +297,87 @@ template<auto R1, typename Rep1, auto R2, typename Rep2, auto R3, typename Rep3>
namespace isq {
template<ReferenceOf<angular_measure> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<one, Rep> sin(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless> auto sin(const quantity<R, Rep>& q) noexcept
requires requires { sin(q.value()); } || requires { std::sin(q.value()); }
{
using std::sin;
return make_quantity<one>(static_cast<Rep>(sin(q.in(si::radian).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(sin(value_cast<si::radian>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<one>(sin(value_cast<rep>(q).value_in(si::radian)));
} else
return make_quantity<one>(sin(q.value_in(si::radian)));
}
template<ReferenceOf<angular_measure> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<one, Rep> cos(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless> auto cos(const quantity<R, Rep>& q) noexcept
requires requires { cos(q.value()); } || requires { std::cos(q.value()); }
{
using std::cos;
return make_quantity<one>(static_cast<Rep>(cos(q.in(si::radian).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(cos(value_cast<si::radian>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<one>(cos(value_cast<rep>(q).value_in(si::radian)));
} else
return make_quantity<one>(cos(q.value_in(si::radian)));
}
template<ReferenceOf<angular_measure> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<one, Rep> tan(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless> auto tan(const quantity<R, Rep>& q) noexcept
requires requires { tan(q.value()); } || requires { std::tan(q.value()); }
{
using std::tan;
return make_quantity<one>(static_cast<Rep>(tan(q.in(si::radian).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(tan(value_cast<si::radian>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<one>(tan(value_cast<rep>(q).value_in(si::radian)));
} else
return make_quantity<one>(tan(q.value_in(si::radian)));
}
template<ReferenceOf<dimensionless> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<si::radian, Rep> asin(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<isq::angular_measure> auto asin(const quantity<R, Rep>& q) noexcept
requires requires { asin(q.value()); } || requires { std::asin(q.value()); }
{
using std::asin;
return make_quantity<si::radian>(static_cast<Rep>(asin(value_cast<one>(q).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(asin(value_cast<one>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<si::radian>(asin(value_cast<rep>(q).value_in(one)));
} else
return make_quantity<si::radian>(asin(q.value_in(one)));
}
template<ReferenceOf<dimensionless> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<si::radian, Rep> acos(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<isq::angular_measure> auto acos(const quantity<R, Rep>& q) noexcept
requires requires { acos(q.value()); } || requires { std::acos(q.value()); }
{
using std::acos;
return make_quantity<si::radian>(static_cast<Rep>(acos(value_cast<one>(q).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(acos(value_cast<one>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<si::radian>(acos(value_cast<rep>(q).value_in(one)));
} else
return make_quantity<si::radian>(acos(q.value_in(one)));
}
template<ReferenceOf<dimensionless> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<si::radian, Rep> atan(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<isq::angular_measure> auto atan(const quantity<R, Rep>& q) noexcept
requires requires { atan(q.value()); } || requires { std::atan(q.value()); }
{
using std::atan;
return make_quantity<si::radian>(static_cast<Rep>(atan(value_cast<one>(q).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(atan(value_cast<one>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<si::radian>(atan(value_cast<rep>(q).value_in(one)));
} else
return make_quantity<si::radian>(atan(q.value_in(one)));
}
} // namespace isq
@@ -355,57 +385,87 @@ template<ReferenceOf<dimensionless> auto R, typename Rep>
namespace angular {
template<ReferenceOf<angle> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<one, Rep> sin(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless> auto sin(const quantity<R, Rep>& q) noexcept
requires requires { sin(q.value()); } || requires { std::sin(q.value()); }
{
using std::sin;
return make_quantity<one>(static_cast<Rep>(sin(q.in(radian).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(sin(value_cast<radian>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<one>(sin(value_cast<rep>(q).value_in(radian)));
} else
return make_quantity<one>(sin(q.value_in(radian)));
}
template<ReferenceOf<angle> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<one, Rep> cos(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless> auto cos(const quantity<R, Rep>& q) noexcept
requires requires { cos(q.value()); } || requires { std::cos(q.value()); }
{
using std::cos;
return make_quantity<one>(static_cast<Rep>(cos(q.in(radian).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(cos(value_cast<radian>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<one>(cos(value_cast<rep>(q).value_in(radian)));
} else
return make_quantity<one>(cos(q.value_in(radian)));
}
template<ReferenceOf<angle> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<one, Rep> tan(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<dimensionless> auto tan(const quantity<R, Rep>& q) noexcept
requires requires { tan(q.value()); } || requires { std::tan(q.value()); }
{
using std::tan;
return make_quantity<one>(static_cast<Rep>(tan(q.in(radian).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(tan(value_cast<radian>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<one>(tan(value_cast<rep>(q).value_in(radian)));
} else
return make_quantity<one>(tan(q.value_in(radian)));
}
template<ReferenceOf<dimensionless> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<radian, Rep> asin(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<angle> auto asin(const quantity<R, Rep>& q) noexcept
requires requires { asin(q.value()); } || requires { std::asin(q.value()); }
{
using std::asin;
return make_quantity<radian>(static_cast<Rep>(asin(value_cast<one>(q).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(asin(value_cast<one>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<radian>(asin(value_cast<rep>(q).value_in(one)));
} else
return make_quantity<radian>(asin(q.value_in(one)));
}
template<ReferenceOf<dimensionless> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<radian, Rep> acos(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<angle> auto acos(const quantity<R, Rep>& q) noexcept
requires requires { acos(q.value()); } || requires { std::acos(q.value()); }
{
using std::acos;
return make_quantity<radian>(static_cast<Rep>(acos(value_cast<one>(q).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(acos(value_cast<one>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<radian>(acos(value_cast<rep>(q).value_in(one)));
} else
return make_quantity<radian>(acos(q.value_in(one)));
}
template<ReferenceOf<dimensionless> auto R, typename Rep>
requires treat_as_floating_point<Rep>
[[nodiscard]] inline quantity<radian, Rep> atan(const quantity<R, Rep>& q) noexcept
[[nodiscard]] inline QuantityOf<angle> auto atan(const quantity<R, Rep>& q) noexcept
requires requires { atan(q.value()); } || requires { std::atan(q.value()); }
{
using std::atan;
return make_quantity<radian>(static_cast<Rep>(atan(value_cast<one>(q).value())));
if constexpr (!treat_as_floating_point<Rep>) {
// check what is the return type when called with the integral value
using rep = decltype(atan(value_cast<one>(q).value()));
// use this type ahead of calling the function to prevent narrowing if a unit conversion is needed
return make_quantity<radian>(atan(value_cast<rep>(q).value_in(one)));
} else
return make_quantity<radian>(atan(q.value_in(one)));
}
} // namespace angular

View File

@@ -311,26 +311,26 @@ TEST_CASE("ISQ trigonometric functions", "[trig][isq]")
{
SECTION("sin")
{
REQUIRE_THAT(isq::sin(0. * isq::angular_measure[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(isq::sin(90. * isq::angular_measure[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(isq::sin(180. * isq::angular_measure[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(isq::sin(270. * isq::angular_measure[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(isq::sin(0 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(isq::sin(90 * deg), AlmostEquals(1. * one));
REQUIRE_THAT(isq::sin(180 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(isq::sin(270 * deg), AlmostEquals(-1. * one));
}
SECTION("cos")
{
REQUIRE_THAT(isq::cos(0. * isq::angular_measure[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(isq::cos(90. * isq::angular_measure[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(isq::cos(180. * isq::angular_measure[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(isq::cos(270. * isq::angular_measure[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(isq::cos(0 * deg), AlmostEquals(1. * one));
REQUIRE_THAT(isq::cos(90 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(isq::cos(180 * deg), AlmostEquals(-1. * one));
REQUIRE_THAT(isq::cos(270 * deg), AlmostEquals(0. * one));
}
SECTION("tan")
{
REQUIRE_THAT(isq::tan(0. * isq::angular_measure[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(isq::tan(45. * isq::angular_measure[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(isq::tan(135. * isq::angular_measure[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(isq::tan(180. * isq::angular_measure[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(isq::tan(0 * deg), AlmostEquals(0. * one));
REQUIRE_THAT(isq::tan(45. * deg), AlmostEquals(1. * one));
REQUIRE_THAT(isq::tan(135. * deg), AlmostEquals(-1. * one));
REQUIRE_THAT(isq::tan(180. * deg), AlmostEquals(0. * one));
}
}
@@ -338,23 +338,23 @@ TEST_CASE("ISQ inverse trigonometric functions", "[inv trig][isq]")
{
SECTION("asin")
{
REQUIRE_THAT(isq::asin(-1. * one), AlmostEquals(-90. * isq::angular_measure[deg]));
REQUIRE_THAT(isq::asin(0. * one), AlmostEquals(0. * isq::angular_measure[deg]));
REQUIRE_THAT(isq::asin(1. * one), AlmostEquals(90. * isq::angular_measure[deg]));
REQUIRE_THAT(isq::asin(-1 * one), AlmostEquals(-90. * deg));
REQUIRE_THAT(isq::asin(0 * one), AlmostEquals(0. * deg));
REQUIRE_THAT(isq::asin(1 * one), AlmostEquals(90. * deg));
}
SECTION("acos")
{
REQUIRE_THAT(isq::asin(-1. * one), AlmostEquals(-90. * isq::angular_measure[deg]));
REQUIRE_THAT(isq::asin(0. * one), AlmostEquals(0. * isq::angular_measure[deg]));
REQUIRE_THAT(isq::asin(1. * one), AlmostEquals(90. * isq::angular_measure[deg]));
REQUIRE_THAT(isq::asin(-1 * one), AlmostEquals(-90. * deg));
REQUIRE_THAT(isq::asin(0 * one), AlmostEquals(0. * deg));
REQUIRE_THAT(isq::asin(1 * one), AlmostEquals(90. * deg));
}
SECTION("atan")
{
REQUIRE_THAT(isq::atan(-1. * one), AlmostEquals(-45. * isq::angular_measure[deg]));
REQUIRE_THAT(isq::atan(0. * one), AlmostEquals(0. * isq::angular_measure[deg]));
REQUIRE_THAT(isq::atan(1. * one), AlmostEquals(45. * isq::angular_measure[deg]));
REQUIRE_THAT(isq::atan(-1 * one), AlmostEquals(-45. * deg));
REQUIRE_THAT(isq::atan(0 * one), AlmostEquals(0. * deg));
REQUIRE_THAT(isq::atan(1 * one), AlmostEquals(45. * deg));
}
}
@@ -367,41 +367,41 @@ TEST_CASE("Angle trigonometric functions", "[trig][angle]")
SECTION("sin")
{
REQUIRE_THAT(sin(0. * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(90. * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(sin(180. * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(270. * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(sin(0 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(90 * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(sin(180 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(270 * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(sin(0. * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(100. * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(sin(200. * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(300. * angle[grad]), AlmostEquals(-1. * one));
REQUIRE_THAT(sin(0 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(100 * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(sin(200 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(sin(300 * angle[grad]), AlmostEquals(-1. * one));
}
SECTION("cos")
{
REQUIRE_THAT(cos(0. * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(cos(90. * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(180. * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(cos(270. * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(0 * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(cos(90 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(180 * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(cos(270 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(0. * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(cos(100. * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(200. * angle[grad]), AlmostEquals(-1. * one));
REQUIRE_THAT(cos(300. * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(0 * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(cos(100 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(cos(200 * angle[grad]), AlmostEquals(-1. * one));
REQUIRE_THAT(cos(300 * angle[grad]), AlmostEquals(0. * one));
}
SECTION("tan")
{
REQUIRE_THAT(tan(0. * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(45. * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(tan(135. * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(tan(180. * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(0 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(45 * angle[deg]), AlmostEquals(1. * one));
REQUIRE_THAT(tan(135 * angle[deg]), AlmostEquals(-1. * one));
REQUIRE_THAT(tan(180 * angle[deg]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(0. * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(50. * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(tan(150. * angle[grad]), AlmostEquals(-1. * one));
REQUIRE_THAT(tan(200. * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(0 * angle[grad]), AlmostEquals(0. * one));
REQUIRE_THAT(tan(50 * angle[grad]), AlmostEquals(1. * one));
REQUIRE_THAT(tan(150 * angle[grad]), AlmostEquals(-1. * one));
REQUIRE_THAT(tan(200 * angle[grad]), AlmostEquals(0. * one));
}
}
@@ -413,22 +413,22 @@ TEST_CASE("Angle inverse trigonometric functions", "[inv trig][angle]")
SECTION("asin")
{
REQUIRE_THAT(asin(-1. * one), AlmostEquals(-90. * angle[deg]));
REQUIRE_THAT(asin(0. * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(asin(1. * one), AlmostEquals(90. * angle[deg]));
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
}
SECTION("acos")
{
REQUIRE_THAT(asin(-1. * one), AlmostEquals(-90. * angle[deg]));
REQUIRE_THAT(asin(0. * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(asin(1. * one), AlmostEquals(90. * angle[deg]));
REQUIRE_THAT(asin(-1 * one), AlmostEquals(-90. * angle[deg]));
REQUIRE_THAT(asin(0 * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(asin(1 * one), AlmostEquals(90. * angle[deg]));
}
SECTION("atan")
{
REQUIRE_THAT(atan(-1. * one), AlmostEquals(-45. * angle[deg]));
REQUIRE_THAT(atan(0. * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(atan(1. * one), AlmostEquals(45. * angle[deg]));
REQUIRE_THAT(atan(-1 * one), AlmostEquals(-45. * angle[deg]));
REQUIRE_THAT(atan(0 * one), AlmostEquals(0. * angle[deg]));
REQUIRE_THAT(atan(1 * one), AlmostEquals(45. * angle[deg]));
}
}