Skip to content

Commit

Permalink
add til::math and magic converters
Browse files Browse the repository at this point in the history
  • Loading branch information
DHowett committed Mar 27, 2020
1 parent 31efd69 commit 138c6b9
Show file tree
Hide file tree
Showing 9 changed files with 488 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/inc/til.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "til/at.h"
#include "til/color.h"
#include "til/math.h"
#include "til/some.h"
#include "til/size.h"
#include "til/point.h"
Expand Down
85 changes: 85 additions & 0 deletions src/inc/til/math.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#pragma once

namespace til
{
// The til::math namespace contains TIL math guidance casts;
// they are intended to be used as the first argument to
// floating-point universal converters elswehere in the til namespace.
namespace math
{
namespace details
{
struct ceiling_t
{
template<typename O, typename T>
static O cast(T val)
{
if constexpr (std::is_floating_point_v<T>)
{
THROW_HR_IF(E_ABORT, ::std::isnan(val));
return ::base::saturated_cast<O>(::std::ceil(val));
}
else
{
return ::base::saturated_cast<O>(val);
}
}
};

struct flooring_t
{
template<typename O, typename T>
static O cast(T val)
{
if constexpr (std::is_floating_point_v<T>)
{
THROW_HR_IF(E_ABORT, ::std::isnan(val));
return ::base::saturated_cast<O>(::std::floor(val));
}
else
{
return ::base::saturated_cast<O>(val);
}
}
};

struct rounding_t
{
template<typename O, typename T>
static O cast(T val)
{
if constexpr (std::is_floating_point_v<T>)
{
THROW_HR_IF(E_ABORT, ::std::isnan(val));
return ::base::saturated_cast<O>(::std::round(val));
}
else
{
return ::base::saturated_cast<O>(val);
}
}
};

struct truncating_t
{
template<typename O, typename T>
static O cast(T val)
{
if constexpr (std::is_floating_point_v<T>)
{
THROW_HR_IF(E_ABORT, ::std::isnan(val));
}
return ::base::saturated_cast<O>(val);
}
};
}

static constexpr details::ceiling_t ceiling; // positives become more positive, negatives become less negative
static constexpr details::flooring_t flooring; // positives become less positive, negatives become more negative
static constexpr details::rounding_t rounding; // it's rounding, from math class
static constexpr details::truncating_t truncating; // drop the decimal point, regardless of how close it is to the next value
}
}
16 changes: 16 additions & 0 deletions src/inc/til/point.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
}

// This template will convert to size from anything that has a X and a Y field that are floating-point;
// a math type is required.
template<typename TilMath, typename TOther>
constexpr point(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().X)> && std::is_floating_point_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
point(TilMath::template cast<ptrdiff_t>(other.X), TilMath::template cast<ptrdiff_t>(other.Y))
{
}

// This template will convert to size from anything that has a x and a y field that are floating-point;
// a math type is required.
template<typename TilMath, typename TOther>
constexpr point(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().x)> && std::is_floating_point_v<decltype(std::declval<TOther>().y)>, int> /*sentinel*/ = 0) :
point(TilMath::template cast<ptrdiff_t>(other.x), TilMath::template cast<ptrdiff_t>(other.y))
{
}

constexpr bool operator==(const point& other) const noexcept
{
return _x == other._x &&
Expand Down
24 changes: 24 additions & 0 deletions src/inc/til/size.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,30 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
}

// This template will convert to size from anything that has a X and a Y field that are floating-point;
// a math type is required.
template<typename TilMath, typename TOther>
constexpr size(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().X)> && std::is_floating_point_v<decltype(std::declval<TOther>().Y)>, int> /*sentinel*/ = 0) :
size(TilMath::template cast<ptrdiff_t>(other.X), TilMath::template cast<ptrdiff_t>(other.Y))
{
}

// This template will convert to size from anything that has a cx and a cy field that are floating-point;
// a math type is required.
template<typename TilMath, typename TOther>
constexpr size(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().cx)> && std::is_floating_point_v<decltype(std::declval<TOther>().cy)>, int> /*sentinel*/ = 0) :
size(TilMath::template cast<ptrdiff_t>(other.cx), TilMath::template cast<ptrdiff_t>(other.cy))
{
}

// This template will convert to size from anything that has a Width and a Height field that are floating-point;
// a math type is required.
template<typename TilMath, typename TOther>
constexpr size(TilMath, const TOther& other, std::enable_if_t<std::is_floating_point_v<decltype(std::declval<TOther>().Width)> && std::is_floating_point_v<decltype(std::declval<TOther>().Height)>, int> /*sentinel*/ = 0) :
size(TilMath::template cast<ptrdiff_t>(other.Width), TilMath::template cast<ptrdiff_t>(other.Height))
{
}

constexpr bool operator==(const size& other) const noexcept
{
return _width == other._width &&
Expand Down
127 changes: 127 additions & 0 deletions src/til/ut_til/MathTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

#include "precomp.h"

#include "til/math.h"

using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;

class MathTests
{
TEST_CLASS(MathTests);

template<typename TG, typename TX = ptrdiff_t>
struct TestCase
{
TG given;
TX expected;
};

template<class TilMath, typename TG, typename TX, int N>
static void _RunCases(TilMath, const std::array<TestCase<TG, TX>, N>& cases)
{
for (const auto& tc : cases)
{
VERIFY_ARE_EQUAL(tc.expected, TilMath::template cast<decltype(tc.expected)>(tc.given));
}
}

TEST_METHOD(Truncating)
{
std::array<TestCase<long double, ptrdiff_t>, 8> cases{
TestCase<long double, ptrdiff_t>{ 1., 1 },
{ 1.9, 1 },
{ -7.1, -7 },
{ -8.5, -8 },
{ PTRDIFF_MAX + 0.5, PTRDIFF_MAX },
{ PTRDIFF_MIN - 0.5, PTRDIFF_MIN },
{ INFINITY, PTRDIFF_MAX },
{ -INFINITY, PTRDIFF_MIN },
};

_RunCases(til::math::truncating, cases);

const auto fn = []() {
const auto v = til::math::details::truncating_t::cast<ptrdiff_t>(NAN);
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}

TEST_METHOD(Ceiling)
{
std::array<TestCase<long double, ptrdiff_t>, 8> cases{
TestCase<long double, ptrdiff_t>{ 1., 1 },
{ 1.9, 2 },
{ -7.1, -7 },
{ -8.5, -8 },
{ PTRDIFF_MAX + 0.5, PTRDIFF_MAX },
{ PTRDIFF_MIN - 0.5, PTRDIFF_MIN },
{ INFINITY, PTRDIFF_MAX },
{ -INFINITY, PTRDIFF_MIN },
};

_RunCases(til::math::ceiling, cases);

const auto fn = []() {
const auto v = til::math::details::ceiling_t::cast<ptrdiff_t>(NAN);
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}

TEST_METHOD(Flooring)
{
std::array<TestCase<long double, ptrdiff_t>, 8> cases{
TestCase<long double, ptrdiff_t>{ 1., 1 },
{ 1.9, 1 },
{ -7.1, -8 },
{ -8.5, -9 },
{ PTRDIFF_MAX + 0.5, PTRDIFF_MAX },
{ PTRDIFF_MIN - 0.5, PTRDIFF_MIN },
{ INFINITY, PTRDIFF_MAX },
{ -INFINITY, PTRDIFF_MIN },
};

_RunCases(til::math::flooring, cases);

const auto fn = []() {
const auto v = til::math::details::flooring_t::cast<ptrdiff_t>(NAN);
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}

TEST_METHOD(Rounding)
{
std::array<TestCase<long double, ptrdiff_t>, 8> cases{
TestCase<long double, ptrdiff_t>{ 1., 1 },
{ 1.9, 2 },
{ -7.1, -7 },
{ -8.5, -9 },
{ PTRDIFF_MAX + 0.5, PTRDIFF_MAX },
{ PTRDIFF_MIN - 0.5, PTRDIFF_MIN },
{ INFINITY, PTRDIFF_MAX },
{ -INFINITY, PTRDIFF_MIN },
};

_RunCases(til::math::rounding, cases);

const auto fn = []() {
const auto v = til::math::details::rounding_t::cast<ptrdiff_t>(NAN);
};
VERIFY_THROWS_SPECIFIC(fn(), wil::ResultException, [](wil::ResultException& e) { return e.GetErrorCode() == E_ABORT; });
}

TEST_METHOD(NormalIntegers)
{
std::array<TestCase<ptrdiff_t, int>, 4> cases{
TestCase<ptrdiff_t, int>{ 1, 1 },
{ -1, -1 },
{ PTRDIFF_MAX, INT_MAX },
{ PTRDIFF_MIN, INT_MIN },
};

_RunCases(til::math::rounding, cases);
}
};
97 changes: 97 additions & 0 deletions src/til/ut_til/PointTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,4 +443,101 @@ class PointTests

// All ptrdiff_ts fit into a float, so there's no exception tests.
}

template<typename T>
struct PointTypeWith_xy
{
T x, y;
};
template<typename T>
struct PointTypeWith_XY
{
T X, Y;
};
TEST_METHOD(CastFromFloatWithMathTypes)
{
PointTypeWith_xy<float> xyFloatIntegral{ 1.f, 2.f };
PointTypeWith_xy<float> xyFloat{ 1.6f, 2.4f };
PointTypeWith_XY<double> XYDoubleIntegral{ 3., 4. };
PointTypeWith_XY<double> XYDouble{ 3.6, 4.4 };
Log::Comment(L"0.) Ceiling");
{
{
til::point converted{ til::math::ceiling, xyFloatIntegral };
VERIFY_ARE_EQUAL((til::point{ 1, 2 }), converted);
}
{
til::point converted{ til::math::ceiling, xyFloat };
VERIFY_ARE_EQUAL((til::point{ 2, 3 }), converted);
}
{
til::point converted{ til::math::ceiling, XYDoubleIntegral };
VERIFY_ARE_EQUAL((til::point{ 3, 4 }), converted);
}
{
til::point converted{ til::math::ceiling, XYDouble };
VERIFY_ARE_EQUAL((til::point{ 4, 5 }), converted);
}
}

Log::Comment(L"1.) Flooring");
{
{
til::point converted{ til::math::flooring, xyFloatIntegral };
VERIFY_ARE_EQUAL((til::point{ 1, 2 }), converted);
}
{
til::point converted{ til::math::flooring, xyFloat };
VERIFY_ARE_EQUAL((til::point{ 1, 2 }), converted);
}
{
til::point converted{ til::math::flooring, XYDoubleIntegral };
VERIFY_ARE_EQUAL((til::point{ 3, 4 }), converted);
}
{
til::point converted{ til::math::flooring, XYDouble };
VERIFY_ARE_EQUAL((til::point{ 3, 4 }), converted);
}
}

Log::Comment(L"2.) Rounding");
{
{
til::point converted{ til::math::rounding, xyFloatIntegral };
VERIFY_ARE_EQUAL((til::point{ 1, 2 }), converted);
}
{
til::point converted{ til::math::rounding, xyFloat };
VERIFY_ARE_EQUAL((til::point{ 2, 2 }), converted);
}
{
til::point converted{ til::math::rounding, XYDoubleIntegral };
VERIFY_ARE_EQUAL((til::point{ 3, 4 }), converted);
}
{
til::point converted{ til::math::rounding, XYDouble };
VERIFY_ARE_EQUAL((til::point{ 4, 4 }), converted);
}
}

Log::Comment(L"3.) Truncating");
{
{
til::point converted{ til::math::truncating, xyFloatIntegral };
VERIFY_ARE_EQUAL((til::point{ 1, 2 }), converted);
}
{
til::point converted{ til::math::truncating, xyFloat };
VERIFY_ARE_EQUAL((til::point{ 1, 2 }), converted);
}
{
til::point converted{ til::math::truncating, XYDoubleIntegral };
VERIFY_ARE_EQUAL((til::point{ 3, 4 }), converted);
}
{
til::point converted{ til::math::truncating, XYDouble };
VERIFY_ARE_EQUAL((til::point{ 3, 4 }), converted);
}
}
}
};
Loading

1 comment on commit 138c6b9

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New misspellings found, please review:

  • cxcy
  • elswehere
  • isnan
To accept these changes, run the following commands
remove_obsolete_words=$(mktemp)
echo '#!/usr/bin/perl -ni
my $re=join "|", qw(
Impl
XY
);
next if /^($re)(?:$| .*)/;
print;' > $remove_obsolete_words
chmod +x $remove_obsolete_words
for file in .github/actions/spell-check/whitelist/alphabet.txt .github/actions/spell-check/whitelist/web.txt .github/actions/spell-check/whitelist/whitelist.txt; do $remove_obsolete_words $file; done
rm $remove_obsolete_words
(
echo "
cxcy
elswehere
impl
isnan
xy
"
) | sort -u -f | perl -ne 'next unless /./; print' > new_whitelist.txt && mv new_whitelist.txt '.github/actions/spell-check/whitelist/138c6b945a57fbbae167bd27bdfc78b091cbcdf9.txt'

Please sign in to comment.