1 /* Copyright (c) 1997-2021
2    Ewgenij Gawrilow, Michael Joswig, and the polymake team
3    Technische Universität Berlin, Germany
4    https://polymake.org
5 
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version: http://www.gnu.org/licenses/gpl.txt.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 --------------------------------------------------------------------------------
16 */
17 
18 #pragma once
19 /** @file IncidenceMatrix.h
20     @brief Implementation of pm::IncidenceMatrix class
21 */
22 
23 
24 
25 #include "polymake/internal/sparse2d.h"
26 #include "polymake/Set.h"
27 #include "polymake/GenericIncidenceMatrix.h"
28 #include "polymake/permutations.h"
29 
30 namespace pm {
31 
32 template <typename Set>
33 class incidence_proxy_base {
34 protected:
35    Set* s;
36    Int j;
37 
get()38    bool get() const { return s->exists(j); }
39 
insert()40    void insert() { s->insert(j); }
41 
erase()42    void erase() { s->erase(j); }
43 
toggle()44    void toggle() { s->toggle(j); }
45 public:
46    typedef bool value_type;
47 
incidence_proxy_base(Set & s_arg,Int j_arg)48    incidence_proxy_base(Set& s_arg, Int j_arg)
49       : s(&s_arg), j(j_arg) {}
50 };
51 
52 template <typename TreeRef> class incidence_line;
53 template <bool rowwise, typename BaseRef = void> class incidence_line_factory;
54 template <typename symmetric> class IncidenceMatrix_base;
55 
56 template <typename TreeRef>
57 struct incidence_line_params
58    : mlist_concat< typename sparse2d::line_params<TreeRef>::type,
59                    OperationTag< BuildUnaryIt<operations::index2element> > > {};
60 
61 template <typename TreeRef>
62 class incidence_line_base
63    : public modified_tree< incidence_line<TreeRef>, typename incidence_line_params<TreeRef>::type > {
64 protected:
65    typedef nothing first_arg_type;
66    typedef nothing second_arg_type;
67    ~incidence_line_base();
68 public:
index()69    Int index() const { return this->get_container().get_line_index(); }
70 };
71 
72 template <typename Tree>
73 class incidence_line_base<Tree&>
74    : public modified_tree< incidence_line<Tree&>, typename incidence_line_params<Tree&>::type > {
75 protected:
76    using tree_type = typename deref<Tree>::type;
77    using symmetric = std::conditional_t<Tree::symmetric, Symmetric, NonSymmetric>;
78    using matrix_ref = typename inherit_ref<IncidenceMatrix_base<symmetric>, Tree&>::type;
79    using alias_t = alias<matrix_ref>;
80 
81    alias_t matrix;
82    Int line_index;
83 
84 public:
85    template <typename Arg, typename = std::enable_if_t<std::is_constructible<alias_t, Arg>::value>>
incidence_line_base(Arg && matrix_arg,Int index_arg)86    incidence_line_base(Arg&& matrix_arg, Int index_arg)
87       : matrix(std::forward<Arg>(matrix_arg))
88       , line_index(index_arg) {}
89 
get_container()90    decltype(auto) get_container()
91    {
92       return matrix->get_table().get_line(line_index, (tree_type*)nullptr);
93    }
get_container()94    decltype(auto) get_container() const
95    {
96       return matrix->get_table().get_line(line_index, (tree_type*)nullptr);
97    }
98 
index()99    Int index() const { return line_index; }
100 };
101 
102 template <typename TreeRef>
103 class incidence_line
104    : public incidence_line_base<TreeRef>
105    , public GenericMutableSet<incidence_line<TreeRef>, Int, operations::cmp>
106 {
107    using base_t = incidence_line_base<TreeRef>;
108    friend class GenericMutableSet<incidence_line>;
109    template <typename> friend class IncidenceMatrix;
110    template <sparse2d::restriction_kind> friend class RestrictedIncidenceMatrix;
111 public:
112    using incidence_line_base<TreeRef>::incidence_line_base;
113 
114    incidence_line& operator= (const incidence_line& other)
115    {
116       return incidence_line::generic_mutable_type::operator=(other);
117    }
118 
119    // TODO: investigate whether the dimension check is active?
120    template <typename Set>
121    incidence_line& operator= (const GenericSet<Set, Int, operations::cmp>& other)
122    {
123       return incidence_line::generic_mutable_type::operator=(other);
124    }
125 
126    incidence_line& operator= (std::initializer_list<Int> l)
127    {
128       return incidence_line::generic_mutable_type::operator=(l);
129    }
130 
131 protected:
132    template <typename Iterator>
fill(Iterator src)133    void fill(Iterator src)
134    {
135       this->clear();
136       for (; !src.at_end(); ++src)
137          this->insert(*src);
138    }
139 };
140 
141 template <typename TreeRef>
142 struct check_container_feature<incidence_line<TreeRef>, sparse_compatible> : std::true_type {};
143 
144 template <typename TreeRef>
145 struct spec_object_traits< incidence_line<TreeRef> >
146    : spec_object_traits<is_container> {
147    static constexpr bool is_temporary = attrib<TreeRef>::is_reference, is_always_const = attrib<TreeRef>::is_const;
148    using masquerade_for = std::conditional_t<is_temporary, void, typename deref<TreeRef>::type>;
149    static constexpr int is_resizeable = 0;
150 };
151 
152 template <typename Iterator>
153 using is_sequence_of_sets=std::is_same<typename object_traits<typename iterator_traits<Iterator>::value_type>::generic_tag, is_set>;
154 
155 template <typename Container>
156 using fits_for_append_to_IM
157    = mlist_or<isomorphic_to_container_of<Container, Int, allow_conversion>,
158               isomorphic_to_container_of<Container, Set<Int>, allow_conversion>,
159               isomorphic_to_container_of<Container, IncidenceMatrix<>, allow_conversion>,
160               std::is_same<typename object_traits<Container>::generic_tag, is_incidence_matrix> >;
161 
162 template <sparse2d::restriction_kind restriction = sparse2d::only_rows>
163 class RestrictedIncidenceMatrix
164    : public matrix_methods<RestrictedIncidenceMatrix<restriction>, bool> {
165 protected:
166    typedef sparse2d::restriction_const<restriction> my_restriction;
167    typedef sparse2d::restriction_const<(restriction==sparse2d::only_rows ? sparse2d::only_cols : sparse2d::only_rows)> cross_restriction;
168 
169    typedef sparse2d::Table<nothing, false, restriction> table_type;
170    table_type data;
171 
172    table_type& get_table() { return data; }
173    const table_type& get_table() const { return data; }
174 
175    template <typename Iterator, typename TLines>
176    static
177    void copy_linewise(Iterator&& src, TLines& lines, my_restriction, std::true_type)
178    {
179       copy_range(std::forward<Iterator>(src), entire(lines));
180    }
181 
182    template <typename Iterator, typename TLines>
183    static
184    void copy_linewise(Iterator&& src, TLines& lines, my_restriction, std::false_type)
185    {
186       for (auto l_i=entire(lines); !l_i.at_end(); ++l_i, ++src)
187          l_i->fill(entire(*src));
188    }
189 
190    template <typename Iterator, typename TLines, typename TSourceOrdered>
191    static
192    void copy_linewise(Iterator&& src, TLines& lines, cross_restriction, TSourceOrdered)
193    {
194       for (Int i = 0; !src.at_end(); ++src, ++i)
195          append_across(lines, *src, i);
196    }
197 
198    template <typename TLines, typename TSet>
199    static
200    void append_across(TLines& lines, const TSet& set, Int i)
201    {
202       for (auto s = entire(set); !s.at_end(); ++s)
203          lines[*s].push_back(i);
204    }
205 
206    using proxy_base = incidence_proxy_base< incidence_line<typename table_type::primary_tree_type> >;
207 
208 public:
209    using value_type = bool;
210    using reference = sparse_elem_proxy<proxy_base>;
211    using const_reference = bool;
212 
213    explicit RestrictedIncidenceMatrix(Int n = 0) : data(n) {}
214 
215    RestrictedIncidenceMatrix(Int r, Int c) : data(r, c) {}
216 
217    template <typename Iterator, typename How,
218              typename = std::enable_if_t<is_among<How, sparse2d::rowwise, sparse2d::columnwise>::value &&
219                                                  assess_iterator_value<Iterator, can_initialize, Set<Int>>::value &&
220                                                  (How::value == restriction || assess_iterator<Iterator, check_iterator_feature, end_sensitive>::value)>>
221    RestrictedIncidenceMatrix(Int n, How how, Iterator&& src)
222       : data(n)
223    {
224       copy_linewise(ensure_private_mutable(std::forward<Iterator>(src)), lines(*this, my_restriction()),
225                     how, is_sequence_of_sets<Iterator>());
226    }
227 
228    template <typename Iterator, typename How,
229              typename=std::enable_if_t<is_among<How, sparse2d::rowwise, sparse2d::columnwise>::value &&
230                                                 assess_iterator_value<Iterator, can_initialize, Set<Int>>::value &&
231                                                 (How::value==restriction || assess_iterator<Iterator, check_iterator_feature, end_sensitive>::value)>>
232    RestrictedIncidenceMatrix(Int r, Int c, How how, Iterator&& src)
233       : data(r, c)
234    {
235       copy_linewise(ensure_private_mutable(std::forward<Iterator>(src)), lines(*this, my_restriction()),
236                     how, is_sequence_of_sets<Iterator>());
237    }
238 
239    template <typename How, typename... Sources,
240              typename=std::enable_if_t<is_among<How, sparse2d::rowwise, sparse2d::columnwise>::value &&
241                                                 mlist_and_nonempty<fits_for_append_to_IM<Sources>...>::value>>
242    RestrictedIncidenceMatrix(How how, const Sources&... src)
243       : data(0)
244    {
245       append_impl(how, src...);
246    }
247 
248    RestrictedIncidenceMatrix(std::initializer_list<std::initializer_list<Int>> l)
249       : data(l.size())
250    {
251       static_assert(restriction==sparse2d::only_rows, "a column-only restricted incidence matrix can't be constructed from an initializer list");
252       copy_linewise(l.begin(), pm::rows(*this), my_restriction(), std::false_type());
253    }
254 
255    RestrictedIncidenceMatrix(RestrictedIncidenceMatrix&& M)
256       : data(std::move(M.data)) {}
257 
258    void swap(RestrictedIncidenceMatrix& M) { data.swap(M.data); }
259 
260    void clear() { data.clear(); }
261 
262 protected:
263    proxy_base random_impl(Int i, Int j, std::false_type)
264    {
265       return proxy_base(this->row(i), j);
266    }
267    proxy_base random_impl(Int i, Int j, std::true_type)
268    {
269       return proxy_base(this->col(j), i);
270    }
271    bool random_impl(Int i, Int j, std::false_type) const
272    {
273       return this->row(i).exists(j);
274    }
275    bool random_impl(Int i, Int j, std::true_type) const
276    {
277       return this->col(j).exists(i);
278    }
279 public:
280    reference operator() (Int i, Int j)
281    {
282       return random_impl(i, j, bool_constant<restriction==sparse2d::only_cols>());
283    }
284    const_reference operator() (Int i, Int j) const
285    {
286       return random_impl(i, j, bool_constant<restriction==sparse2d::only_cols>());
287    }
288 
289    bool exists(Int i, Int j) const
290    {
291       return random_impl(i, j, bool_constant<restriction==sparse2d::only_cols>());
292    }
293 
294 private:
295    auto append_lines_start(sparse2d::rowwise, Int n)
296    {
297       const Int oldrows = data.rows();
298       data.resize_rows(oldrows + n);
299       return pm::rows(*this).begin() + oldrows;
300    }
301 
302    auto append_lines_start(sparse2d::columnwise, Int n)
303    {
304       const Int oldcols=data.cols();
305       data.resize_cols(oldcols + n);
306       return pm::cols(*this).begin() + oldcols;
307    }
308 
309    template <typename Container, typename... MoreSources>
310    auto append_lines_start(my_restriction how,
311                            std::enable_if_t<isomorphic_to_container_of<Container, Int, allow_conversion>::value, Int> n,
312                            const Container& c, MoreSources&&... more_src)
313    {
314       return append_lines_start(how, n+1, std::forward<MoreSources>(more_src)...);
315    }
316 
317    template <typename Container, typename... MoreSources>
318    auto append_lines_start(my_restriction how,
319                            std::enable_if_t<isomorphic_to_container_of<Container, Set<Int>, allow_conversion>::value, Int> n,
320                            const Container& c, MoreSources&&... more_src)
321    {
322       return append_lines_start(how, n+c.size(), std::forward<MoreSources>(more_src)...);
323    }
324 
325    template <typename TMatrix, typename... MoreSources>
326    auto append_lines_start(my_restriction how, Int n, const GenericIncidenceMatrix<TMatrix>& m, MoreSources&&... more_src)
327    {
328       return append_lines_start(how, n+(restriction==sparse2d::only_rows ? m.rows() : m.cols()), std::forward<MoreSources>(more_src)...);
329    }
330 
331    template <typename Container, typename... MoreSources>
332    auto append_lines_start(my_restriction how,
333                            std::enable_if_t<isomorphic_to_container_of<Container, IncidenceMatrix<>, allow_conversion>::value, Int> n,
334                            const Container& c, MoreSources&&... more_src)
335    {
336       for (const auto& m : c)
337          n += (restriction==sparse2d::only_rows ? m.rows() : m.cols());
338       return append_lines_start(how, n, std::forward<MoreSources>(more_src)...);
339    }
340 
341    template <typename... Sources>
342    Int append_lines_start(cross_restriction, Int, Sources&&...)
343    {
344       return restriction == sparse2d::only_rows ? data.cols() : data.rows();
345    }
346 
347    template <typename Iterator, typename TSet>
348    void append_lines_from(my_restriction, Iterator& dst, const GenericSet<TSet, Int, operations::cmp>& s)
349    {
350       *dst = s.top();
351       ++dst;
352    }
353 
354    template <typename Iterator, typename Container>
355    std::enable_if_t<isomorphic_to_container_of<Container, Int, is_set>::value>
356    append_lines_from(my_restriction, Iterator& dst, const Container& c)
357    {
358       dst->fill(entire(c));
359       ++dst;
360    }
361 
362    template <typename THow, typename Iterator, typename TMatrix>
363    void append_lines_from(THow how, Iterator& dst, const GenericIncidenceMatrix<TMatrix>& m)
364    {
365       for (auto src=entire(sparse2d::lines(m.top(), how)); !src.at_end(); ++src)
366          append_lines_from(how, dst, *src);
367    }
368 
369    template <typename Iterator, typename Container>
370    std::enable_if_t<isomorphic_to_container_of<Container, Set<Int>, allow_conversion>::value ||
371                     isomorphic_to_container_of<Container, IncidenceMatrix<>, allow_conversion>::value>
372    append_lines_from(my_restriction how, Iterator& dst, const Container& c)
373    {
374       for (auto src = entire(c); !src.at_end(); ++src)
375          append_lines_from(how, dst, *src);
376    }
377 
378    template <typename Container>
379    std::enable_if_t<isomorphic_to_container_of<Container, Int, allow_conversion>::value>
380    append_lines_from(cross_restriction, Int& r, const Container& c)
381    {
382       append_across(sparse2d::lines(*this, my_restriction()), c, r);
383       ++r;
384    }
385 
386    template <typename How, typename Iterator>
387    void append_lines(How, Iterator&) {}
388 
389    template <typename How, typename Iterator, typename Source, typename... MoreSources>
390    void append_lines(How how, Iterator&& dst, const Source& src, MoreSources&&... more_src)
391    {
392       append_lines_from(how, dst, src);
393       append_lines(how, dst, std::forward<MoreSources>(more_src)...);
394    }
395 
396    template <typename How, typename... Sources>
397    void append_impl(How how, Sources&&... src)
398    {
399       append_lines(how, append_lines_start(how, 0, std::forward<Sources>(src)...), std::forward<Sources>(src)...);
400    }
401 
402 public:
403    template <typename TMatrix>
404    RestrictedIncidenceMatrix& operator/= (const GenericIncidenceMatrix<TMatrix>& m)
405    {
406       append_impl(sparse2d::rowwise(), m);
407       return *this;
408    }
409 
410    template <typename TSet>
411    RestrictedIncidenceMatrix& operator/= (const GenericSet<TSet, Int, operations::cmp>& s)
412    {
413       append_impl(sparse2d::rowwise(), s.top());
414       return *this;
415    }
416 
417    template <typename TMatrix>
418    RestrictedIncidenceMatrix& operator|= (const GenericIncidenceMatrix<TMatrix>& m)
419    {
420       append_impl(sparse2d::columnwise(), m);
421       return *this;
422    }
423 
424    template <typename TSet>
425    RestrictedIncidenceMatrix& operator|= (const GenericSet<TSet, Int, operations::cmp>& s)
426    {
427       append_impl(sparse2d::columnwise(), s.top());
428       return *this;
429    }
430 
431    /// append one or more rows
432    template <typename... Sources,
433              typename=std::enable_if_t<mlist_and_nonempty<fits_for_append_to_IM<Sources>...>::value>>
434    void append_rows(const Sources&... src)
435    {
436       append_impl(sparse2d::rowwise(), src...);
437    }
438 
439    /// append one or more columns
440    template <typename... Sources,
441              typename=std::enable_if_t<mlist_and_nonempty<fits_for_append_to_IM<Sources>...>::value>>
442    void append_columns(const Sources&... src)
443    {
444       append_impl(sparse2d::columnwise(), src...);
445    }
446 
447    void squeeze() { data.squeeze(); }
448 
449    template <typename Permutation>
450    std::enable_if_t<isomorphic_to_container_of<Permutation, Int>::value>
451    permute_rows(const Permutation& perm)
452    {
453       data.permute_rows(perm, std::false_type());
454    }
455 
456    template <typename Permutation>
457    std::enable_if_t<isomorphic_to_container_of<Permutation, Int>::value>
458    permute_cols(const Permutation& perm)
459    {
460       data.permute_cols(perm, std::false_type());
461    }
462 
463    template <typename InvPermutation>
464    std::enable_if_t<isomorphic_to_container_of<InvPermutation, Int>::value>
465    permute_inv_rows(const InvPermutation& inv_perm)
466    {
467       data.permute_rows(inv_perm, std::true_type());
468    }
469 
470    template <typename InvPermutation>
471    std::enable_if_t<isomorphic_to_container_of<InvPermutation, Int>::value>
472    permute_inv_cols(const InvPermutation& inv_perm)
473    {
474       data.permute_cols(inv_perm, std::true_type());
475    }
476 
477 #if POLYMAKE_DEBUG
478    void check() const { data.check(); }
479 #endif
480    friend class Rows<RestrictedIncidenceMatrix>;
481    friend class Cols<RestrictedIncidenceMatrix>;
482    template <typename, typename, bool, sparse2d::restriction_kind, typename> friend class sparse2d::Rows;
483    template <typename, typename, bool, sparse2d::restriction_kind, typename> friend class sparse2d::Cols;
484    template <typename> friend class IncidenceMatrix;
485 };
486 
487 template <sparse2d::restriction_kind restriction>
488 class Rows< RestrictedIncidenceMatrix<restriction> >
489    : public sparse2d::Rows< RestrictedIncidenceMatrix<restriction>, nothing, false, restriction,
490                             operations::masquerade<incidence_line> > {
491 protected:
492    ~Rows();
493 public:
494    using container_category = std::conditional_t<restriction==sparse2d::only_rows, random_access_iterator_tag, output_iterator_tag>;
495 };
496 
497 template <sparse2d::restriction_kind restriction>
498 class Cols< RestrictedIncidenceMatrix<restriction> >
499    : public sparse2d::Cols< RestrictedIncidenceMatrix<restriction>, nothing, false, restriction,
500                             operations::masquerade<incidence_line> > {
501 protected:
502    ~Cols();
503 public:
504    using container_category = std::conditional<restriction==sparse2d::only_cols, random_access_iterator_tag, output_iterator_tag>;
505 };
506 
507 template <sparse2d::restriction_kind restriction>
508 struct spec_object_traits< RestrictedIncidenceMatrix<restriction> >
509    : spec_object_traits<is_container> {
510    static constexpr int dimension = 2;
511 
512    using serialized = std::conditional_t<restriction==sparse2d::only_rows,
513                                          Rows< RestrictedIncidenceMatrix<restriction> >,
514                                          Cols< RestrictedIncidenceMatrix<restriction> >>;
515 
516    static serialized& serialize(RestrictedIncidenceMatrix<restriction>& M)
517    {
518       return reinterpret_cast<serialized&>(M);
519    }
520    static const serialized& serialize(const RestrictedIncidenceMatrix<restriction>& M)
521    {
522       return reinterpret_cast<const serialized&>(M);
523    }
524 };
525 
526 template <typename symmetric>
527 class IncidenceMatrix_base {
528 protected:
529    using table_type = sparse2d::Table<nothing, symmetric::value>;
530    shared_object<table_type, AliasHandlerTag<shared_alias_handler>> data;
531 
532    table_type& get_table() { return *data; }
533    const table_type& get_table() const { return *data; }
534 
535    friend IncidenceMatrix_base& make_mutable_alias(IncidenceMatrix_base& alias, IncidenceMatrix_base& owner)
536    {
537       alias.data.make_mutable_alias(owner.data);
538       return alias;
539    }
540    IncidenceMatrix_base() = default;
541 
542    IncidenceMatrix_base(Int r, Int c)
543       : data(r, c) {}
544 
545    template <sparse2d::restriction_kind restriction>
546    explicit IncidenceMatrix_base(sparse2d::Table<nothing, symmetric::value, restriction>&& input_data)
547       : data(std::move(input_data)) {}
548 
549    template <typename> friend class Rows;
550    template <typename> friend class Cols;
551    template <typename, typename, bool, sparse2d::restriction_kind, typename> friend class sparse2d::Rows;
552    template <typename, typename, bool, sparse2d::restriction_kind, typename> friend class sparse2d::Cols;
553    template <bool, typename> friend class incidence_line_factory;
554    template <typename> friend class incidence_line_base;
555    template <typename, alias_kind> friend class alias;
556 };
557 
558 template <typename symmetric>
559 class Rows< IncidenceMatrix_base<symmetric> >
560    : public sparse2d::Rows< IncidenceMatrix_base<symmetric>, nothing, symmetric::value, sparse2d::full,
561                             operations::masquerade<incidence_line> > {
562 protected:
563    ~Rows();
564 };
565 
566 template <typename symmetric>
567 class Cols< IncidenceMatrix_base<symmetric> >
568    : public sparse2d::Cols< IncidenceMatrix_base<symmetric>, nothing, symmetric::value, sparse2d::full,
569                             operations::masquerade<incidence_line> > {
570 protected:
571    ~Cols();
572 };
573 
574 
575 /** @class IncidenceMatrix
576     @brief 0/1 incidence matrix.
577 
578    The only @ref persistent class from the incidence matrix family.
579    The implementation is based on a two-dimensional grid of <a href="AVL.html">balanced binary search (AVL) trees</a>,
580    the same as for @see SparseMatrix. The whole internal data structure
581    is attached to a smart pointer with @see {reference counting}.
582 
583    A symmetric incidence matrix is a square matrix whose elements `(i,j)` and `(j,i)`
584    are always equal. Internally it is stored in a triangular form, avoiding redundant elements, but appears as a full square.
585 
586 */
587 
588 template <typename symmetric>
589 class IncidenceMatrix
590    : public IncidenceMatrix_base<symmetric>
591    , public GenericIncidenceMatrix< IncidenceMatrix<symmetric> > {
592 protected:
593    using base_t = IncidenceMatrix_base<symmetric>;
594 
595    friend IncidenceMatrix& make_mutable_alias(IncidenceMatrix& alias, IncidenceMatrix& owner)
596    {
597       return static_cast<IncidenceMatrix&>(make_mutable_alias(static_cast<base_t&>(alias), static_cast<base_t&>(owner)));
598    }
599 
600    /// initialize from a dense boolean sequence in row order
601    template <typename Iterator>
602    void init_impl(Iterator&& src, std::true_type)
603    {
604       const Int n = this->cols();
605       for (auto r_i = entire(pm::rows(static_cast<base_t&>(*this))); !r_i.at_end(); ++r_i) {
606          Int i = 0;
607          if (symmetric::value) {
608             i = r_i.index();
609             std::advance(src, i);
610          }
611          for (; i < n; ++i, ++src)
612             if (*src) r_i->push_back(i);
613       }
614    }
615 
616    /// input already ordered
617    template <typename Iterator>
618    void init_rowwise(Iterator&& src, std::true_type)
619    {
620       copy_range(std::forward<Iterator>(src), entire(pm::rows(static_cast<base_t&>(*this))));
621    }
622 
623    /// input in uncertain order
624    template <typename Iterator>
625    void init_rowwise(Iterator&& src, std::false_type)
626    {
627       for (auto r_i=entire(pm::rows(static_cast<base_t&>(*this))); !r_i.at_end(); ++r_i, ++src)
628          r_i->fill(entire(*src));
629    }
630 
631    /// initialize rowwise from a sequence of sets
632    template <typename Iterator>
633    void init_impl(Iterator&& src, std::false_type)
634    {
635       init_rowwise(std::forward<Iterator>(src), is_sequence_of_sets<Iterator>());
636    }
637 
638    typedef incidence_proxy_base< incidence_line<typename base_t::table_type::primary_tree_type> > proxy_base;
639 public:
640    using unknown_columns_type = std::conditional_t<symmetric::value, void, RestrictedIncidenceMatrix<>>;
641    using value_type = bool;
642    using reference = sparse_elem_proxy<proxy_base>;
643    using const_reference = bool;
644 
645    /// Create an empty IncidenceMatrix.
646    IncidenceMatrix() {}
647 
648    /// Create an empty IncidenceMatrix with @a r rows and @a c columns initialized with zeroes.
649    IncidenceMatrix(Int r, Int c)
650       : base_t(r,c) {}
651 
652    /** @brief Create an IncidenceMatrix IncidenceMatrix with @a r rows and @a c columns and initialize it from a data sequence.
653 
654        @a src should iterate either over @a r&times;@a c boolean values, corresponding to the
655        elements in the row order (the column index changes first,) or over @a r sets with
656        integer elements (or convertible to integer), which are assigned to the matrix rows.
657 
658        In the symmetric case the redundant elements must be present in the input sequence; their values are ignored.
659 
660        @param r the number of rows
661        @param c the number of columns
662        @param src an iterator
663    */
664    template <typename Iterator>
665    IncidenceMatrix(Int r, Int c, Iterator&& src)
666       : base_t(r, c)
667    {
668       init_impl(ensure_private_mutable(std::forward<Iterator>(src)),
669                 bool_constant<(object_traits<typename iterator_traits<Iterator>::value_type>::total_dimension==0)>());
670    }
671 
672    IncidenceMatrix(const GenericIncidenceMatrix<IncidenceMatrix>& M)
673       : base_t(M.top()) {}
674 
675    template <typename Matrix2, typename=std::enable_if_t<IncidenceMatrix::template compatible_symmetry_types<Matrix2>()>>
676    IncidenceMatrix(const GenericIncidenceMatrix<Matrix2>& M)
677       : base_t(M.rows(), M.cols())
678    {
679       init_impl(pm::rows(M).begin(), std::false_type());
680    }
681 
682    template <sparse2d::restriction_kind restriction, typename=std::enable_if_t<!symmetric::value && restriction != sparse2d::full>>
683    explicit IncidenceMatrix(RestrictedIncidenceMatrix<restriction>&& M)
684       : base_t(std::move(M.data)) {}
685 
686    /// Construct a matrix by rowwise or columnwise concatenation of given matrices and/or sets.
687    /// Dimensions are set automatically to encompass all input elements.
688    template <typename How, typename... Sources,
689              typename=std::enable_if_t<!symmetric::value && is_among<How, sparse2d::rowwise, sparse2d::columnwise>::value &&
690              mlist_and_nonempty<fits_for_append_to_IM<Sources>...>::value>>
691    IncidenceMatrix(How how, const Sources&... src)
692       : base_t(RestrictedIncidenceMatrix<How::value>(how, src...).data) {}
693 
694    /// Construct a matrix from a given sequence of row sets.
695    /// Number of columns is set automatically to encompass all input elements.
696    template <typename Container, typename=std::enable_if_t<!symmetric::value && isomorphic_to_container_of<Container, Set<Int>, allow_conversion>::value>>
697    explicit IncidenceMatrix(const Container& src)
698       : base_t(RestrictedIncidenceMatrix<>(src.size(), sparse2d::rowwise(), src.begin()).data) {}
699 
700    /// Construct a matrix with a prescribed number of columns from a given sequence of row sets
701    template <typename Container,
702              typename=std::enable_if_t<!symmetric::value && isomorphic_to_container_of<Container, Set<Int>, allow_conversion>::value>>
703    IncidenceMatrix(const Container& src, Int c)
704       : base_t(src.size(), c)
705    {
706       init_impl(src.begin(), std::false_type());
707    }
708 
709    IncidenceMatrix(Int r, Int c, std::initializer_list<bool> l)
710       : base_t(r, c)
711    {
712       if (POLYMAKE_DEBUG && r*c != l.size())
713          throw std::runtime_error("initializer_list size does not match the dimensions");
714       init_impl(l.begin(), std::true_type());
715    }
716 
717    IncidenceMatrix(std::initializer_list<std::initializer_list<Int>> l)
718       : base_t(RestrictedIncidenceMatrix<>(l).data) {}
719 
720    IncidenceMatrix& operator= (const IncidenceMatrix& other) { assign(other); return *this; }
721    using IncidenceMatrix::generic_type::operator=;
722 
723    template <sparse2d::restriction_kind restriction, typename=std::enable_if_t<!symmetric::value && restriction != sparse2d::full>>
724    IncidenceMatrix& operator= (RestrictedIncidenceMatrix<restriction>&& M)
725    {
726       this->data.replace(std::move(M.data));
727       return *this;
728    }
729 
730    /// Swap the contents with that of another matrix in an efficient way.
731    void swap(IncidenceMatrix& M) { this->data.swap(M.data); }
732 
733    friend void relocate(IncidenceMatrix* from, IncidenceMatrix* to)
734    {
735       relocate(&from->data, &to->data);
736    }
737 
738    /**  @brief Extend or truncate to new dimensions (@a m rows, @a n columns).
739         Surviving elements keep their values, new elements are implicitly @c false.
740 
741         @c IncidenceMatrix deploys an adaptive reallocation strategy similar to @c std::vector,
742         reserving additional stock memory by every reallocation. If you repeatedly increase the matrix dimensions by one,
743         the amortized reallocation costs will be proportional to the logarithm of the final dimension.
744 
745         A special case, looking at the first glance like a "no operation": @c{ M.resize(M.rows(), M.cols()) },
746         gets rid of this extra allocated storage.
747    */
748    void resize(Int m, Int n) { this->data->resize(m, n); }
749 
750    /// Clear contents.
751    void clear() { this->data.apply(shared_clear()); }
752 
753    /// Clear contents.
754    void clear(Int r, Int c) { this->data.apply(typename base_t::table_type::shared_clear(r, c)); }
755 
756    /// Entry at row i column j.
757    reference operator() (Int i, Int j)
758    {
759       if (POLYMAKE_DEBUG) {
760          if (i < 0 || i >= this->rows() || j < 0 || j >= this->cols())
761             throw std::runtime_error("IncidenceMatrix::operator() - index out of range");
762       }
763       return proxy_base(pm::rows(static_cast<base_t&>(*this))[i], j);
764    }
765 
766    /// Entry at row i column j (const).
767    const_reference operator() (Int i, Int j) const
768    {
769       if (POLYMAKE_DEBUG) {
770          if (i < 0 || i >= this->rows() || j < 0 || j >= this->cols())
771             throw std::runtime_error("IncidenceMatrix::operator() - index out of range");
772       }
773       return pm::rows(static_cast<const base_t&>(*this))[i].exists(j);
774    }
775 
776    /// Returns the entry at position (i,j).
777    bool exists(Int i, Int j) const { return operator()(i, j); }
778 
779    template <typename row_number_consumer, typename col_number_consumer>
780    void squeeze(const row_number_consumer& rnc, const col_number_consumer& cnc) { this->data->squeeze(rnc,cnc); }
781 
782    template <typename row_number_consumer>
783    void squeeze(const row_number_consumer& rnc) { this->data->squeeze(rnc); }
784 
785    /// Delete empty rows and columns, renumber the rest and reduce the dimensions.
786    void squeeze() { this->data->squeeze(); }
787 
788    template <typename row_number_consumer>
789    void squeeze_rows(const row_number_consumer& rnc) { this->data->squeeze_rows(rnc); }
790 
791    /// Delete empty rows, renumber the rest and reduce the dimensions.
792    void squeeze_rows() { this->data->squeeze_rows(); }
793 
794    template <typename col_number_consumer>
795    void squeeze_cols(const col_number_consumer& cnc) { this->data->squeeze_cols(cnc); }
796 
797    /// Delete empty columns, renumber the rest and reduce the dimensions.
798    void squeeze_cols() { this->data->squeeze_cols(); }
799 
800    /// Permute the rows according to the given permutation.
801    template <typename Permutation>
802    std::enable_if_t<isomorphic_to_container_of<Permutation, Int>::value>
803    permute_rows(const Permutation& perm)
804    {
805       this->data->permute_rows(perm, std::false_type());
806    }
807 
808    /// Permute the columns according to the given permutation.
809    template <typename Permutation>
810    std::enable_if_t<isomorphic_to_container_of<Permutation, Int>::value>
811    permute_cols(const Permutation& perm)
812    {
813       this->data->permute_cols(perm, std::false_type());
814    }
815 
816    /// Permute the rows according to the inverse of the given permutation.
817    template <typename InvPermutation>
818    std::enable_if_t<isomorphic_to_container_of<InvPermutation, Int>::value>
819    permute_inv_rows(const InvPermutation& inv_perm)
820    {
821       this->data->permute_rows(inv_perm, std::true_type());
822    }
823 
824    /// Permute the columns according to the inverse of the given permutation.
825    template <typename InvPermutation>
826    std::enable_if_t<isomorphic_to_container_of<InvPermutation, Int>::value>
827    permute_inv_cols(const InvPermutation& inv_perm)
828    {
829       this->data->permute_cols(inv_perm, std::true_type());
830    }
831 
832    template <typename Permutation, typename InvPermutation,
833              typename=std::enable_if_t<symmetric::value, typename mproject1st<void, Permutation>::type>>
834    IncidenceMatrix copy_permuted(const Permutation& perm, const InvPermutation& inv_perm) const
835    {
836       const Int n = this->rows();
837       IncidenceMatrix result(n, n);
838       result.data.get()->copy_permuted(*this->data, perm, inv_perm);
839       return result;
840    }
841 
842 #if POLYMAKE_DEBUG
843    void check() const { this->data->check(); }
844 #endif
845 protected:
846    void assign(const GenericIncidenceMatrix<IncidenceMatrix>& M) { this->data=M.top().data; }
847 
848    template <typename Matrix>
849    void assign(const GenericIncidenceMatrix<Matrix>& M)
850    {
851       if (this->data.is_shared() || this->rows() != M.rows() || this->cols() != M.cols())
852          // circumvent the symmetry checks, they are already done in GenericIncidenceMatrix methods
853          assign(IncidenceMatrix(M.rows(), M.cols(), pm::rows(M).begin()));
854       else
855          GenericIncidenceMatrix<IncidenceMatrix>::assign(M);
856    }
857 
858    template <typename Matrix2>
859    void append_rows(const Matrix2& m)
860    {
861       const Int old_rows = this->rows();
862       this->data.apply(typename base_t::table_type::shared_add_rows(m.rows()));
863       copy_range(entire(pm::rows(m)), pm::rows(static_cast<base_t&>(*this)).begin() + old_rows);
864    }
865 
866    template <typename Set2>
867    void append_row(const Set2& s)
868    {
869       const Int old_rows = this->rows();
870       this->data.apply(typename base_t::table_type::shared_add_rows(1));
871       this->row(old_rows) = s;
872    }
873 
874    template <typename Matrix2>
875    void append_cols(const Matrix2& m)
876    {
877       const Int old_cols = this->cols();
878       this->data.apply(typename base_t::table_type::shared_add_cols(m.cols()));
879       copy_range(entire(pm::cols(m)), pm::cols(static_cast<base_t&>(*this)).begin() + old_cols);
880    }
881 
882    template <typename Set2>
883    void append_col(const Set2& s)
884    {
885       const Int old_cols = this->cols();
886       this->data.apply(typename base_t::table_type::shared_add_cols(1));
887       this->col(old_cols) = s;
888    }
889 
890    template <typename> friend class GenericIncidenceMatrix;
891    friend class Rows<IncidenceMatrix>;
892    friend class Cols<IncidenceMatrix>;
893    template <typename, typename, bool, sparse2d::restriction_kind, typename> friend class sparse2d::Rows;
894    template <typename, typename, bool, sparse2d::restriction_kind, typename> friend class sparse2d::Cols;
895    template <typename, typename> friend class BlockMatrix;
896 };
897 
898 template <typename symmetric>
899 struct check_container_feature< IncidenceMatrix<symmetric>, Symmetric >
900    : bool_constant<symmetric::value> {};
901 
902 // FIXME: temporary hack until all Vector<IncidenceMatrix> disappear from atint
903 template <typename symmetric>
904 struct spec_object_traits<IncidenceMatrix<symmetric>> : spec_object_traits<is_container> {
905    static constexpr int is_resizeable = 2 - symmetric::value;
906    static const IncidenceMatrix<symmetric>& zero()
907    {
908       static const IncidenceMatrix<symmetric> z{};
909       return z;
910    }
911 };
912 
913 template <bool rowwise, typename BaseRef>
914 class incidence_line_factory {
915 public:
916    typedef BaseRef first_argument_type;
917    typedef Int second_argument_type;
918    using tree_type = std::conditional_t<rowwise, typename pure_type_t<BaseRef>::table_type::row_tree_type,
919                                                  typename pure_type_t<BaseRef>::table_type::col_tree_type>;
920    typedef incidence_line<typename inherit_ref<tree_type, BaseRef>::type> result_type;
921 
922    result_type operator() (BaseRef matrix, Int index) const
923    {
924       return result_type(matrix, index);
925    }
926 };
927 
928 template <bool rowwise>
929 class incidence_line_factory<rowwise, void> : public operations::incomplete {};
930 
931 template <bool rowwise, typename BaseRef>
932 struct operation_cross_const_helper< incidence_line_factory<rowwise, BaseRef> > {
933    typedef incidence_line_factory<rowwise, typename attrib<BaseRef>::minus_const> operation;
934    typedef incidence_line_factory<rowwise, typename attrib<BaseRef>::plus_const> const_operation;
935 };
936 
937 template <bool rowwise, typename Iterator1, typename Iterator2, typename Reference1, typename Reference2>
938 struct binary_op_builder< incidence_line_factory<rowwise>, Iterator1, Iterator2, Reference1, Reference2>
939    : empty_op_builder< incidence_line_factory<rowwise,Reference1> > {};
940 
941 template <typename TSymmetric>
942 class Rows< IncidenceMatrix<TSymmetric> >
943    : public modified_container_pair_impl< Rows< IncidenceMatrix<TSymmetric> >,
944                                           mlist< Container1Tag< same_value_container< IncidenceMatrix_base<TSymmetric>& > >,
945                                                  Container2Tag< sequence >,
946                                                  OperationTag< pair< incidence_line_factory<true>,
947                                                                      BuildBinaryIt<operations::dereference2> > >,
948                                                  MasqueradedTop > > {
949 protected:
950    ~Rows();
951 public:
952    auto get_container1()
953    {
954       return same_value_container< IncidenceMatrix_base<TSymmetric>& >(this->hidden());
955    }
956    auto get_container1() const
957    {
958       return same_value_container< const IncidenceMatrix_base<TSymmetric>& >(this->hidden());
959    }
960    sequence get_container2() const
961    {
962       return sequence(0, this->hidden().get_table().rows());
963    }
964    void resize(Int n)
965    {
966       this->hidden().get_table().resize_rows(n);
967    }
968 };
969 
970 template <typename TSymmetric>
971 class Cols< IncidenceMatrix<TSymmetric> >
972    : public modified_container_pair_impl< Cols< IncidenceMatrix<TSymmetric> >,
973                                           mlist< Container1Tag< same_value_container< IncidenceMatrix_base<TSymmetric>& > >,
974                                                  Container2Tag< sequence >,
975                                                  OperationTag< pair< incidence_line_factory<false>,
976                                                                      BuildBinaryIt<operations::dereference2> > >,
977                                                  MasqueradedTop > > {
978 protected:
979    ~Cols();
980 public:
981    auto get_container1()
982    {
983       return same_value_container< IncidenceMatrix_base<TSymmetric>& >(this->hidden());
984    }
985    auto get_container1() const
986    {
987       return same_value_container< const IncidenceMatrix_base<TSymmetric>& >(this->hidden());
988    }
989    sequence get_container2() const
990    {
991       return sequence(0, this->hidden().get_table().cols());
992    }
993    void resize(Int n)
994    {
995       this->hidden().get_table().resize_cols(n);
996    }
997 };
998 
999 /// Convolution of two incidence relations.
1000 template <typename Matrix1, typename Matrix2>
1001 IncidenceMatrix<>
1002 convolute(const GenericIncidenceMatrix<Matrix1>& m1, const GenericIncidenceMatrix<Matrix2>& m2)
1003 {
1004    if (POLYMAKE_DEBUG || is_wary<Matrix1>() || is_wary<Matrix2>()) {
1005       if (m1.cols() != m2.rows())
1006          throw std::runtime_error("convolute - dimension mismatch");
1007    }
1008    IncidenceMatrix<> result(m1.rows(), m2.cols());
1009    auto r1=rows(m1).begin();
1010    for (auto dst=entire(rows(result)); !dst.at_end();  ++dst, ++r1)
1011       accumulate_in(entire(rows(m2.minor(*r1,All))), BuildBinary<operations::add>(), *dst);
1012 
1013    return result;
1014 }
1015 
1016 template <typename TMatrix, typename Permutation>
1017 std::enable_if_t<!TMatrix::is_symmetric, typename TMatrix::persistent_type>
1018 permuted_rows(const GenericIncidenceMatrix<TMatrix>& m, const Permutation& perm)
1019 {
1020    if (POLYMAKE_DEBUG || is_wary<TMatrix>()) {
1021       if (m.rows() != perm.size())
1022          throw std::runtime_error("permuted_rows - dimension mismatch");
1023    }
1024    return IncidenceMatrix<>(RestrictedIncidenceMatrix<sparse2d::only_rows>(m.rows(), m.cols(), sparse2d::rowwise(), select(rows(m), perm).begin()));
1025 }
1026 
1027 template <typename TMatrix, typename Permutation>
1028 std::enable_if_t<!TMatrix::is_symmetric, typename TMatrix::persistent_type>
1029 permuted_cols(const GenericIncidenceMatrix<TMatrix>& m, const Permutation& perm)
1030 {
1031    if (POLYMAKE_DEBUG || is_wary<TMatrix>()) {
1032       if (m.cols() != perm.size())
1033          throw std::runtime_error("permuted_cols - dimension mismatch");
1034    }
1035    return IncidenceMatrix<>(RestrictedIncidenceMatrix<sparse2d::only_cols>(m.rows(), m.cols(), sparse2d::columnwise(), select(cols(m), perm).begin()));
1036 }
1037 
1038 template <typename TMatrix, typename Permutation>
1039 std::enable_if_t<!TMatrix::is_symmetric, typename TMatrix::persistent_type>
1040 permuted_inv_rows(const GenericIncidenceMatrix<TMatrix>& m, const Permutation& perm)
1041 {
1042    if (POLYMAKE_DEBUG || is_wary<TMatrix>()) {
1043       if (m.rows() != perm.size())
1044          throw std::runtime_error("permuted_inv_rows - dimension mismatch");
1045    }
1046    RestrictedIncidenceMatrix<sparse2d::only_rows> result(m.rows(), m.cols());
1047    copy_range(entire(rows(m)), select(rows(result), perm).begin());
1048    return IncidenceMatrix<>(std::move(result));
1049 }
1050 
1051 template <typename TMatrix, typename Permutation>
1052 std::enable_if_t<!TMatrix::is_symmetric, typename TMatrix::persistent_type>
1053 permuted_inv_cols(const GenericIncidenceMatrix<TMatrix>& m, const Permutation& perm)
1054 {
1055    if (POLYMAKE_DEBUG || is_wary<TMatrix>()) {
1056       if (m.cols() != perm.size())
1057          throw std::runtime_error("permuted_inv_cols - dimension mismatch");
1058    }
1059    RestrictedIncidenceMatrix<sparse2d::only_cols> result(m.rows(), m.cols());
1060    copy_range(entire(cols(m)), select(cols(result), perm).begin());
1061    return IncidenceMatrix<>(std::move(result));
1062 }
1063 
1064 template <typename TMatrix, typename Permutation>
1065 std::enable_if_t<TMatrix::is_symmetric, typename TMatrix::persistent_type>
1066 permuted_rows(const GenericIncidenceMatrix<TMatrix>& m, const Permutation& perm)
1067 {
1068    if (POLYMAKE_DEBUG || is_wary<TMatrix>()) {
1069       if (m.rows() != perm.size())
1070          throw std::runtime_error("permuted_rows - dimension mismatch");
1071    }
1072    std::vector<Int> inv_perm(m.rows());
1073    inverse_permutation(perm,inv_perm);
1074    return m.top().copy_permuted(perm,inv_perm);
1075 }
1076 
1077 template <typename TMatrix, typename Permutation>
1078 std::enable_if_t<TMatrix::is_symmetric && container_traits<Permutation>::is_random, typename TMatrix::persistent_type>
1079 permuted_inv_rows(const GenericIncidenceMatrix<TMatrix>& m, const Permutation& inv_perm)
1080 {
1081    if (POLYMAKE_DEBUG || is_wary<TMatrix>()) {
1082       if (m.rows() != inv_perm.size())
1083          throw std::runtime_error("permuted_inv_rows - dimension mismatch");
1084    }
1085    std::vector<Int> perm(m.rows());
1086    inverse_permutation(inv_perm,perm);
1087    return m.copy_permuted(perm,inv_perm);
1088 }
1089 
1090 template <typename TMatrix, typename Permutation>
1091 std::enable_if_t<TMatrix::is_symmetric && !container_traits<Permutation>::is_random, typename TMatrix::persistent_type>
1092 permuted_inv_rows(const GenericIncidenceMatrix<TMatrix>& m, const Permutation& inv_perm)
1093 {
1094    if (POLYMAKE_DEBUG || is_wary<TMatrix>()) {
1095       if (m.rows() != inv_perm.size())
1096          throw std::runtime_error("permuted_inv_rows - dimension mismatch");
1097    }
1098    std::vector<Int> inv_perm_copy(inv_perm.size());
1099    copy_range(entire(inv_perm), inv_perm_copy.begin());
1100    return permuted_inv_rows(m,inv_perm_copy);
1101 }
1102 
1103 template <typename TMatrix, typename Permutation>
1104 std::enable_if_t<TMatrix::is_symmetric, typename TMatrix::persistent_type>
1105 permuted_cols(const GenericIncidenceMatrix<TMatrix>& m, const Permutation& perm)
1106 {
1107    return permuted_rows(m,perm);
1108 }
1109 
1110 template <typename TMatrix, typename Permutation>
1111 std::enable_if_t<TMatrix::is_symmetric, typename TMatrix::persistent_type>
1112 permuted_inv_cols(const GenericIncidenceMatrix<TMatrix>& m, const Permutation& inv_perm)
1113 {
1114    return permuted_inv_rows(m,inv_perm);
1115 }
1116 
1117 } // end namespace pm
1118 
1119 namespace polymake {
1120    using pm::IncidenceMatrix;
1121    using pm::RestrictedIncidenceMatrix;
1122 }
1123 
1124 namespace std {
1125    template <typename symmetric>
1126    void swap(pm::IncidenceMatrix<symmetric>& M1, pm::IncidenceMatrix<symmetric>& M2) { M1.swap(M2); }
1127 
1128    template <pm::sparse2d::restriction_kind restriction>
1129    void swap(pm::RestrictedIncidenceMatrix<restriction>& M1,
1130              pm::RestrictedIncidenceMatrix<restriction>& M2)
1131    {
1132       M1.swap(M2);
1133    }
1134 
1135    template <typename TreeRef>
1136    void swap(pm::incidence_line<TreeRef>& l1, pm::incidence_line<TreeRef>& l2)
1137    {
1138       l1.swap(l2);
1139    }
1140 }
1141 
1142 
1143 // Local Variables:
1144 // mode:C++
1145 // c-basic-offset:3
1146 // indent-tabs-mode:nil
1147 // End:
1148