halcheck 1.0
Loading...
Searching...
No Matches
shrinks.hpp
1#ifndef HALCHECK_GEN_SHRINKS_HPP
2#define HALCHECK_GEN_SHRINKS_HPP
3
4#include <halcheck/gen/label.hpp>
5#include <halcheck/gen/shrink.hpp>
6#include <halcheck/lib/any.hpp>
7#include <halcheck/lib/atom.hpp>
8#include <halcheck/lib/effect.hpp>
9#include <halcheck/lib/functional.hpp>
10#include <halcheck/lib/iterator.hpp>
11#include <halcheck/lib/optional.hpp>
12#include <halcheck/lib/pp.hpp>
13#include <halcheck/lib/scope.hpp>
14#include <halcheck/lib/trie.hpp>
15#include <halcheck/lib/type_traits.hpp>
16#include <halcheck/lib/utility.hpp>
17
18#include <algorithm>
19#include <cstddef>
20#include <cstdint>
21#include <memory>
22#include <mutex>
23#include <utility>
24#include <vector>
25
26namespace halcheck { namespace gen {
27
28namespace detail {
29
30struct shrink_call {
33};
34
35struct shrink_calls {
36 std::mutex mutex;
39};
40
41struct to_tries {
42 to_tries() = default;
43
44 to_tries(
45 const std::vector<detail::shrink_call> *calls, const lib::trie<lib::atom, lib::optional<std::uintmax_t>> *input)
46 : calls(calls), input(input) {}
47
48 lib::optional<lib::trie<lib::atom, lib::optional<std::uintmax_t>>> operator()() {
49 while (call_index < calls->size() && sample_index == (*calls)[call_index].size) {
50 sample_index = 0;
51 ++call_index;
52 }
53
54 if (call_index == calls->size())
55 return lib::nullopt;
56 else
57 return input->set((*calls)[call_index].path, sample_index++);
58 }
59
60 const std::vector<detail::shrink_call> *calls = nullptr;
61 const lib::trie<lib::atom, lib::optional<std::uintmax_t>> *input = nullptr;
62 std::size_t call_index = 0;
63 std::size_t sample_index = 0;
64};
65
66struct shrink_handler : lib::effect::handler<shrink_handler, gen::shrink_effect, gen::label_effect> {
67 shrink_handler(
68 lib::trie<lib::atom, lib::optional<std::uintmax_t>> input,
71 : input(std::move(input)), path(std::move(path)), calls(std::move(calls)) {}
72
73 lib::optional<std::uintmax_t> operator()(gen::shrink_effect args) final {
74 auto output = *input;
75 if (!output && args.size > 0) {
76 if (auto locked = calls.lock()) {
77 const std::lock_guard<std::mutex> _(locked->mutex);
78 locked->data.push_back({path, args.size});
79 locked->size += args.size;
80 }
81 }
82 if (args.size == 0 || !output)
83 return lib::nullopt;
84 else
85 return std::min(*output, args.size - 1);
86 }
87
88 lib::finally_t<> operator()(gen::label_effect args) final {
89 auto prev = input;
90 input = input.drop(args.value);
91 path.push_back(args.value);
92 return gen::label(args.value) + lib::finally([&, prev] {
93 path.pop_back();
94 input = prev;
95 });
96 }
97
98 lib::trie<lib::atom, lib::optional<std::uintmax_t>> input;
101};
102
103} // namespace detail
104
105template<typename T>
106struct shrinks {
107public:
108 template<typename F, typename... Args, HALCHECK_REQUIRE(lib::is_invocable_r<T, F, Args...>())>
109 shrinks(lib::trie<lib::atom, lib::optional<std::uintmax_t>> input, F func, Args &&...args)
110 : shrinks(
111 std::make_shared<detail::shrink_calls>(), std::move(input), std::move(func), std::forward<Args>(args)...) {}
112
113 lib::add_lvalue_reference_t<const T> get() const { return _value.get(); }
114 lib::add_lvalue_reference_t<T> get() { return _value.get(); }
115
116 class children_view : public lib::view_interface<children_view> {
117 public:
118 explicit children_view(const shrinks &parent) : _parent(&parent) {}
119
120 lib::generate_iterator<detail::to_tries> begin() const {
121 return lib::make_generate_iterator(detail::to_tries{&_parent->_calls, &_parent->_input});
122 }
123
124 lib::generate_iterator<detail::to_tries> end() const { return {}; }
125
126 std::size_t size() const { return _parent->_size; }
127
128 private:
129 const shrinks *_parent;
130 };
131
132 children_view children() const { return children_view(*this); }
133
134private:
135 template<typename F, typename... Args>
136 shrinks(
138 lib::trie<lib::atom, lib::optional<std::uintmax_t>> input,
139 F func,
140 Args &&...args)
141 : _value(detail::shrink_handler(input, {}, calls).handle([&] {
142 return lib::make_result_holder(func, std::forward<Args>(args)...);
143 })),
144 _calls([&] {
145 const std::lock_guard<std::mutex> _(calls->mutex);
146 return std::move(calls->data);
147 }()),
148 _input(std::move(input)), _size(calls->size) {}
149
150 lib::result_holder<T> _value;
152 lib::trie<lib::atom, lib::optional<std::uintmax_t>> _input;
153 std::size_t _size;
154};
155
157 template<typename F, typename... Args, HALCHECK_REQUIRE(lib::is_invocable<F, Args...>())>
158 gen::shrinks<lib::invoke_result_t<F, Args...>>
159 operator()(lib::trie<lib::atom, lib::optional<std::uintmax_t>> input, F func, Args &&...args) const {
160 return gen::shrinks<lib::invoke_result_t<F, Args...>>(
161 std::move(input),
162 std::move(func),
163 std::forward<Args>(args)...);
164 }
165
166 template<typename F, typename... Args, HALCHECK_REQUIRE(lib::is_invocable<F, Args...>())>
167 gen::shrinks<lib::invoke_result_t<F, Args...>> operator()(F func, Args &&...args) const {
168 return gen::shrinks<lib::invoke_result_t<F, Args...>>(
169 lib::trie<lib::atom, lib::optional<std::uintmax_t>>(),
170 std::move(func),
171 std::forward<Args>(args)...);
172 }
173} make_shrinks;
174
175}} // namespace halcheck::gen
176
177#endif
T data(T... args)
T forward(T... args)
static struct halcheck::gen::@9 label
Extends the unique identifiers passed to other random generation functions.
std::uintmax_t size()
Gets the maximum size of value that should be generated.
Definition size.hpp:35
lib::variant< lib::symbol, lib::number > atom
An atom is either a symbol or a number.
Definition atom.hpp:194
decltype(lib::invoke(std::declval< F >(), std::declval< Args >()...)) invoke_result_t
An implementation of std::invoke_result_t.
Definition invoke.hpp:42
HALCHECK_INLINE_CONSTEXPR struct halcheck::lib::@25 size
Obtains the size of a range.
static constexpr nullopt_t nullopt
An implementation of std::nullopt.
Definition optional.hpp:41
#define HALCHECK_INLINE_CONSTEXPR
A backwards-compatible substitute for inline constexpr.
Definition pp.hpp:70
lib::finally_t< F > finally(F func)
Definition scope.hpp:157
typename std::add_lvalue_reference< T >::type add_lvalue_reference_t
An implementation of std::add_lvalue_reference_t.
Definition type_traits.hpp:199
#define HALCHECK_REQUIRE(...)
Expands to a template argument that is only valid if the given argument evaluates to true.
Definition type_traits.hpp:24
T make_shared(T... args)
T min(T... args)
STL namespace.
T prev(T... args)