// libsemigroups - C++ library for semigroups and monoids
// Copyright (C) 2021 James D. Mitchell + Maria Tsalakou
//
// 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 <http://www.gnu.org/licenses/>.
//
// TODO(later)
// - Write tests for maximum_common_suffix
#include "catch.hpp" // for REQUIRE, REQUIRE_NOTHROW, REQUIRE_THROWS_AS
#include "test-main.hpp" // for LIBSEMIGROUPS_TEST_CASE
#include <cstddef>
// for size_t
#include <cstdint>
// for int64_t, uint64_t
#include <iterator>
// for distance, advance, operator!=
#include <string>
// for string
#include <string>
// for basic_string, operator==
#include <type_traits>
// for move
#include "libsemigroups/string-view.hpp" // for MultiStringView, StringView...
namespace libsemigroups {
using detail::MultiStringView;
struct LibsemigroupsException;
// forward decl
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"000",
"catch all",
"[quick][multistringview]") {
// REQUIRE(sizeof(detail::MultiStringView) == 32);
// REQUIRE(sizeof(uint64_t) == 8);
std::string s =
"abcdefghijkl";
detail::MultiStringView msv(s.cbegin(), s.cbegin() + 3);
REQUIRE(msv.size() == 3);
msv.append(s.cbegin(), s.cend());
REQUIRE(msv.size() == s.size() + 3);
REQUIRE(msv.cbegin() != msv.cbegin() + 3);
REQUIRE(*msv.cbegin() == *(msv.cbegin() + 3));
REQUIRE(msv.cbegin() + 1 != msv.cbegin() + 4);
REQUIRE(*(msv.cbegin() + 1) == *(msv.cbegin() + 4));
REQUIRE(msv.cbegin() + 2 != msv.cbegin() + 5);
REQUIRE(*(msv.cbegin() + 2) == *(msv.cbegin() + 5));
REQUIRE((msv.cbegin() + 3) != (msv.cbegin() + 6));
REQUIRE(*(msv.cbegin() + 3) != *(msv.cbegin() + 6));
REQUIRE(std::string(msv) ==
"abcabcdefghijkl");
REQUIRE(((msv.cbegin() + 3) - msv.cbegin()) == 3);
REQUIRE((msv.cbegin() - (msv.cbegin() + 3)) == -3);
REQUIRE(
static_cast<size_t>(msv.cend() - msv.cbegin()) == msv.size());
REQUIRE(msv.cbegin() - msv.cend() == -
static_cast<int64_t>(msv.size()));
REQUIRE(msv.cbegin() + msv.size() == msv.cend());
std::string t =
"";
for (
auto it = msv.cbegin(); it != msv.cend(); ++it) {
t += *it;
}
REQUIRE(t ==
"abcabcdefghijkl");
REQUIRE(msv.number_of_views() == 2);
REQUIRE(msv == msv);
REQUIRE(msv.cbegin()[10] ==
'h');
detail::MultiStringView msv2(s.cbegin(), s.cbegin() + 3);
msv2.append(s.cbegin(), s.cbegin() + 3);
msv2.append(s.cbegin() + 3, s.cend());
REQUIRE(msv == msv);
REQUIRE(msv == msv2);
REQUIRE(msv2 == msv);
msv2.append(s.cbegin(), s.cbegin() + 3);
REQUIRE(msv2 != msv);
REQUIRE(msv != msv2);
REQUIRE(msv < msv2);
REQUIRE(msv <= msv2);
REQUIRE(msv2 >= msv);
REQUIRE(msv2 > msv);
t =
"";
for (
auto it = msv2.crbegin(); it != msv2.crend(); ++it) {
t += *it;
}
REQUIRE(t ==
"cbalkjihgfedcbacba");
REQUIRE(msv2.number_of_views() == 3);
msv2.append(s.cbegin() + 1, s.cbegin() + 1);
REQUIRE(msv2.number_of_views() == 3);
detail::MultiStringView msv3;
REQUIRE(msv3.empty());
REQUIRE(std::string(msv3) ==
"");
msv += msv2;
REQUIRE(std::string(msv) ==
"abcabcdefghijklabcabcdefghijklabc");
REQUIRE(msv.number_of_views() == 5);
auto it1 = msv.cbegin();
auto it2 = msv.cbegin();
std::advance(it1, 6);
std::advance(it2, 15);
REQUIRE(it2 - it1 == 9);
auto it3 = (msv.cbegin() + 6);
REQUIRE(it3 == it1);
it3 = (msv.cbegin() + 6);
std::advance(it3, 9);
REQUIRE(*it3 ==
'a');
REQUIRE(it3 - msv.cbegin() == 15);
REQUIRE(it3 == (msv.cbegin() + 15));
REQUIRE((msv.cbegin() + 15) - (msv.cbegin() + 6) == 9);
t =
"";
for (
auto it = msv.cbegin() + 6; it != msv.cbegin() + 15; ++it) {
t += *it;
}
REQUIRE(t ==
"defghijkl");
REQUIRE(msv3.number_of_views() == 0);
msv3.append(msv.cbegin() + 6, msv.cbegin() + 15);
REQUIRE(std::string(msv3) ==
"defghijkl");
msv3.clear();
REQUIRE(msv3.empty());
msv3.append(msv.cbegin() + 6, msv.cbegin() + 16);
REQUIRE(std::string(msv3) ==
"defghijkla");
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"001",
"is_prefix",
"[quick][multistringview]") {
using detail::MultiStringView;
std::string s =
"abcdefghijkl";
MultiStringView m1(s.cbegin(), s.cend() - 4);
REQUIRE(std::string(m1) ==
"abcdefgh");
REQUIRE(m1.number_of_views() == 1);
m1.append(s.begin(), s.cend());
REQUIRE(std::string(m1) ==
"abcdefghabcdefghijkl");
REQUIRE(m1.number_of_views() == 2);
m1.append(s.begin(), s.cend());
REQUIRE(m1.number_of_views() == 3);
REQUIRE(std::string(m1) ==
"abcdefghabcdefghijklabcdefghijkl");
m1.append(s.begin(), s.cend());
REQUIRE(m1.number_of_views() == 4);
MultiStringView m2(s.cbegin(), s.cend() - 4);
m2.append(s.begin(), s.cend());
REQUIRE(detail::is_prefix(m1, m2));
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"002",
"erase",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin(), s.cend() - 4);
m.append(s.begin(), s.cend());
m.append(s.begin(), s.cend());
m.append(s.begin(), s.cend());
REQUIRE(std::string(m) ==
"abcdefghabcdefghijklabcdefghijklabcdefghijkl");
// Both string_type::iterators in the same range not at the beginning or
// end.
m.erase(m.cbegin() + 1, m.cbegin() + 2);
REQUIRE(std::string(m) ==
"acdefghabcdefghijklabcdefghijklabcdefghijkl");
m.erase(m.cbegin(), m.cbegin() + 3);
REQUIRE(std::string(m) ==
"efghabcdefghijklabcdefghijklabcdefghijkl");
m.erase(m.cbegin() + 1, m.cbegin() + 4);
REQUIRE(std::string(m) ==
"eabcdefghijklabcdefghijklabcdefghijkl");
m.erase(m.cbegin() + 5, m.cend());
REQUIRE(std::string(m) ==
"eabcd");
REQUIRE(m.size() == 5);
m.erase(m.cend(), m.cbegin());
REQUIRE(m.size() == 5);
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"003",
"iterators",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin(), s.cend() - 4);
m.append(s.begin(), s.cend());
m.append(s.begin(), s.cend());
m.append(s.begin(), s.cend());
auto it = m.cbegin();
it += 0;
REQUIRE(it == m.cbegin());
REQUIRE(m.cbegin() == it);
it += 3;
REQUIRE(it > m.cbegin());
REQUIRE(it - m.cbegin() == 3);
MultiStringView mm;
REQUIRE(mm.empty());
REQUIRE(mm.cbegin() == mm.cend());
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"004",
"constructors (long)",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin(), s.cend() - 4);
m.append(s.begin(), s.cend());
m.append(s.begin(), s.cend());
m.append(s.begin(), s.cend());
REQUIRE(!m.empty());
MultiStringView mm(m);
REQUIRE(m == mm);
MultiStringView mmm(std::move(mm));
REQUIRE(m == mmm);
m.erase(m.cbegin() + 1, m.cbegin() + 4);
REQUIRE(std::string(m) ==
"aefghabcdefghijklabcdefghijklabcdefghijkl");
mm = m;
REQUIRE(mm == m);
REQUIRE(mm != mmm);
mmm = std::move(mm);
REQUIRE(mmm == m);
REQUIRE(m.size() == 41);
REQUIRE(std::distance(m.cbegin() + 4, m.cend()) == 37);
REQUIRE((m.cend() - 11) - (m.cbegin() + 4) == 26);
MultiStringView mmmm(m.cbegin() + 4, m.cend() - 11);
REQUIRE(std::string(m) ==
"aefghabcdefghijklabcdefghijklabcdefghijkl");
REQUIRE(std::string(mmmm) ==
"habcdefghijklabcdefghijkla");
REQUIRE(mmmm == MultiStringView(
"habcdefghijklabcdefghijkla"));
REQUIRE(!mmmm.empty());
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"005",
"constructors (short)",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin(), s.cend() - 4);
m.append(s.begin(), s.cend());
MultiStringView mm(m);
REQUIRE(m == mm);
MultiStringView mmm(std::move(mm));
REQUIRE(m == mmm);
m.erase(m.cbegin() + 1, m.cbegin() + 4);
REQUIRE(std::string(m) ==
"aefghabcdefghijkl");
mm = m;
REQUIRE(mm == m);
REQUIRE(mm != mmm);
mmm = std::move(mm);
REQUIRE(mmm == m);
REQUIRE(m.size() == 17);
MultiStringView mmmm(m.cbegin() + 4, m.cend() - 11);
REQUIRE(std::string(m) ==
"aefghabcdefghijkl");
REQUIRE(std::string(mmmm) ==
"ha");
REQUIRE(mmmm == MultiStringView(
"ha"));
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"006",
"copy assignment (short assigned to long)",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin(), s.cend() - 4);
m.append(s.begin(), s.cend());
REQUIRE(m.size() == 20);
MultiStringView mm(s.cbegin(), s.cend() - 4);
mm.append(s.begin(), s.cend());
mm.append(s.begin(), s.cend());
REQUIRE(mm.size() == 32);
mm = m;
REQUIRE(mm.size() == 20);
REQUIRE(mm == m);
REQUIRE(mm.number_of_views() == 2);
REQUIRE(std::string(mm) == std::string(m));
REQUIRE(std::string(mm) ==
"abcdefghabcdefghijkl");
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"007",
"copy assignment (short assigned to short)",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin(), s.cend() - 4);
m.append(s.begin(), s.cend());
REQUIRE(m.size() == 20);
MultiStringView mm(s.cbegin(), s.cend() - 4);
mm.append(s.begin(), s.cend() - 1);
REQUIRE(mm.size() == 19);
REQUIRE(m != mm);
mm = m;
REQUIRE(mm.size() == 20);
REQUIRE(mm == m);
REQUIRE(mm.number_of_views() == 2);
REQUIRE(std::string(mm) == std::string(m));
REQUIRE(std::string(mm) ==
"abcdefghabcdefghijkl");
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"008",
"move assignment (short assigned to long)",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin(), s.cend() - 4);
m.append(s.begin(), s.cend());
REQUIRE(m.size() == 20);
MultiStringView mm(s.cbegin(), s.cend() - 4);
mm.append(s.begin(), s.cend());
mm.append(s.begin(), s.cend());
REQUIRE(mm.size() == 32);
mm = std::move(m);
REQUIRE(mm.size() == 20);
REQUIRE(mm == m);
REQUIRE(mm.number_of_views() == 2);
REQUIRE(std::string(mm) == std::string(m));
REQUIRE(std::string(mm) ==
"abcdefghabcdefghijkl");
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"009",
"move assignment (short assigned to short)",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin(), s.cend() - 4);
m.append(s.begin(), s.cend());
REQUIRE(m.size() == 20);
MultiStringView mm(s.cbegin(), s.cend() - 4);
mm.append(s.begin(), s.cend() - 1);
REQUIRE(mm.size() == 19);
REQUIRE(m != mm);
mm = std::move(m);
REQUIRE(mm.size() == 20);
REQUIRE(mm == m);
REQUIRE(mm.number_of_views() == 2);
REQUIRE(std::string(mm) == std::string(m));
REQUIRE(std::string(mm) ==
"abcdefghabcdefghijkl");
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"010",
"code coverage",
"[quick][stringview]") {
std::string s =
"abcdefghijkl";
detail::StringViewContainer m;
m.emplace_back(s.cbegin() + 1, s.cbegin() + 5);
m.emplace_back(s.cbegin() + 2, s.cbegin() + 6);
REQUIRE(m.size(0) == 4);
REQUIRE(m.size(1) == 4);
REQUIRE(m.number_of_views() == 2);
auto foo = [](detail::StringViewContainer
const& mm) {
return mm.size(1); };
REQUIRE(foo(m) == 4);
m.pop_back();
REQUIRE(m.number_of_views() == 1);
m.pop_back();
REQUIRE(m.number_of_views() == 0);
REQUIRE(m.size() == 0);
REQUIRE(m.empty());
m.pop_back();
// does nothing
REQUIRE(m.number_of_views() == 0);
REQUIRE(m.size() == 0);
REQUIRE(m.empty());
m.insert(0, detail::StringView(s.cbegin(), s.cend()));
REQUIRE(m.number_of_views() == 1);
REQUIRE(m.size() == 12);
m.insert(0, detail::StringView(s.cend() - 2, s.cend()));
REQUIRE(m.number_of_views() == 2);
REQUIRE(m.size(0) == 2);
REQUIRE(m.size(1) == 12);
REQUIRE(m.size() == 14);
m.erase(0, 1);
REQUIRE(m.number_of_views() == 1);
REQUIRE(m.size() == 12);
m.insert(1, detail::StringView(s.cend() - 2, s.cend()));
REQUIRE(m.number_of_views() == 2);
REQUIRE(m.size(1) == 2);
REQUIRE(m.size(0) == 12);
m.erase(0, 2);
REQUIRE(m.empty());
m.erase(0, 2);
// does nothing
REQUIRE(m.empty());
m.emplace_back(s.cbegin() + 1, s.cbegin() + 6);
m.emplace_back(s.cbegin() + 2, s.cbegin() + 6);
REQUIRE(m.size(0) == 5);
REQUIRE(m.size(1) == 4);
REQUIRE(m.size() == 9);
REQUIRE(m.number_of_views() == 2);
m.erase(1, 2);
REQUIRE(m.size(0) == 5);
REQUIRE(m.size() == 5);
REQUIRE(m.number_of_views() == 1);
m.erase(0, 1);
REQUIRE(m.size() == 0);
REQUIRE(m.number_of_views() == 0);
REQUIRE(m.empty());
m.emplace_back(s.cbegin() + 1, s.cbegin() + 6);
m.emplace_back(s.cbegin() + 2, s.cbegin() + 6);
m.emplace_back(s.cbegin() + 2, s.cbegin() + 6);
m.emplace_back(s.cbegin() + 2, s.cbegin() + 6);
REQUIRE(!m.empty());
REQUIRE(m.size() == 17);
m.pop_back();
REQUIRE(m.size() == 13);
m.clear();
REQUIRE(m.empty());
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"011",
"pop_front",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin(), s.begin() + 4);
m.append(s.begin() + 1, s.begin() + 3);
REQUIRE(m.number_of_views() == 2);
REQUIRE(m.size() == 6);
REQUIRE(std::string(m) ==
"abcdbc");
REQUIRE(m.pop_front() ==
'a');
REQUIRE(std::string(m) ==
"bcdbc");
REQUIRE(m.number_of_views() == 2);
REQUIRE(m.pop_front() ==
'b');
REQUIRE(std::string(m) ==
"cdbc");
REQUIRE(m.number_of_views() == 2);
REQUIRE(m.pop_front() ==
'c');
REQUIRE(std::string(m) ==
"dbc");
REQUIRE(m.number_of_views() == 2);
REQUIRE(m.pop_front() ==
'd');
REQUIRE(std::string(m) ==
"bc");
REQUIRE(m.number_of_views() == 1);
REQUIRE(m.pop_front() ==
'b');
REQUIRE(std::string(m) ==
"c");
REQUIRE(m.number_of_views() == 1);
REQUIRE(m.pop_front() ==
'c');
REQUIRE(std::string(m) ==
"");
REQUIRE(m.number_of_views() == 0);
REQUIRE_THROWS_AS(m.pop_front(), LibsemigroupsException);
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"012",
"append",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin() + 3, s.begin() + 6);
REQUIRE(std::string(m) ==
"def");
m.append(m.cbegin(), m.cbegin() + 1);
REQUIRE(std::string(m) ==
"defd");
m.append(m.cbegin(), m.cend());
REQUIRE(std::string(m) ==
"defddefd");
m.append(m.cbegin() + 3, m.cbegin() + 5);
REQUIRE(std::string(m) ==
"defddefddd");
REQUIRE(m.size() == 10);
m.append(m.cbegin(), m.cend());
REQUIRE(m.size() == 20);
// Check the case when we append an interval adjacent to the end of the
// current view
m.clear();
REQUIRE(m.number_of_views() == 0);
m.append(s.cbegin(), s.cbegin() + 5);
REQUIRE(m.number_of_views() == 1);
m.append(s.cbegin() + 5, s.cend());
REQUIRE(m.number_of_views() == 1);
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"013",
"operators",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin() + 3, s.begin() + 6);
m.append(m.cbegin(), m.cbegin() + 1);
m.append(m.cbegin(), m.cend());
m.append(m.cbegin() + 3, m.cbegin() + 5);
REQUIRE(std::string(m) ==
"defddefddd");
REQUIRE(m[0] ==
'd');
REQUIRE(m[1] ==
'e');
REQUIRE(m[2] ==
'f');
REQUIRE(m[3] ==
'd');
REQUIRE(m[4] ==
'd');
REQUIRE(m[5] ==
'e');
REQUIRE(m[6] ==
'f');
REQUIRE(m[7] ==
'd');
REQUIRE(m[8] ==
'd');
REQUIRE(m[9] ==
'd');
MultiStringView mm(s);
REQUIRE(std::string(mm + m) ==
"abcdefghijkldefddefddd");
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"014",
"maximum_common_suffix",
"[quick][multistringview]") {
std::string s =
"abcdefghijkl";
MultiStringView m(s.cbegin() + 3, s.begin() + 6);
m.append(m.cbegin(), m.cbegin() + 1);
m.append(m.cbegin(), m.cend());
m.append(m.cbegin() + 3, m.cbegin() + 5);
REQUIRE(std::string(m) ==
"defddefddd");
MultiStringView mm = m + m;
REQUIRE(maximum_common_suffix(mm, m) == m);
}
LIBSEMIGROUPS_TEST_CASE(
"MultiStringView",
"015",
"deep tests",
"[quick][multistringview]") {
std::string s =
"bcdabcd";
MultiStringView m(s.cbegin(), s.begin() + 2);
m.append(s.cbegin() + 2, s.end());
MultiStringView ww(m.cbegin(), m.cbegin() + 2);
REQUIRE(ww.size() == 2);
}
}
// namespace libsemigroups