Skip to content
Snippets Groups Projects
Commit ab22a0b7 authored by Julius Nehring-Wirxel's avatar Julius Nehring-Wirxel
Browse files

Implemenation of basic math functions.

parent 8bc5ded8
No related branches found
No related tags found
No related merge requests found
#pragma once
#include <type_traits>
#include <typed-geometry/functions/math.hh>
namespace tg
{
......@@ -40,6 +41,53 @@ constexpr bool str_less(char const* lhs, char const* rhs)
return *lhs != *rhs && *lhs == '\0';
}
template <class T>
struct string_id_of_t
{
static constexpr char const* value = pretty_function<T>();
};
template <class T>
static inline constexpr char const* string_id_of = string_id_of_t<T>::value;
template <class A, class B>
struct type_less_t
{
static constexpr bool value = str_less(string_id_of<A>, string_id_of<B>);
};
template <class A, class B>
static inline constexpr bool less = type_less_t<A, B>::value;
/// single unit and its power, i.e meter^2
template <class unit_tag, int power>
struct base_unit
{
using reciprocal_t = base_unit<unit_tag, -power>;
};
template <class scaling_tag>
struct scaling
{
static constexpr double value = scaling_tag::value;
};
/// a product of base_units meter^1 * second^-1 = m/s
template <class ScalingT, class... BaseUnits>
struct unit
{
using scaling_t = ScalingT;
static constexpr double scaling = ScalingT::value;
using reciprocal_t = unit<ScalingT, typename BaseUnits::reciprocal_t...>;
};
/// the quantity of a unit, ie. 1.5 meter
template <class ScalarT, class Unit>
struct quantity
{
using unit_t = Unit;
using scalar_t = ScalarT;
ScalarT value;
};
struct scaling_kilo
{
static constexpr double value = 1e3;
......@@ -69,6 +117,13 @@ struct scaling_milli
static constexpr double value = 1e-3;
};
static constexpr scaling<scaling_kilo> kilo;
static constexpr scaling<scaling_hecto> hecto;
static constexpr scaling<scaling_deca> deca;
static constexpr scaling<scaling_deci> deci;
static constexpr scaling<scaling_centi> centi;
static constexpr scaling<scaling_milli> milli;
template <class scaling_a, class scaling_b>
struct combined_scaling
{
......@@ -80,56 +135,13 @@ struct scaling_of_t
using type = combined_scaling<scaling_a, scaling_b>;
};
template <>
struct scaling_of_t<no_scaling, no_scaling>
struct scaling_of_t<scaling<no_scaling>, scaling<no_scaling>>
{
using type = no_scaling;
using type = scaling<no_scaling>;
};
template <class scaling_a, class scaling_b>
using scaling_of = typename scaling_of_t<scaling_a, scaling_b>::type;
template <class T>
struct string_id_of_t
{
static constexpr char const* value = pretty_function<T>();
};
template <class T>
static inline constexpr char const* string_id_of = string_id_of_t<T>::value;
template <class A, class B>
struct less_t
{
static constexpr bool value = str_less(string_id_of<A>, string_id_of<B>);
};
template <class A, class B>
static inline constexpr bool less = less_t<A, B>::value;
/// single unit and its power, i.e meter^2
template <class unit_tag, int power>
struct base_unit
{
using reciprocal_t = base_unit<unit_tag, -power>;
};
/// a product of base_units meter^1 * second^-1 = m/s
template <class ScalingT, class... BaseUnits>
struct unit
{
using scaling_t = ScalingT;
static constexpr double scaling = ScalingT::value;
using reciprocal_t = unit<ScalingT, typename BaseUnits::reciprocal_t...>;
};
/// the quantity of a unit, ie. 1.5 meter
template <class ScalarT, class Unit>
struct quantity
{
using unit_t = Unit;
using scalar_t = ScalarT;
ScalarT value;
};
template <class T>
struct is_any_unit_type_t : std::false_type
{
......@@ -146,6 +158,11 @@ template <class ScalarT, class UnitT>
struct is_any_unit_type_t<quantity<ScalarT, UnitT>> : std::true_type
{
};
template <class scaling_tag>
struct is_any_unit_type_t<scaling<scaling_tag>> : std::true_type
{
};
template <class T>
static constexpr bool is_any_unit_type = is_any_unit_type_t<T>::value;
......@@ -164,8 +181,8 @@ template <class T>
struct is_empty_unit_t : std::false_type
{
};
template <class ScalingT>
struct is_empty_unit_t<unit<ScalingT>> : std::true_type
template <class scaling_tag>
struct is_empty_unit_t<unit<scaling<scaling_tag>>> : std::true_type
{
};
template <class T>
......@@ -270,21 +287,21 @@ constexpr auto operator/(ScalarT const& s, quantity<ScalarT, unit<ScalingT, Unit
/// quantity x scalar
template <class ScalarT, class ScalingT, class... UnitsT>
template <class ScalarT, class ScalingT, class... UnitsT, std::enable_if_t<!is_any_unit_type<ScalarT>, int> = 0>
constexpr auto operator*(quantity<ScalarT, unit<ScalingT, UnitsT...>> const& q, ScalarT const& s)
{
quantity<ScalarT, unit<ScalingT, UnitsT...>>{s * q.value};
return quantity<ScalarT, unit<ScalingT, UnitsT...>>{s * q.value};
}
template <class ScalarT, class ScalingT, class... UnitsT>
template <class ScalarT, class ScalingT, class... UnitsT, std::enable_if_t<!is_any_unit_type<ScalarT>, int> = 0>
constexpr auto operator/(quantity<ScalarT, unit<ScalingT, UnitsT...>> const& q, ScalarT const& s)
{
quantity<ScalarT, typename unit<ScalingT, UnitsT...>::reciprocal_t>{s / q.value};
return quantity<ScalarT, typename unit<ScalingT, UnitsT...>::reciprocal_t>{s / q.value};
}
/// unit x quantity
template <class ScalingA, class... UnitsA, class ScalarT, class... UnitsB>
template <class ScalingA, class... UnitsA, class ScalarT, class... UnitsB, std::enable_if_t<!is_any_unit_type<ScalarT>, int> = 0>
constexpr auto operator*(unit<ScalingA, UnitsA...> const&, quantity<ScalarT, unit<no_scaling, UnitsB...>> const& q)
{
using merged_unit = typename merge_units<unit<no_scaling, UnitsA...>, unit<no_scaling, UnitsB...>>::type;
......@@ -298,7 +315,7 @@ constexpr auto operator*(unit<ScalingA, UnitsA...> const&, quantity<ScalarT, uni
}
}
template <class ScalingA, class... UnitsA, class ScalarT, class... UnitsB>
template <class ScalingA, class... UnitsA, class ScalarT, class... UnitsB, std::enable_if_t<!is_any_unit_type<ScalarT>, int> = 0>
constexpr auto operator/(unit<ScalingA, UnitsA...> const&, quantity<ScalarT, unit<no_scaling, UnitsB...>> const& q)
{
using merged_unit = merge_units<unit<no_scaling, UnitsA...>, typename unit<no_scaling, UnitsB...>::reciprocal_t>;
......@@ -331,14 +348,14 @@ constexpr auto operator*(quantity<ScalarT, unit<no_scaling, UnitsB...>> const& q
template <class ScalingA, class... UnitsA, class ScalarT, class... UnitsB>
constexpr auto operator/(quantity<ScalarT, unit<no_scaling, UnitsB...>> const& q, unit<ScalingA, UnitsA...> const&)
{
using merged_unit = merge_units<unit<no_scaling, UnitsA...>, typename unit<no_scaling, UnitsB...>::reciprocal_t>;
using merged_unit = merge_units<unit<scaling<no_scaling>, UnitsA...>, typename unit<scaling<no_scaling>, UnitsB...>::reciprocal_t>;
if constexpr (is_empty_unit<merged_unit>)
{
return ScalarT(ScalingA::value) / ScalarT(q.value);
}
else
{
return quantity<no_scaling, merged_unit>{ScalarT(ScalingA::value) / q.value};
return quantity<no_scaling, merged_unit>{q.value / ScalarT{ScalingA::value}};
}
}
......@@ -401,27 +418,150 @@ constexpr auto operator-(quantity<ScalarT, UnitA> const& lhs, quantity<ScalarT,
return quantity<ScalarT, UnitA>{lhs.value - rhs.value};
}
/// scaling x unit
template <class scaling_tag, class UnitScaling, class... UnitsT>
constexpr auto operator*(scaling<scaling_tag> const&, unit<UnitScaling, UnitsT...> const&)
{
return unit<scaling_of<scaling<scaling_tag>, UnitScaling>, UnitsT...>{};
}
/// scaling x quanity
template <class scaling_tag, class ScalarT, class UnitScaling, class... UnitsT>
constexpr auto operator*(scaling<scaling_tag> const&, quantity<ScalarT, unit<UnitScaling, UnitsT...>> const& q)
{
return quantity<ScalarT, unit<scaling_of<scaling<scaling_tag>, UnitScaling>, UnitsT...>>{q.value};
}
/// scalar x scaling
template <class ScalarT, class scaling_tag>
constexpr auto operator*(ScalarT const& s, scaling<scaling_tag> const&)
{
return s * ScalarT(scaling<scaling_tag>::value);
}
// static constexpr auto kilo = detail::quantity<double, detail::unit<>>{1000};
template <class unit_tag, int power>
constexpr void assert_power_of_two(base_unit<unit_tag, power> const&)
{
static_assert(power % 2 == 0, "unit is not power of two!");
}
template <class ScalingT, class... UnitsT>
constexpr void assert_power_of_two(unit<ScalingT, UnitsT...> const&)
{
(... && assert_power_of_two(UnitsT{}));
}
template <class scaling_tag>
struct sqrt_scaling_t
{
static constexpr double value = tg::sqrt(scaling_tag::value);
};
template <class scaling_tag, class = void>
struct sqrt_of_t
{
using type = sqrt_scaling_t<scaling_tag>;
};
template <>
struct sqrt_of_t<no_scaling>
{
using type = no_scaling;
};
template <class scaling_tag>
using sqrt_of = typename sqrt_of_t<scaling_tag>::type;
// todo:
// sqrt
// pow
// min
// max
// abs
template <class unit_tag, int power>
struct sqrt_of_t<base_unit<unit_tag, power>>
{
using type = base_unit<unit_tag, power / 2>;
};
template <class ScalingT, class... UnitsT>
struct sqrt_of_t<unit<ScalingT, UnitsT...>>
{
using type = unit<sqrt_of<ScalingT>, sqrt_of<UnitsT>...>;
};
template <class ScalarT, class UnitT>
struct sqrt_of_t<quantity<ScalarT, UnitT>>
{
using type = quantity<ScalarT, sqrt_of<UnitT>>;
};
template <class scaling_tag, class... UnitsT>
auto sqrt(unit<scaling<scaling_tag>, UnitsT...> const&)
{
(... && assert_power_of_two(UnitsT{}));
return sqrt_of<unit<scaling<scaling_tag>, UnitsT...>>{};
}
template <class ScalarT, class UnitT>
auto sqrt(quantity<ScalarT, UnitT> const& q)
{
assert_power_of_two(UnitT{});
return sqrt_of<quantity<ScalarT, UnitT>>{tg::sqrt(q.value)};
}
template <class ScalarA, class UnitA, class ScalarB, class UnitB>
auto min(quantity<ScalarA, UnitA> const& q0, quantity<ScalarB, UnitB> const& q1)
{
static_assert(std::is_same_v<UnitA, UnitB>, "Units must be the same!");
static_assert(std::is_same_v<ScalarA, ScalarB>, "Scalars must be the same!"); // todo: possibly promote
return quantity<ScalarA, UnitA>{min(q0.value, q1.value)};
}
template <class ScalarA, class UnitA, class ScalarB, class UnitB>
auto max(quantity<ScalarA, UnitA> const& q0, quantity<ScalarB, UnitB> const& q1)
{
static_assert(std::is_same_v<UnitA, UnitB>, "Units must be the same!");
static_assert(std::is_same_v<ScalarA, ScalarB>, "Scalars must be the same!"); // todo: possibly promote
return quantity<ScalarA, UnitA>{max(q0.value, q1.value)};
}
template <class ScalarT, class UnitT>
auto abs(quantity<ScalarT, UnitT> const& q)
{
return quantity<ScalarT, UnitT>{abs(q.value)};
}
template <int power, class T>
struct pow_t;
template <int power, class T>
using apply_power = typename pow_t<power, T>::type;
template <int power, class ScalingT, class... UnitsT>
struct pow_t<power, unit<ScalingT, UnitsT...>>
{
using type = unit<apply_power<power, ScalingT>, apply_power<power, UnitsT>...>;
};
template <int power, class unit_tag, int unit_power>
struct pow_t<power, base_unit<unit_tag, unit_power>>
{
using type = base_unit<unit_tag, power * unit_power>;
};
template <int power, class ScalarT, class... UnitTs>
auto pow(quantity<ScalarT, unit<no_scaling, UnitTs...>> const& q)
{
return quantity<ScalarT, unit<no_scaling, apply_power<power, UnitTs>...>>{tg::pow(q.value, power)};
}
}
#define TG_DEFINE_UNIT(unit_name) \
struct unit_name##_tag \
{ \
static constexpr char const* name = #unit_name; \
}; \
using unit_name##_t = detail::unit<detail::no_scaling, detail::base_unit<unit_name##_tag, 1>>; \
static constexpr unit_name##_t unit_name
using unit_name##_t = detail::unit<detail::scaling<detail::no_scaling>, detail::base_unit<unit_name##_tag, 1>>; \
[[maybe_unused]] static constexpr unit_name##_t unit_name
namespace units
{
TG_DEFINE_UNIT(ampere);
TG_DEFINE_UNIT(kalvin);
TG_DEFINE_UNIT(mol);
TG_DEFINE_UNIT(candela);
TG_DEFINE_UNIT(meter);
TG_DEFINE_UNIT(second);
TG_DEFINE_UNIT(gram);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment