1 //
2 // Semigroups package for GAP
3 // Copyright (C) 2016 James D. Mitchell
4 //
5 // This program is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 //
18 
19 #ifndef SEMIGROUPS_SRC_CONVERTER_H_
20 #define SEMIGROUPS_SRC_CONVERTER_H_
21 
22 // Inclusion of <cstdef> appears to be required to prevent travis from issuing
23 // the warning:
24 //
25 //     /usr/include/c++/5/cstddef:51:11: error: ‘::max_align_t’ has not been
26 //     declared
27 //
28 // according to:
29 //
30 // https://stackoverflow.com/questions/35110786/how-to-fix-the-error-max-align-t
31 
32 #include <cstddef>
33 
34 #include <algorithm>
35 #include <vector>
36 
37 #include "src/compiled.h"
38 
39 #include "pkg.h"
40 #include "semigroups-debug.h"
41 
42 #include "libsemigroups/element.hpp"
43 #include "libsemigroups/semiring.hpp"
44 
45 using libsemigroups::Bipartition;
46 using libsemigroups::BooleanMat;
47 using libsemigroups::Element;
48 using libsemigroups::NaturalSemiring;
49 using libsemigroups::PartialPerm;
50 using libsemigroups::PBR;
51 using libsemigroups::ProjectiveMaxPlusMatrix;
52 using libsemigroups::Semiring;
53 using libsemigroups::SemiringWithThreshold;
54 using libsemigroups::Transformation;
55 using libsemigroups::UNDEFINED;
56 using libsemigroups::detail::MatrixOverSemiringBase;
57 
58 ////////////////////////////////////////////////////////////////////////////////
59 // Abstract base class
60 ////////////////////////////////////////////////////////////////////////////////
61 
62 class Converter {
63  public:
~Converter()64   virtual ~Converter() {}
65   virtual Element* convert(Obj, size_t) const      = 0;
66   virtual Obj      unconvert(Element const*) const = 0;
67 };
68 
69 ////////////////////////////////////////////////////////////////////////////////
70 // Transformations
71 ////////////////////////////////////////////////////////////////////////////////
72 
73 template <typename T>
74 class TransConverter : public Converter {
75  public:
convert(Obj o,size_t n)76   Transformation<T>* convert(Obj o, size_t n) const override {
77     SEMIGROUPS_ASSERT(IS_TRANS(o));
78 
79     std::vector<T> x;
80     x.reserve(n);
81 
82     size_t i = 0;
83     if (TNUM_OBJ(o) == T_TRANS2) {
84       UInt2* pto2 = ADDR_TRANS2(o);
85       for (i = 0; i < std::min((size_t) DEG_TRANS2(o), n); i++) {
86         x.push_back(pto2[i]);
87       }
88     } else if (TNUM_OBJ(o) == T_TRANS4) {
89       UInt4* pto4 = ADDR_TRANS4(o);
90       for (i = 0; i < std::min((size_t) DEG_TRANS4(o), n); i++) {
91         x.push_back(pto4[i]);
92       }
93     } else {
94       // in case of future changes to transformations in GAP
95       SEMIGROUPS_ASSERT(false);
96     }
97 
98     for (; i < n; i++) {
99       x.push_back(i);
100     }
101     return new Transformation<T>(x);
102   }
103 
unconvert(Element const * x)104   Obj unconvert(Element const* x) const override {
105     auto xx = static_cast<Transformation<T> const*>(x);
106     Obj  o  = NEW_TRANS(xx->degree());
107 
108     T* pto = reinterpret_cast<T*>(static_cast<Obj*>(ADDR_OBJ(o) + 3));
109     for (T i = 0; i < xx->degree(); i++) {
110       pto[i] = (*xx)[i];
111     }
112     return o;
113   }
114 };
115 
116 ////////////////////////////////////////////////////////////////////////////////
117 // Partial perms
118 ////////////////////////////////////////////////////////////////////////////////
119 
120 template <typename T>
121 class PPermConverter : public Converter {
122  public:
convert(Obj o,size_t n)123   PartialPerm<T>* convert(Obj o, size_t n) const override {
124     SEMIGROUPS_ASSERT(IS_PPERM(o));
125 
126     std::vector<T> x;
127     x.reserve(n);
128 
129     size_t i = 0;
130     if (TNUM_OBJ(o) == T_PPERM2) {
131       UInt2* pto2 = ADDR_PPERM<UInt2>(o);
132       for (i = 0; i < std::min((size_t) DEG_PPERM2(o), n); i++) {
133         if (pto2[i] == 0) {
134           x.push_back(UNDEFINED);
135         } else {
136           x.push_back(pto2[i] - 1);
137         }
138       }
139     } else if (TNUM_OBJ(o) == T_PPERM4) {
140       UInt4* pto4 = ADDR_PPERM<UInt4>(o);
141       for (i = 0; i < std::min((size_t) DEG_PPERM4(o), n); i++) {
142         if (pto4[i] == 0) {
143           x.push_back(UNDEFINED);
144         } else {
145           x.push_back(pto4[i] - 1);
146         }
147       }
148     } else {
149       // in case of future changes to partial perms in GAP
150       SEMIGROUPS_ASSERT(false);
151     }
152 
153     for (; i < n; i++) {
154       x.push_back(UNDEFINED);
155     }
156     return new PartialPerm<T>(x);
157   }
158 
159   // similar to FuncDensePartialPermNC in gap/src/pperm.c
unconvert(Element const * x)160   Obj unconvert(Element const* x) const override {
161     auto xx  = static_cast<PartialPerm<T> const*>(x);
162     T    deg = xx->degree();
163 
164     // remove trailing 0s
165     while (deg > 0 && (*xx)[deg - 1] == UNDEFINED) {
166       deg--;
167     }
168 
169     Obj o   = NEW_PPERM(deg);
170     T*  pto = reinterpret_cast<T*>(static_cast<Obj*>(ADDR_OBJ(o)) + 2) + 1;
171 
172     for (T i = 0; i < deg; i++) {
173       if ((*xx)[i] == UNDEFINED) {
174         pto[i] = 0;
175       } else {
176         pto[i] = (*xx)[i] + 1;
177       }
178     }
179     return o;
180   }
181 
182  private:
183   // FIXME this isn't right it depends on the codegree only and not the degree
NEW_PPERM(size_t deg)184   inline Obj NEW_PPERM(size_t deg) const {
185     if (deg < 65536) {
186       return NEW_PPERM2(deg);
187     } else {
188       return NEW_PPERM4(deg);
189     }
190   }
191 
192   template <typename UIntT>
ADDR_PPERM(Obj x)193   inline UIntT* ADDR_PPERM(Obj x) const {
194     return reinterpret_cast<UIntT*>(static_cast<Obj*>(ADDR_OBJ(x)) + 2) + 1;
195   }
196 };
197 
198 ////////////////////////////////////////////////////////////////////////////////
199 // Bipartitions
200 ////////////////////////////////////////////////////////////////////////////////
201 
202 class BipartConverter : public Converter {
203  public:
204   Bipartition* convert(Obj o, size_t n) const override;
205   Obj          unconvert(Element const* x) const override;
206 };
207 
208 ////////////////////////////////////////////////////////////////////////////////
209 // Matrices over semirings
210 ////////////////////////////////////////////////////////////////////////////////
211 
212 template <class TSubclass>
213 class MatrixOverSemiringConverter : public Converter {
214  public:
~MatrixOverSemiringConverter()215   ~MatrixOverSemiringConverter() {
216     delete _semiring;
217   }
218 
MatrixOverSemiringConverter(Semiring<int64_t> * semiring,Obj gap_zero,Obj gap_type)219   MatrixOverSemiringConverter(Semiring<int64_t>* semiring,
220                               Obj                gap_zero,
221                               Obj                gap_type)
222       : _semiring(semiring), _gap_zero(gap_zero), _gap_type(gap_type) {}
223 
convert(Obj o,size_t n)224   MatrixOverSemiringBase<int64_t, TSubclass>* convert(Obj    o,
225                                                       size_t n) const override {
226     assert(CALL_1ARGS(IsMatrixOverSemiring, o) == True);
227     assert(IS_PLIST(ELM_PLIST(o, 1)));
228 
229     size_t m = LEN_PLIST(ELM_PLIST(o, 1));
230 
231     std::vector<int64_t> matrix;
232     matrix.reserve(m);
233 
234     for (size_t i = 0; i < m; i++) {
235       Obj row = ELM_PLIST(o, i + 1);
236       for (size_t j = 0; j < m; j++) {
237         Obj entry = ELM_PLIST(row, j + 1);
238         if (EQ(_gap_zero, entry)) {
239           matrix.push_back(_semiring->zero());
240         } else {
241           matrix.push_back(INT_INTOBJ(entry));
242         }
243       }
244     }
245     return new TSubclass(matrix, _semiring);
246   }
247 
unconvert(Element const * x)248   Obj unconvert(Element const* x) const override {
249     TSubclass const* xx(static_cast<TSubclass const*>(x));
250     size_t           n = xx->degree();
251 
252     Obj plist = NEW_PLIST(T_PLIST, n + 2);
253 
254     if (_gap_type == NTPMatrixType) {
255       NaturalSemiring* semiring = static_cast<NaturalSemiring*>(_semiring);
256       SET_LEN_PLIST(plist, n + 2);
257       SET_ELM_PLIST(plist, n + 1, INTOBJ_INT(semiring->threshold()));
258       SET_ELM_PLIST(plist, n + 2, INTOBJ_INT(semiring->period()));
259     } else if (_gap_type == TropicalMaxPlusMatrixType
260                || _gap_type == TropicalMinPlusMatrixType) {
261       SemiringWithThreshold* semiring
262           = static_cast<SemiringWithThreshold*>(_semiring);
263       SET_LEN_PLIST(plist, n + 1);
264       SET_ELM_PLIST(plist, n + 1, INTOBJ_INT(semiring->threshold()));
265     } else {
266       SET_LEN_PLIST(plist, n);
267     }
268 
269     for (size_t i = 0; i < n; i++) {
270       Obj row = NEW_PLIST_IMM(T_PLIST_CYC, n);
271       SET_LEN_PLIST(row, n);
272       for (size_t j = 0; j < n; j++) {
273         int64_t entry = xx->at(i * n + j);
274         if (entry == _semiring->zero()) {
275           SET_ELM_PLIST(row, j + 1, _gap_zero);
276         } else {
277           SET_ELM_PLIST(row, j + 1, INTOBJ_INT(entry));
278         }
279       }
280       SET_ELM_PLIST(plist, i + 1, row);
281       CHANGED_BAG(plist);
282     }
283     SET_TYPE_POSOBJ(plist, _gap_type);
284     RetypeBag(plist, T_POSOBJ);
285     CHANGED_BAG(plist);
286     return plist;
287   }
288 
289  protected:
290   Semiring<int64_t>* _semiring;
291   Obj                _gap_zero;
292   Obj                _gap_type;
293 };
294 
295 ////////////////////////////////////////////////////////////////////////////////
296 // Boolean matrices
297 ////////////////////////////////////////////////////////////////////////////////
298 
299 class BoolMatConverter : public Converter {
300  public:
301   BooleanMat* convert(Obj o, size_t n) const override;
302   Obj         unconvert(Element const* x) const override;
303 };
304 
305 ////////////////////////////////////////////////////////////////////////////////
306 // Partitioned binary relations (PBRs)
307 ////////////////////////////////////////////////////////////////////////////////
308 
309 class PBRConverter : public Converter {
310  public:
311   PBR* convert(Obj o, size_t n) const override;
312   Obj  unconvert(Element const* x) const override;
313 
314  private:
315   Obj get_gap_type(size_t deg) const;
316 };
317 
318 #endif  // SEMIGROUPS_SRC_CONVERTER_H_
319