1 /*
2     Copyright (C) 2020 Julian Rüth
3 
4     This file is part of e-antic
5 
6     e-antic is free software: you can redistribute it and/or modify it under
7     the terms of the GNU Lesser General Public License (LGPL) as published
8     by the Free Software Foundation; either version 3.0 of the License, or
9     (at your option) any later version.  See <http://www.gnu.org/licenses/>.
10 */
11 
12 #ifndef E_ANTIC_TEST_RENF_ELEM_GENERATOR_HPP
13 #define E_ANTIC_TEST_RENF_ELEM_GENERATOR_HPP
14 
15 #include "../e-antic/renf_elem.h"
16 
17 #include "renf_generator.hpp"
18 
19 #include "external/catch2/single_include/catch2/catch.hpp"
20 
21 namespace {
22 
23 /*
24  * A generator of random renf_elem_t in a given number field renf_t.
25  */
26 struct RenfElemGenerator : public Catch::Generators::IGenerator<renf_elem_t&>
27 {
28     flint_rand_t& state;
29     renf_t& nf;
30     ulong minbits, maxbits;
31 
32     mutable renf_t _nf;
33     mutable renf_elem_t a;
34     mutable bool has_value_for_iteration = false;
35     mutable bool initial = true;
36 
RenfElemGenerator__anon992918230111::RenfElemGenerator37     RenfElemGenerator(flint_rand_t& state, renf_t& nf, ulong minbits, ulong maxbits) : state(state), nf(nf), minbits(minbits), maxbits(maxbits)
38     {
39        assert(maxbits > minbits);
40     }
41 
clear__anon992918230111::RenfElemGenerator42     void clear()
43     {
44         if (has_value_for_iteration) {
45             renf_elem_clear(a, _nf);
46             renf_clear(_nf);
47         }
48         has_value_for_iteration = false;
49     }
50 
next__anon992918230111::RenfElemGenerator51     bool next() override
52     {
53         clear();
54         return true;
55     }
56 
get__anon992918230111::RenfElemGenerator57     renf_elem_t& get() const override
58     {
59         if (!has_value_for_iteration)
60         {
61             renf_init_set(_nf, nf);
62             renf_elem_init(a, _nf);
63 
64             if (initial)
65             {
66                 initial = false;
67                 renf_elem_zero(a, nf);
68             }
69             else
70             {
71                 ulong bits = minbits + n_randint(state, maxbits - minbits);
72 
73                 renf_elem_randtest(a, state, bits, _nf);
74             }
75 
76             has_value_for_iteration = true;
77         }
78         return a;
79     }
80 
~RenfElemGenerator__anon992918230111::RenfElemGenerator81     ~RenfElemGenerator() override
82     {
83         clear();
84     }
85 };
86 
87 /*
88  * Wrap RenfElemGenerator for use as GENERATE_REF(take(64, renf_elems(...))).
89  */
renf_elems(flint_rand_t & state,renf_t & nf,ulong minbits=10,ulong maxbits=40)90 [[maybe_unused]] Catch::Generators::GeneratorWrapper<renf_elem_t&> renf_elems(flint_rand_t& state, renf_t& nf, ulong minbits = 10, ulong maxbits = 40)
91 {
92     return Catch::Generators::GeneratorWrapper<renf_elem_t&>(std::unique_ptr<Catch::Generators::IGenerator<renf_elem_t&>>(new RenfElemGenerator(state, nf, minbits, maxbits)));
93 }
94 
95 }
96 
97 namespace Catch {
98 
99 /*
100  * Make printing of `renf_elem_t` work in Catch, i.e., show meaingful debug
101  * output for `CAPTURE(a)` where `a` is a `renf_elem_t`.
102  * Note that it's a bit of a hack how we handle the containing renf_t here.
103  * Namely, we store the current `renf_t` in a static variable so we can
104  * correctly print the following `renf_elem_t`. This allows us to write:
105  * ```
106  * CAPTURE(nf);
107  * CAPTURE(a);
108  * ```
109  * An alternative would have been to create a printer for something like
110  * `tuple<renf_t, renf_elem_t>` and then call:
111  * ```
112  * CAPTURE(std::tuple(nf, a));
113  * ```
114  * But we want to make C programmers feel mostly at home in our
115  * test code so that's probably too confusing.
116  */
117 template <>
118 struct StringMaker<renf_elem_t>
119 {
convertCatch::StringMaker120     static std::string convert(const renf_elem_t& a)
121     {
122         assert(StringMaker<renf_t>::latest != nullptr && "you must always CAPTURE(nf) before capturing a contained renf_elem_t");
123 
124         char * alg = renf_elem_get_str_pretty(const_cast<renf_elem_struct*>(a), "x", StringMaker<renf_t>::latest, 10, EANTIC_STR_ALG);
125         char * emb = arb_get_str(a->emb, 10, ARB_STR_MORE);
126 
127         std::stringstream str;
128         str << alg << " ~ " << emb;
129 
130         flint_free(alg);
131         flint_free(emb);
132 
133         return str.str();
134     }
135 };
136 
137 }
138 
139 
140 #endif
141