Skip to content

Commit 800a5a1

Browse files
algo.5 Inverse of a real number (#15)
1 parent 9f49a02 commit 800a5a1

File tree

7 files changed

+196
-72
lines changed

7 files changed

+196
-72
lines changed

src/epsilon/chars.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ constexpr std::string to_string(r<C> num, unsigned int k) {
9696
constexpr int extra_precision = 10;
9797
if constexpr (B == 10) {
9898
auto n = static_cast<int>(log_4_10 * k) + extra_precision;
99-
auto xn = coro::sync_get(num(n));
99+
auto xn = num(n).get();
100100
auto sgn = xn.sgn; // use the absolute value of xn to round towards zero.
101101
xn.sgn = sign::positive;
102102

src/epsilon/coro.hpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class lazy {
3232
class awaiter;
3333
awaiter operator co_await() noexcept;
3434

35+
T get();
36+
3537
private:
3638
explicit lazy(std::coroutine_handle<lazy_promise<T>> coro) : coro_(coro) {}
3739
std::coroutine_handle<lazy_promise<T>> coro_;
@@ -46,7 +48,14 @@ class lazy_promise {
4648
constexpr auto initial_suspend() noexcept { return std::suspend_always{}; }
4749
constexpr auto final_suspend() noexcept {
4850
struct final_awaiter : std::suspend_always {
49-
auto await_suspend(std::coroutine_handle<lazy_promise> handle) noexcept { return handle.promise().cont_; }
51+
std::coroutine_handle<> await_suspend(std::coroutine_handle<lazy_promise> handle) noexcept {
52+
auto cont = handle.promise().cont_;
53+
if (cont) {
54+
return cont;
55+
} else {
56+
return std::noop_coroutine();
57+
}
58+
}
5059
};
5160
return final_awaiter{};
5261
}
@@ -73,13 +82,18 @@ class lazy<T>::awaiter : public std::suspend_always {
7382
T await_resume() {
7483
return std::visit(
7584
tmp::overloads{
76-
[](T& val) -> T&& { return std::move(val); }, // normal path
77-
[](std::monostate) -> T&& { std::terminate(); }, // unassigned
78-
[](std::exception_ptr& ex) -> T&& { std::rethrow_exception(std::move(ex)); } // contains exception
85+
[](T& val) -> T { return std::move(val); }, // normal path
86+
[](std::monostate) -> T { std::terminate(); }, // unassigned
87+
[](std::exception_ptr& ex) -> T { std::rethrow_exception(std::move(ex)); } // contains exception
7988
},
8089
coro_.promise().state_);
8190
}
8291

92+
T get() {
93+
coro_();
94+
return await_resume();
95+
}
96+
8397
private:
8498
std::coroutine_handle<lazy_promise<T>> coro_;
8599
};
@@ -88,15 +102,13 @@ typename lazy<T>::awaiter lazy<T>::operator co_await() noexcept {
88102
return lazy<T>::awaiter{coro_};
89103
}
90104

91-
struct forget {};
92-
93105
template <class T>
94-
T sync_get(lazy<T> awaitable) {
95-
std::optional<T> slot;
96-
[&]() -> forget { slot = co_await awaitable; }();
97-
return std::move(*slot);
106+
T lazy<T>::get() {
107+
return lazy<T>::awaiter{coro_}.get();
98108
}
99109

110+
struct forget {};
111+
100112
} // namespace epx::coro
101113

102114
namespace std {

src/epsilon/r.hpp

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// std
88
#include <algorithm>
99
#include <functional>
10+
#include <limits>
1011

1112
// epx
1213
#include "coro.hpp"
@@ -73,11 +74,20 @@ coro::lazy<int> msd(r<C> x, int max) {
7374
if (c > 0 || i >= max) {
7475
co_return i;
7576
}
76-
xi = co_await x(++i); // TODO: use faster search algo.
77+
++i; // TODO: use faster search algo.
78+
if (i > max_msd<global_config_tag>) [[unlikely]] {
79+
throw msd_overflow_error{};
80+
}
81+
xi = co_await x(i);
7782
}
7883
}
7984
}
8085

86+
template <container C>
87+
coro::lazy<int> msd(r<C> x) {
88+
return msd(std::move(x), std::numeric_limits<int>::max());
89+
}
90+
8191
template <container C>
8292
constexpr r<C> mul(r<C> x, r<C> y) {
8393
struct {
@@ -100,6 +110,28 @@ constexpr r<C> mul(r<C> x, r<C> y) {
100110
return expr;
101111
}
102112

113+
template <container C>
114+
constexpr r<C> inv(r<C> x) {
115+
struct {
116+
r<C> x;
117+
118+
coro::lazy<z<C>> operator()(unsigned int n) {
119+
int msdx = co_await msd(x);
120+
int nn = static_cast<int>(n);
121+
if (nn <= -msdx) {
122+
co_return z<C>{}; // zero
123+
}
124+
125+
int k = nn + 2 * msdx + 1;
126+
auto p = mul_4exp(details::one<C>(), k + nn);
127+
auto q = add(co_await x(k), details::one<C>());
128+
auto [quo, _] = ceil_div(p, q);
129+
co_return add(quo, details::one<C>());
130+
}
131+
} expr{.x = std::move(x)};
132+
return expr;
133+
}
134+
103135
} // namespace epx
104136

105137
#endif // EPSILON_INC_R_HPP

src/epsilon/t.hpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@
99

1010
namespace epx {
1111

12-
using default_digit_t = uint32_t;
13-
using max_digit_t = uint32_t;
14-
using default_container_t = std::vector<default_digit_t>;
12+
using default_digit_type = uint32_t;
13+
using max_digit_type = uint32_t;
14+
using default_container_type = std::vector<default_digit_type>;
1515

16-
struct bad_digit_t;
16+
struct bad_digit_type;
1717
template <class D>
18-
using wide_digit_t =
18+
using wide_digit_type =
1919
std::conditional_t<sizeof(D) == sizeof(uint8_t), uint16_t,
2020
std::conditional_t<sizeof(D) == sizeof(uint16_t), uint32_t,
21-
std::conditional_t<sizeof(D) == sizeof(uint32_t), uint64_t, bad_digit_t>>>;
21+
std::conditional_t<sizeof(D) == sizeof(uint32_t), uint64_t, bad_digit_type>>>;
2222

2323
template <class T>
2424
concept container = std::ranges::random_access_range<T> && //
@@ -29,12 +29,12 @@ concept container = std::ranges::random_access_range<T> && //
2929
} && requires(T c) {
3030
c.push_back(typename T::value_type{});
3131
c.reserve(size_t{});
32-
sizeof(typename T::value_type) < sizeof(max_digit_t);
32+
sizeof(typename T::value_type) < sizeof(max_digit_type);
3333
};
3434

3535
enum class sign : uint8_t { positive, negative };
3636

37-
template <container C = default_container_t>
37+
template <container C = default_container_type>
3838
struct z {
3939
using container_type = C;
4040
using digit_type = typename C::value_type;
@@ -44,6 +44,11 @@ struct z {
4444
sign sgn = sign::positive;
4545
};
4646

47+
struct global_config_tag {}; // allow user-defined configs
48+
49+
template <typename>
50+
constexpr int max_msd = 10000; // can be overridden by global_config_tag
51+
4752
struct divide_by_zero_error : public std::runtime_error {
4853
divide_by_zero_error() : std::runtime_error("epx: divide by zero") {}
4954
};
@@ -52,6 +57,10 @@ struct overflow_error : public std::runtime_error {
5257
overflow_error() : std::runtime_error("epx: overflow error") {}
5358
};
5459

60+
struct msd_overflow_error : public std::runtime_error {
61+
msd_overflow_error() : std::runtime_error("epx: msd overflow error") {}
62+
};
63+
5564
} // namespace epx
5665

5766
#endif

src/epsilon/z.hpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ constexpr auto umul(T lhs, T rhs) {
7676
// u0 - LSD, u1 - MSD
7777
template <class D>
7878
constexpr auto div_2d(D u0, D u1, D v) {
79-
using W = wide_digit_t<D>;
79+
using W = wide_digit_type<D>;
8080
struct result_t {
8181
W q;
8282
D r;
@@ -233,7 +233,7 @@ constexpr z<C> mul_n(const z<C>& lhs, const z<C>& rhs) {
233233
for (size_t j = 0; j < std::ranges::size(b); ++j) {
234234
D cy = 0;
235235
for (size_t i = 0; i < std::ranges::size(a); ++i) {
236-
auto [p0, p1] = details::umul<default_digit_t>(a[i], b[j]);
236+
auto [p0, p1] = details::umul<default_digit_type>(a[i], b[j]);
237237
p0 += cy;
238238
cy = (p0 < cy ? 1u : 0u) + p1;
239239
r.digits[i + j] += p0;
@@ -248,7 +248,7 @@ constexpr z<C> mul_n(const z<C>& lhs, const z<C>& rhs) {
248248
template <container C>
249249
constexpr auto div_n(const z<C>& u, typename z<C>::digit_type v) {
250250
using D = typename z<C>::digit_type;
251-
static_assert(sizeof(D) <= sizeof(max_digit_t));
251+
static_assert(sizeof(D) <= sizeof(max_digit_type));
252252

253253
struct {
254254
z<C> q;
@@ -283,7 +283,7 @@ constexpr auto div_n(z<C> lhs, z<C> rhs) {
283283
if (rel > 0) {
284284
if (std::ranges::size(rhs.digits) > 1) {
285285
using D = typename z<C>::digit_type;
286-
using W = wide_digit_t<D>;
286+
using W = wide_digit_type<D>;
287287
constexpr W b = W{1} << (sizeof(D) * CHAR_BIT);
288288
constexpr D mask = D{b - 1};
289289

@@ -426,6 +426,26 @@ constexpr auto floor_div(z<C> lhs, z<C> rhs) {
426426
return res;
427427
}
428428

429+
template <container C>
430+
constexpr auto ceil_div(z<C> lhs, z<C> rhs) {
431+
struct result_t {
432+
z<C> q;
433+
z<C> r;
434+
};
435+
436+
auto sgn = lhs.sgn == rhs.sgn ? sign::positive : sign::negative;
437+
auto [q, r] = div_n(std::move(lhs), rhs);
438+
result_t res = {.q = std::move(q), .r = std::move(r)};
439+
440+
if (sgn == sign::positive && !is_zero(res.r)) {
441+
res.q = add_n(res.q, details::one<C>());
442+
res.r = sub_n(rhs, res.r);
443+
}
444+
res.q.sgn = sgn;
445+
res.r.sgn = rhs.sgn == sign::positive ? sign::negative : sign::positive;
446+
return res;
447+
}
448+
429449
template <container C>
430450
constexpr z<C>& mul_2exp(z<C>& val, int exp) {
431451
using D = typename z<C>::digit_type;

0 commit comments

Comments
 (0)