//
// Semigroups package for GAP
// Copyright (C) 2020-2022 James D. Mitchell
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
// This file contains specialisations of gapbind14::to_cpp for various types
// defined in the GAP package Semigroups and the corresponding types in
// libsemigroups.
#ifndef SEMIGROUPS_SRC_TO_CPP_HPP_
#define SEMIGROUPS_SRC_TO_CPP_HPP_
// Standard library
#include <algorithm>
// for max, min, sort
#include <cstddef>
// for size_t
#include <cstdint>
// for uint32_t
#include <memory>
// for make_unique, unique_ptr
#include <string>
// for string
#include <type_traits>
// for decay_t, is_same, conditional_t
#include <unordered_map>
// for operator==, unordered_map
#include <utility>
// for forward
#include <vector>
// for vector
// GAP headers
#include "gap_all.h"
// Semigroups package headers
#include "bipart.hpp" // for bipart_get_cpp
#include "froidure-pin.hpp" // for WBMat8
#include "pkg.hpp" // for IsInfinity etc
#include "semigroups-debug.hpp" // for SEMIGROUPS_ASSERT
// gapbind14 headers
#include "gapbind14/cpp_fn.hpp" // for GAPBIND14_TRY
#include "gapbind14/to_cpp.hpp" // for to_cpp
#include "gapbind14/to_gap.hpp" // for gap_tnum_type
// libsemigroups headers
#include "libsemigroups/adapters.hpp" // for Degree
#include "libsemigroups/bmat8.hpp" // for BMat8
#include "libsemigroups/cong.hpp" // for Congruence
#include "libsemigroups/constants.hpp" // for NegativeInfinity, PositiveIn...
#include "libsemigroups/containers.hpp" // for DynamicArray2
#include "libsemigroups/matrix.hpp" // for NTPMat, MaxPlusTruncMat, Min...
#include "libsemigroups/pbr.hpp" // for PBR
#include "libsemigroups/transf.hpp" // for PPerm, Transf, IsPPerm
#include "libsemigroups/types.hpp" // for congruence_kind, congruence_...
namespace libsemigroups {
class Bipartition;
}
using libsemigroups::BMat;
using libsemigroups::BMat8;
using libsemigroups::IntMat;
using semigroups::WBMat8;
using libsemigroups::IntMat;
using libsemigroups::MaxPlusMat;
using libsemigroups::MaxPlusTruncMat;
using libsemigroups::MinPlusMat;
using libsemigroups::MinPlusTruncMat;
using libsemigroups::NTPMat;
using libsemigroups::ProjMaxPlusMat;
using libsemigroups::NTPSemiring;
using libsemigroups::Bipartition;
using libsemigroups::PBR;
using libsemigroups::IsPPerm;
using libsemigroups::PPerm;
using libsemigroups::Transf;
using libsemigroups::congruence_kind;
using libsemigroups::NegativeInfinity;
using libsemigroups::PositiveInfinity;
using libsemigroups::UNDEFINED;
using libsemigroups::detail::DynamicArray2;
namespace semigroups {
NTPSemiring<>
const* semiring(size_t threshold, size_t period);
template <
typename T>
T
const* semiring(size_t threshold) {
static std::unordered_map<size_t, std::unique_ptr<T
const>> cache;
auto it = cache.find(threshold);
if (it == cache.end()) {
it = cache.emplace(threshold, std::make_unique<T
const>(threshold)).first;
}
return it->second.get();
}
}
// namespace semigroups
namespace gapbind14 {
////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////
template <>
struct to_cpp<PositiveInfinity> {
using cpp_type = PositiveInfinity;
static gap_tnum_type constexpr gap_type = T_POSOBJ;
auto operator()(Obj x)
const {
if (CALL_1ARGS(IsInfinity, x) !=
True) {
ErrorQuit(
"expected object satisfying IsInfinity, found %s",
(
Int) TNAM_OBJ(x),
0L);
}
return libsemigroups::POSITIVE_INFINITY;
}
};
template <>
struct to_cpp<NegativeInfinity> {
using cpp_type = NegativeInfinity;
static gap_tnum_type constexpr gap_type = T_POSOBJ;
auto operator()(Obj x)
const {
if (CALL_1ARGS(IsNegInfinity, x) !=
True) {
ErrorQuit(
"expected object satisfying IsNegInfinity, found %s",
(
Int) TNAM_OBJ(x),
0L);
}
return libsemigroups::NEGATIVE_INFINITY;
}
};
////////////////////////////////////////////////////////////////////////
// BMat <-> IsBooleanMat
////////////////////////////////////////////////////////////////////////
template <>
struct to_cpp<BMat<>> {
using cpp_type = BMat<>;
static gap_tnum_type constexpr gap_type = T_POSOBJ;
BMat<>
operator()(Obj o)
const {
if (CALL_1ARGS(IsBooleanMat, o) !=
True) {
ErrorQuit(
"expected boolean matrix but got %s!", (
Int) TNAM_OBJ(o), 0L);
}
SEMIGROUPS_ASSERT(LEN_PLIST(o) > 0);
Obj row = ELM_PLIST(o, 1);
SEMIGROUPS_ASSERT(IS_PLIST(row) || IS_BLIST_REP(row));
size_t m = (IS_BLIST_REP(row) ? LEN_BLIST(row) : LEN_PLIST(row));
BMat<> x(m, m);
for (size_t i = 0; i < m; i++) {
row = ELM_PLIST(o, i + 1);
if (!IS_BLIST_REP(row)) {
ConvBlist(row);
}
for (size_t j = 0; j < m; j++) {
if (ELM_BLIST(row, j + 1) ==
True) {
x(i, j) = 1;
}
}
}
GAPBIND14_TRY(libsemigroups::validate(x));
return x;
}
};
template <>
struct to_cpp<BMat<>
const&> : to_cpp<BMat<>> {};
template <>
struct to_cpp<WBMat8> {
WBMat8
operator()(Obj o)
const {
if (CALL_1ARGS(IsBooleanMat, o) !=
True) {
ErrorQuit(
"expected boolean matrix but got %s!", (
Int) TNAM_OBJ(o), 0L);
}
SEMIGROUPS_ASSERT(LEN_PLIST(o) > 0);
Obj row = ELM_PLIST(o, 1);
SEMIGROUPS_ASSERT(IS_PLIST(row) || IS_BLIST_REP(row));
size_t m = (IS_BLIST_REP(row) ? LEN_BLIST(row) : LEN_PLIST(row));
BMat8 x(0);
for (size_t i = 0; i < m; i++) {
row = ELM_PLIST(o, i + 1);
if (!IS_BLIST_REP(row)) {
ConvBlist(row);
}
for (size_t j = 0; j < m; j++) {
if (ELM_BLIST(row, j + 1) ==
True) {
x.set(i, j, 1);
}
}
}
return std::make_pair(x, m);
}
};
template <>
struct to_cpp<WBMat8&> : to_cpp<WBMat8> {};
template <>
struct to_cpp<WBMat8
const&> : to_cpp<WBMat8> {};
////////////////////////////////////////////////////////////////////////
// MaxPlusMat + MinPlusMat
////////////////////////////////////////////////////////////////////////
namespace detail {
template <
typename T,
typename... TArgs>
T make_matrix(size_t r, size_t c, TArgs&&... params) {
return T(std::forward<TArgs>(params)..., r, c);
}
template <
typename T,
typename... TArgs>
T init_cpp_matrix(Obj o, TArgs&&... params) {
using scalar_type =
typename T::scalar_type;
if (LEN_PLIST(o) == 0) {
ErrorQuit(
"expected matrix of non-zero dimension!", 0L, 0L);
}
size_t m = LEN_PLIST(ELM_PLIST(o, 1));
T x = make_matrix<T>(m, m, std::forward<TArgs>(params)...);
for (size_t i = 0; i < m; i++) {
Obj row = ELM_PLIST(o, i + 1);
for (size_t j = 0; j < m; j++) {
Obj val = ELM_PLIST(row, j + 1);
scalar_type itm;
if (CALL_1ARGS(IsInfinity, val) !=
True
&& CALL_1ARGS(IsNegInfinity, val) !=
True) {
itm = to_cpp<scalar_type>()(val);
}
else if (CALL_1ARGS(IsInfinity, val) ==
True) {
itm = to_cpp<PositiveInfinity>()(val);
}
else if (CALL_1ARGS(IsNegInfinity, val) ==
True) {
itm = to_cpp<std::conditional_t<std::is_signed<scalar_type>::value,
NegativeInfinity,
scalar_type>>()(val);
}
x(i, j) = itm;
}
}
GAPBIND14_TRY(libsemigroups::validate(x));
return x;
}
}
// namespace detail
template <>
struct to_cpp<IntMat<>> {
static gap_tnum_type constexpr gap_type = T_POSOBJ;
IntMat<>
operator()(Obj o) {
if (CALL_1ARGS(IsMatrixObj, o) !=
True) {
ErrorMayQuit(
"expected a matrix obj found %s!", (
Int) TNAM_OBJ(o), 0L);
}
else if (!EQ(CALL_1ARGS(BaseDomain, o), Integers)) {
ErrorMayQuit(
"expected a base domain of matrix to be the integers!", 0L, 0L);
}
size_t m = INT_INTOBJ(CALL_1ARGS(NrRows, o));
if (m == 0) {
ErrorQuit(
"expected matrix of non-zero dimension!", 0L, 0L);
}
IntMat<> x(m, m);
using scalar_type =
typename IntMat<>::scalar_type;
for (size_t i = 0; i < m; i++) {
for (size_t j = 0; j < m; j++) {
SEMIGROUPS_ASSERT(
IS_INTOBJ(ELM_MAT(o, INTOBJ_INT(i + 1), INTOBJ_INT(j + 1))));
x(i, j) = to_cpp<scalar_type>()(
ELM_MAT(o, INTOBJ_INT(i + 1), INTOBJ_INT(j + 1)));
}
}
GAPBIND14_TRY(libsemigroups::validate(x));
return x;
}
};
template <>
struct to_cpp<MaxPlusMat<>> {
using cpp_type = MaxPlusMat<>;
MaxPlusMat<>
operator()(Obj o)
const {
if (CALL_1ARGS(IsMaxPlusMatrix, o) !=
True) {
ErrorQuit(
"expected max-plus matrix, found %s", (
Int) TNAM_OBJ(o), 0L);
}
return detail::init_cpp_matrix<MaxPlusMat<>>(o);
}
};
template <>
struct to_cpp<MinPlusMat<>> {
using cpp_type = MinPlusMat<>;
MinPlusMat<>
operator()(Obj o)
const {
if (CALL_1ARGS(IsMinPlusMatrix, o) !=
True) {
ErrorQuit(
"expected min-plus matrix, found %s", (
Int) TNAM_OBJ(o), 0L);
}
return detail::init_cpp_matrix<MinPlusMat<>>(o);
}
};
template <>
struct to_cpp<ProjMaxPlusMat<>> {
using cpp_type = ProjMaxPlusMat<>;
ProjMaxPlusMat<>
operator()(Obj o)
const {
if (CALL_1ARGS(IsProjectiveMaxPlusMatrix, o) !=
True) {
ErrorQuit(
"expected min-plus matrix, found %s", (
Int) TNAM_OBJ(o), 0L);
}
return detail::init_cpp_matrix<ProjMaxPlusMat<>>(o);
}
};
template <>
struct to_cpp<MaxPlusTruncMat<>> {
using cpp_type = MaxPlusTruncMat<>;
cpp_type
operator()(Obj o)
const {
if (CALL_1ARGS(IsTropicalMaxPlusMatrix, o) !=
True) {
ErrorQuit(
"expected tropical max-plus matrix, found %s",
(
Int) TNAM_OBJ(o),
0L);
}
size_t m = LEN_PLIST(ELM_PLIST(o, 1));
using semiring_type =
typename MaxPlusTruncMat<>::semiring_type;
auto const* sr = semigroups::semiring<semiring_type>(
static_cast<size_t>(INT_INTOBJ(ELM_PLIST(o, m + 1))));
return detail::init_cpp_matrix<MaxPlusTruncMat<>>(o, sr);
}
};
template <>
struct to_cpp<MinPlusTruncMat<>> {
using cpp_type = MinPlusTruncMat<>;
cpp_type
operator()(Obj o)
const {
if (CALL_1ARGS(IsTropicalMinPlusMatrix, o) !=
True) {
ErrorQuit(
"expected tropical min-plus matrix, found %s",
(
Int) TNAM_OBJ(o),
0L);
}
size_t m = LEN_PLIST(ELM_PLIST(o, 1));
using semiring_type =
typename MinPlusTruncMat<>::semiring_type;
auto const* sr = semigroups::semiring<semiring_type>(
static_cast<size_t>(INT_INTOBJ(ELM_PLIST(o, m + 1))));
return detail::init_cpp_matrix<MinPlusTruncMat<>>(o, sr);
}
};
template <>
struct to_cpp<NTPMat<>> {
using cpp_type = NTPMat<>;
NTPMat<>
operator()(Obj o)
const {
if (CALL_1ARGS(IsNTPMatrix, o) !=
True) {
ErrorQuit(
"expected ntp matrix, found %s!", (
Int) TNAM_OBJ(o), 0L);
}
SEMIGROUPS_ASSERT(LEN_PLIST(o) != 0);
size_t m = LEN_PLIST(ELM_PLIST(o, 1));
auto const* sr = semigroups::semiring(
static_cast<size_t>(INT_INTOBJ(ELM_PLIST(o, m + 1))),
static_cast<size_t>(INT_INTOBJ(ELM_PLIST(o, m + 2))));
return detail::init_cpp_matrix<NTPMat<>>(o, sr);
}
};
////////////////////////////////////////////////////////////////////////
// congruence_kind
////////////////////////////////////////////////////////////////////////
template <>
struct to_cpp<congruence_kind> {
using cpp_type = congruence_kind;
static gap_tnum_type constexpr gap_type = T_STRING;
cpp_type
operator()(Obj o)
const {
if (!IS_STRING_REP(o)) {
ErrorQuit(
"expected string but got %s!", (
Int) TNAM_OBJ(o), 0L);
}
std::string stype = std::string(CSTR_STRING(o));
if (stype ==
"left") {
return congruence_kind::left;
}
else if (stype ==
"right") {
return congruence_kind::right;
}
else if (stype ==
"2-sided") {
return congruence_kind::twosided;
}
else {
ErrorQuit(
"Unrecognised type %s", (
Int) stype.c_str(), 0L);
}
}
};
template <>
struct to_cpp<libsemigroups::Congruence::options::runners> {
using cpp_type = libsemigroups::Congruence::options::runners;
cpp_type
operator()(Obj o)
const {
if (!IS_STRING_REP(o)) {
ErrorQuit(
"expected string but got %s!", (
Int) TNAM_OBJ(o), 0L);
}
std::string stype = std::string(CSTR_STRING(o));
if (stype ==
"none") {
return cpp_type::none;
}
else if (stype ==
"standard") {
return cpp_type::standard;
}
else {
ErrorQuit(
"Unrecognised type %s", (
Int) stype.c_str(), 0L);
}
}
};
////////////////////////////////////////////////////////////////////////
// Transformations
////////////////////////////////////////////////////////////////////////
namespace detail {
template <
typename S,
typename T>
void to_cpp_transf(S& x, T* ptr, size_t
const N) {
static_assert(
std::is_same<T, UInt2>::value || std::is_same<T, UInt4>::value,
"the template parameter T must be the same as UInt2 or UInt4");
SEMIGROUPS_ASSERT(N <= libsemigroups::Degree<S>()(x));
T i;
for (i = 0; i < N; ++i) {
x[i] = ptr[i];
}
for (; i < libsemigroups::Degree<S>()(x); ++i) {
x[i] = i;
}
}
template <
typename TransfType>
TransfType new_transf(size_t N) {
return TransfType(N);
}
#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
template <>
inline HPCombi::Transf16 new_transf(size_t N) {
if (N > 16) {
ErrorQuit(
"expected transformation of degree at most 16, found %d",
(
Int) N,
0L);
}
return HPCombi::Transf16();
}
#endif
}
// namespace detail
template <
typename TransfType>
struct ToTransf {
using cpp_type = TransfType;
cpp_type
operator()(Obj t)
const {
if (!IS_PLIST(t)) {
ErrorQuit(
"expected list, got %s", (
Int) TNAM_OBJ(t), 0L);
}
else if (LEN_PLIST(t) != 2) {
ErrorQuit(
"expected list of length 2, but it has length %d",
(
Int) LEN_PLIST(t),
0L);
}
else if (!IS_TRANS(ELM_PLIST(t, 1))) {
ErrorQuit(
"expected transforamtion in position 1, got %s",
(
Int) TNAM_OBJ(t),
0L);
}
else if (!IS_INTOBJ(ELM_PLIST(t, 2))) {
ErrorQuit(
"expected integer in position 2, got %s", (
Int) TNAM_OBJ(t), 0L);
}
Obj x = ELM_PLIST(t, 1);
decltype(DEG_TRANS(x)) N = INT_INTOBJ(ELM_PLIST(t, 2));
if (INT_INTOBJ(CALL_1ARGS(LARGEST_MOVED_PT_TRANS, x)) > N) {
ErrorQuit(
"expected transformation with largest moved point not "
"greater than %d, found %d",
(
Int) N,
(
Int) DEG_TRANS(x));
}
cpp_type result = detail::new_transf<cpp_type>(N);
if (TNUM_OBJ(x) == T_TRANS2) {
detail::to_cpp_transf(
result, ADDR_TRANS2(x), std::min(DEG_TRANS(x), N));
}
else if (TNUM_OBJ(x) == T_TRANS4) {
detail::to_cpp_transf(
result, ADDR_TRANS4(x), std::min(DEG_TRANS(x), N));
}
else {
// in case of future changes to transf in GAP
ErrorQuit(
"transformation degree too high!", 0L, 0L);
}
return result;
}
};
template <
typename Scalar>
struct to_cpp<Transf<0, Scalar>> : ToTransf<Transf<0, Scalar>> {};
#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
template <>
struct to_cpp<HPCombi::Transf16> :
public ToTransf<HPCombi::Transf16> {};
template <>
struct to_cpp<HPCombi::Transf16&> : ToTransf<HPCombi::Transf16> {};
template <>
struct to_cpp<HPCombi::Transf16
const&> : ToTransf<HPCombi::Transf16> {};
#endif
////////////////////////////////////////////////////////////////////////
// Partial perms
////////////////////////////////////////////////////////////////////////
namespace detail {
template <
typename T>
struct Undef;
template <
typename T>
struct Undef<PPerm<0, T>> {
static constexpr T value = UNDEFINED;
};
#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
template <>
struct Undef<HPCombi::PPerm16> {
static constexpr UInt2 value = 0xFF;
};
#endif
template <
typename S,
typename T>
void to_cpp_pperm(S& x, T* ptr, size_t
const N) {
static_assert(std::is_same<T, UInt2>::value
|| std::is_same<T, UInt4>::value,
"the template parameter T must be UInt2 or UInt4");
SEMIGROUPS_ASSERT(N <= libsemigroups::Degree<S>()(x));
T i;
T undef =
static_cast<T>(Undef<S>::value);
for (i = 0; i < N; ++i) {
if (ptr[i] == 0) {
x[i] = undef;
}
else {
x[i] = ptr[i] - 1;
}
}
for (; i < libsemigroups::Degree<S>()(x); ++i) {
x[i] = undef;
}
}
template <
typename PPermType>
PPermType new_pperm(size_t N) {
return PPermType(N);
}
#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
template <>
inline HPCombi::PPerm16 new_pperm(size_t N) {
if (N > 16) {
ErrorQuit(
"expected partial perm of degree at most 16, found %d",
(
Int) N,
0L);
}
return HPCombi::PPerm16();
}
#endif
}
// namespace detail
template <
typename PPermType>
struct ToPPerm {
using cpp_type = PPermType;
cpp_type
operator()(Obj t)
const {
if (!IS_PLIST(t)) {
ErrorQuit(
"expected list, got %s", (
Int) TNAM_OBJ(t), 0L);
}
else if (LEN_PLIST(t) != 2) {
ErrorQuit(
"expected list of length 2, but it has length %d",
(
Int) LEN_PLIST(t),
0L);
}
else if (!IS_PPERM(ELM_PLIST(t, 1))) {
ErrorQuit(
"expected partial perm in position 1, got %s",
(
Int) TNAM_OBJ(t),
0L);
}
else if (!IS_INTOBJ(ELM_PLIST(t, 2))) {
ErrorQuit(
"expected integer in position 2, got %s", (
Int) TNAM_OBJ(t), 0L);
}
Obj x = ELM_PLIST(t, 1);
UInt4 N = INT_INTOBJ(ELM_PLIST(t, 2));
UInt4 M = 0;
if (TNUM_OBJ(x) == T_PPERM2) {
M = lmp<UInt2>(ADDR_PPERM2(x), DEG_PPERM2(x));
}
else if (TNUM_OBJ(x) == T_PPERM4) {
M = lmp<UInt4>(ADDR_PPERM4(x), DEG_PPERM4(x));
}
else {
ErrorQuit(
"partial perm degree too high!", 0L, 0L);
}
if (M > N) {
ErrorQuit(
"expected partial perm where the largest point in the domain and "
"range is %d, found %d",
(
Int) N,
(
Int) M);
}
cpp_type result = detail::new_pperm<cpp_type>(N);
if (TNUM_OBJ(x) == T_PPERM2) {
detail::to_cpp_pperm(result, ADDR_PPERM2(x), DEG_PPERM2(x));
}
else if (TNUM_OBJ(x) == T_PPERM4) {
detail::to_cpp_pperm(result, ADDR_PPERM4(x), DEG_PPERM4(x));
}
else {
// in case of future changes to partial perms in GAP
ErrorQuit(
"partial perm degree too high!", 0L, 0L);
}
return result;
}
template <
typename S>
static S lmp(S* ptr, S deg) {
S result = 0;
for (S i = 0; i < deg; i++) {
if (ptr[i] != 0) {
result = std::max(result, i);
result = std::max(result, S(ptr[i] - 1));
}
}
return result;
}
};
template <
typename Scalar>
struct to_cpp<PPerm<0, Scalar>> : ToPPerm<PPerm<0, Scalar>> {};
#ifdef LIBSEMIGROUPS_HPCOMBI_ENABLED
template <>
struct to_cpp<HPCombi::PPerm16> : ToPPerm<HPCombi::PPerm16> {};
template <>
struct to_cpp<HPCombi::PPerm16&> : ToPPerm<HPCombi::PPerm16> {};
template <>
struct to_cpp<HPCombi::PPerm16
const&> : ToPPerm<HPCombi::PPerm16> {};
#endif
////////////////////////////////////////////////////////////////////////
// Bipartition
////////////////////////////////////////////////////////////////////////
template <>
struct to_cpp<Bipartition> {
Bipartition&
operator()(Obj x)
const {
if (TNUM_OBJ(x) != T_BIPART) {
ErrorQuit(
"expected a bipartition, got %s", (
Int) TNAM_OBJ(x), 0L);
}
return *bipart_get_cpp(x);
}
};
////////////////////////////////////////////////////////////////////////
// PBR
////////////////////////////////////////////////////////////////////////
template <>
struct to_cpp<PBR> {
PBR
operator()(Obj x)
const {
if (CALL_1ARGS(IsPBR, x) !=
True) {
ErrorQuit(
"expected a PBR, got %s", (
Int) TNAM_OBJ(x), 0L);
}
else if (LEN_PLIST(x) == 0) {
ErrorQuit(
"expected a PBR of degree > 0", 0L, 0L);
}
size_t m = INT_INTOBJ(ELM_PLIST(x, 1));
// SEMIGROUPS_ASSERT(LEN_PLIST(x) == 2 * m + 1);
libsemigroups::PBR result(m);
for (uint32_t i = 0; i < 2 * m; i++) {
Obj adj = ELM_PLIST(x, i + 2);
for (uint32_t j = 1; j <= LEN_PLIST(adj); j++) {
result[i].push_back(INT_INTOBJ(ELM_PLIST(adj, j)) - 1);
// assumes that adj is duplicate-free
}
std::sort(result[i].begin(), result[i].end());
}
return result;
}
};
template <
typename T>
struct to_cpp<DynamicArray2<T>> {
using cpp_type = DynamicArray2<T>;
cpp_type
operator()(Obj x) {
using value_type =
typename DynamicArray2<T>::value_type;
if (!IS_LIST(x)) {
ErrorQuit(
"expected a list, got %s", (
Int) TNAM_OBJ(x), 0L);
}
for (size_t i = 0; i < LEN_LIST(x); ++i) {
Obj item = ELM_LIST(x, i + 1);
if (!IS_HOMOG_LIST(item)) {
ErrorQuit(
"expected a list of homog. lists, but got %s in position %d",
(
Int) TNAM_OBJ(x),
(
Int) i + 1);
}
else if (i > 0 && LEN_LIST(ELM_LIST(x, i)) != LEN_LIST(item)) {
ErrorQuit(
"expected a list of lists of equal length, but found a "
"list of length %d in position %d",
(
Int) LEN_LIST(item),
(
Int) i + 1);
}
}
size_t nr_cols = (LEN_LIST(x) == 0 ? 0 : LEN_LIST(ELM_LIST(x, 1)));
cpp_type result(nr_cols, LEN_LIST(x));
for (size_t i = 0; i < LEN_LIST(x); ++i) {
Obj item = ELM_LIST(x, i + 1);
for (size_t j = 0; j < nr_cols; ++j) {
result.set(i, j, to_cpp<value_type>()(ELM_LIST(item, j + 1)));
}
}
return result;
}
};
}
// namespace gapbind14
#endif // SEMIGROUPS_SRC_TO_CPP_HPP_