Options are a type that can be either 'None' (undefined) or 'Some x' for any value 'x'
#include <pgs/pgs.hpp>
#include <gtest/gtest.h>
#include <iostream>
#include <cstdlib>
#include <climits>
#include <functional>
namespace {
template <class T>
struct some_t {
T data;
template <class U>
explicit some_t (U&& data) : data { std::forward<U> (data) }
{}
};
struct none_t
{};
template<class T>
template<class T>
bool is_none (option<T> const& o) {
return o.template is<none_t> ();
}
template <class>
struct option_value_type;
template <class T>
struct option_value_type<option<T>> { typedef T type; };
template <class T>
using option_value_type_t = typename option_value_type<T>::type;
template <class T>
option<T> none () {
}
template <class T>
option<decay_t<T>> some (T&& val) {
using t = decay_t<T>;
}
template<class T>
inline bool is_some (option<T> const& o) {
return o.template is<some_t<T>>();
}
template <class T>
T const& get (option<T> const & u) {
return u.template match<T const&> (
[](some_t<T> const& o) -> T const& { return o.data; },
);
}
template <class T>
T& get (option<T>& u) {
return u.template match<T&> (
[](some_t<T>& o) -> T& { return o.data; },
);
}
template <class T>
T default_ (T x, option<T> const& u) {
return u.template match<T> (
[](some_t<T> const& o) -> T { return o.data; },
[=](none_t const&) -> T { return x; }
);
}
template<class F, class U, class T>
auto map_default (F f, U const& x, option<T> const& u) -> U {
return u.template match <U> (
[=](some_t<T> const& o) -> U { return f (o.data); },
[=](none_t const&) -> U { return x; }
);
}
template<class T, class F>
auto operator * (option<T> const& o, F k) -> decltype (k (get (o))) {
using result_t = decltype (k ( get (o)));
using t = option_value_type_t<result_t>;
return o.template match<result_t> (
[](none_t const&) -> result_t { return none<t>(); },
[=](some_t<T> const& o) -> result_t { return k (o.data); }
);
}
template<class T>
option<decay_t<T>> unit (T&& a) {
return some (std::forward<T> (a));
}
template <class T, class F>
auto map (F f, option<T> const& m) -> option<decltype (f (get (m)))>{
using t = decltype (f ( get (m)));
return m.template match<option<t>> (
[](none_t const&) -> option<t> { return none<t>(); },
[=](some_t<T> const& o) -> option<t> { return some (f (o.data)); }
);
}
}
ASSERT_EQ (get(some (1)), 1);
auto f = [](int i) {
return some (i * i); };
ASSERT_EQ (get (some (3) * f), 9);
auto g = [](int x) { return x * x; };
ASSERT_EQ (get (map (g, some (3))), 9);
ASSERT_TRUE (is_none (map (g, none<int>())));
ASSERT_EQ (default_(1, none<int>()), 1);
ASSERT_EQ (default_(1, some(3)), 3);
auto h = [](int y) -> float{ return float (y * y); };
ASSERT_EQ (map_default (h, 0.0, none<int>()), 0.0);
ASSERT_EQ (map_default (h, 0.0, some (3)), 9.0);
}
namespace {
return [=](int y) -> option<int> {
if ((x > 0) && (y > INT_MAX - x) ||
(x < 0) && (y < INT_MIN - x)) {
return none<int>();
}
return some (y + x);
};
}
return [=](int y) -> option<int> {
if ((x > 0) && (y < (INT_MIN + x)) ||
(x < 0) && (y > (INT_MAX + x))) {
return none<int>();
}
return some (y - x);
};
}
return [=](int y) -> option<int> {
if (y > 0) {
if (x > 0) {
if (y > (INT_MAX / x)) {
return none<int>();
}
}
else {
if (x < (INT_MIN / y)) {
return none<int>();
}
}
}
else {
if (x > 0) {
if (y < (INT_MIN / x)) {
return none<int>();
}
}
else {
if ((y != 0) && (x < (INT_MAX / y))) {
return none<int>();
}
}
}
return some (y * x);
};
}
return [=](int y) {
if (x == 0) {
return none<int>();
}
if (y == INT_MIN && x == -1)
return none<int>();
return some (y / x);
};
}
}
TEST(
pgs, safe_arithmetic) {
ASSERT_EQ (
get (unit (INT_MAX) *
div (2) * mul (2) * add (1)), INT_MAX);
ASSERT_TRUE (is_none (unit (INT_MAX) *
div (2) * add (1) * mul (2)));
ASSERT_TRUE (is_none (unit (INT_MIN) *
div (-1)));
}